1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2017, assimp team
7 
8 
9 All rights reserved.
10 
11 Redistribution and use of this software in source and binary forms,
12 with or without modification, are permitted provided that the following
13 conditions are met:
14 
15 * Redistributions of source code must retain the above
16   copyright notice, this list of conditions and the
17   following disclaimer.
18 
19 * Redistributions in binary form must reproduce the above
20   copyright notice, this list of conditions and the
21   following disclaimer in the documentation and/or other
22   materials provided with the distribution.
23 
24 * Neither the name of the assimp team, nor the names of its
25   contributors may be used to endorse or promote products
26   derived from this software without specific prior
27   written permission of the assimp team.
28 
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 ---------------------------------------------------------------------------
41 */
42 
43 #ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
44 
45 #include "ObjFileImporter.h"
46 #include "ObjFileParser.h"
47 #include "ObjFileData.h"
48 #include "IOStreamBuffer.h"
49 #include <memory>
50 #include <assimp/DefaultIOSystem.h>
51 #include <assimp/Importer.hpp>
52 #include <assimp/scene.h>
53 #include <assimp/ai_assert.h>
54 #include <assimp/DefaultLogger.hpp>
55 #include <assimp/importerdesc.h>
56 
57 static const aiImporterDesc desc = {
58     "Wavefront Object Importer",
59     "",
60     "",
61     "surfaces not supported",
62     aiImporterFlags_SupportTextFlavour,
63     0,
64     0,
65     0,
66     0,
67     "obj"
68 };
69 
70 static const unsigned int ObjMinSize = 16;
71 
72 namespace Assimp {
73 
74 using namespace std;
75 
76 // ------------------------------------------------------------------------------------------------
77 //  Default constructor
ObjFileImporter()78 ObjFileImporter::ObjFileImporter() :
79     m_Buffer(),
80     m_pRootObject( NULL ),
81     m_strAbsPath( "" )
82 {
83     DefaultIOSystem io;
84     m_strAbsPath = io.getOsSeparator();
85 }
86 
87 // ------------------------------------------------------------------------------------------------
88 //  Destructor.
~ObjFileImporter()89 ObjFileImporter::~ObjFileImporter()
90 {
91     delete m_pRootObject;
92     m_pRootObject = NULL;
93 }
94 
95 // ------------------------------------------------------------------------------------------------
96 //  Returns true, if file is an obj file.
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const97 bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem*  pIOHandler , bool checkSig ) const
98 {
99     if(!checkSig) //Check File Extension
100     {
101         return SimpleExtensionCheck(pFile,"obj");
102     }
103     else //Check file Header
104     {
105         static const char *pTokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " };
106         return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 9 );
107     }
108 }
109 
110 // ------------------------------------------------------------------------------------------------
GetInfo() const111 const aiImporterDesc* ObjFileImporter::GetInfo () const
112 {
113     return &desc;
114 }
115 
116 // ------------------------------------------------------------------------------------------------
117 //  Obj-file import implementation
InternReadFile(const std::string & file,aiScene * pScene,IOSystem * pIOHandler)118 void ObjFileImporter::InternReadFile( const std::string &file, aiScene* pScene, IOSystem* pIOHandler) {
119     // Read file into memory
120     static const std::string mode = "rb";
121     std::unique_ptr<IOStream> fileStream( pIOHandler->Open( file, mode));
122     if( !fileStream.get() ) {
123         throw DeadlyImportError( "Failed to open file " + file + "." );
124     }
125 
126     // Get the file-size and validate it, throwing an exception when fails
127     size_t fileSize = fileStream->FileSize();
128     if( fileSize < ObjMinSize ) {
129         throw DeadlyImportError( "OBJ-file is too small.");
130     }
131 
132     IOStreamBuffer<char> streamedBuffer;
133     streamedBuffer.open( fileStream.get() );
134 
135     // Allocate buffer and read file into it
136     //TextFileToBuffer( fileStream.get(),m_Buffer);
137 
138     // Get the model name
139     std::string  modelName, folderName;
140     std::string::size_type pos = file.find_last_of( "\\/" );
141     if ( pos != std::string::npos ) {
142         modelName = file.substr(pos+1, file.size() - pos - 1);
143         folderName = file.substr( 0, pos );
144         if ( !folderName.empty() ) {
145             pIOHandler->PushDirectory( folderName );
146         }
147     } else {
148         modelName = file;
149     }
150 
151     // This next stage takes ~ 1/3th of the total readFile task
152     // so should amount for 1/3th of the progress
153     // only update every 100KB or it'll be too slow
154     /*unsigned int progress = 0;
155     unsigned int progressCounter = 0;
156     const unsigned int updateProgressEveryBytes = 100 * 1024;
157     const unsigned int progressTotal = static_cast<unsigned int>(3*m_Buffer.size()/updateProgressEveryBytes);*/
158     // process all '\'
159     /*std::vector<char> ::iterator iter = m_Buffer.begin();
160     while (iter != m_Buffer.end())
161     {
162         if (*iter == '\\')
163         {
164             // remove '\'
165             iter = m_Buffer.erase(iter);
166             // remove next character
167             while (*iter == '\r' || *iter == '\n')
168                 iter = m_Buffer.erase(iter);
169         }
170         else
171             ++iter;
172 
173         if (++progressCounter >= updateProgressEveryBytes)
174         {
175             m_progress->UpdateFileRead(++progress, progressTotal);
176             progressCounter = 0;
177         }
178     }*/
179 
180     // 1/3rd progress
181     m_progress->UpdateFileRead(1, 3);
182 
183     // parse the file into a temporary representation
184     ObjFileParser parser( streamedBuffer, modelName, pIOHandler, m_progress, file);
185 
186     // And create the proper return structures out of it
187     CreateDataFromImport(parser.GetModel(), pScene);
188 
189     streamedBuffer.close();
190 
191     // Clean up allocated storage for the next import
192     m_Buffer.clear();
193 
194     // Pop directory stack
195     if ( pIOHandler->StackSize() > 0 ) {
196         pIOHandler->PopDirectory();
197     }
198 }
199 
200 // ------------------------------------------------------------------------------------------------
201 //  Create the data from parsed obj-file
CreateDataFromImport(const ObjFile::Model * pModel,aiScene * pScene)202 void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene) {
203     if( 0L == pModel ) {
204         return;
205     }
206 
207     // Create the root node of the scene
208     pScene->mRootNode = new aiNode;
209     if ( !pModel->m_ModelName.empty() )
210     {
211         // Set the name of the scene
212         pScene->mRootNode->mName.Set(pModel->m_ModelName);
213     }
214     else
215     {
216         // This is a fatal error, so break down the application
217         ai_assert(false);
218     }
219 
220     // Create nodes for the whole scene
221     std::vector<aiMesh*> MeshArray;
222     for (size_t index = 0; index < pModel->m_Objects.size(); index++)
223     {
224         createNodes(pModel, pModel->m_Objects[ index ], pScene->mRootNode, pScene, MeshArray);
225     }
226 
227     // Create mesh pointer buffer for this scene
228     if (pScene->mNumMeshes > 0)
229     {
230         pScene->mMeshes = new aiMesh*[ MeshArray.size() ];
231         for (size_t index =0; index < MeshArray.size(); index++)
232         {
233             pScene->mMeshes[ index ] = MeshArray[ index ];
234         }
235     }
236 
237     // Create all materials
238     createMaterials( pModel, pScene );
239 }
240 
241 // ------------------------------------------------------------------------------------------------
242 //  Creates all nodes of the model
createNodes(const ObjFile::Model * pModel,const ObjFile::Object * pObject,aiNode * pParent,aiScene * pScene,std::vector<aiMesh * > & MeshArray)243 aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pObject,
244                                      aiNode *pParent, aiScene* pScene,
245                                      std::vector<aiMesh*> &MeshArray )
246 {
247     ai_assert( NULL != pModel );
248     if( NULL == pObject ) {
249         return NULL;
250     }
251 
252     // Store older mesh size to be able to computes mesh offsets for new mesh instances
253     const size_t oldMeshSize = MeshArray.size();
254     aiNode *pNode = new aiNode;
255 
256     pNode->mName = pObject->m_strObjName;
257 
258     // If we have a parent node, store it
259     if( pParent != NULL ) {
260         appendChildToParentNode( pParent, pNode );
261     }
262 
263     for ( size_t i=0; i< pObject->m_Meshes.size(); i++ )
264     {
265         unsigned int meshId = pObject->m_Meshes[ i ];
266         aiMesh *pMesh = createTopology( pModel, pObject, meshId );
267         if( pMesh && pMesh->mNumFaces > 0 ) {
268             MeshArray.push_back( pMesh );
269         }
270     }
271 
272     // Create all nodes from the sub-objects stored in the current object
273     if ( !pObject->m_SubObjects.empty() )
274     {
275         size_t numChilds = pObject->m_SubObjects.size();
276         pNode->mNumChildren = static_cast<unsigned int>( numChilds );
277         pNode->mChildren = new aiNode*[ numChilds ];
278         pNode->mNumMeshes = 1;
279         pNode->mMeshes = new unsigned int[ 1 ];
280     }
281 
282     // Set mesh instances into scene- and node-instances
283     const size_t meshSizeDiff = MeshArray.size()- oldMeshSize;
284     if ( meshSizeDiff > 0 )
285     {
286         pNode->mMeshes = new unsigned int[ meshSizeDiff ];
287         pNode->mNumMeshes = static_cast<unsigned int>( meshSizeDiff );
288         size_t index = 0;
289         for (size_t i = oldMeshSize; i < MeshArray.size(); i++)
290         {
291             pNode->mMeshes[ index ] = pScene->mNumMeshes;
292             pScene->mNumMeshes++;
293             index++;
294         }
295     }
296 
297     return pNode;
298 }
299 
300 // ------------------------------------------------------------------------------------------------
301 //  Create topology data
createTopology(const ObjFile::Model * pModel,const ObjFile::Object * pData,unsigned int meshIndex)302 aiMesh *ObjFileImporter::createTopology( const ObjFile::Model* pModel, const ObjFile::Object* pData, unsigned int meshIndex ) {
303     // Checking preconditions
304     ai_assert( NULL != pModel );
305 
306     if( NULL == pData ) {
307         return NULL;
308     }
309 
310     // Create faces
311     ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ meshIndex ];
312     if( !pObjMesh ) {
313         return NULL;
314     }
315 
316     if( pObjMesh->m_Faces.empty() ) {
317         return NULL;
318     }
319 
320     aiMesh* pMesh = new aiMesh;
321     if( !pObjMesh->m_name.empty() ) {
322         pMesh->mName.Set( pObjMesh->m_name );
323     }
324 
325     for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++)
326     {
327         ObjFile::Face *const inp = pObjMesh->m_Faces[ index ];
328         ai_assert( NULL != inp  );
329 
330         if (inp->m_PrimitiveType == aiPrimitiveType_LINE) {
331             pMesh->mNumFaces += static_cast<unsigned int>(inp->m_vertices.size() - 1);
332             pMesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
333         } else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) {
334             pMesh->mNumFaces += static_cast<unsigned int>(inp->m_vertices.size());
335             pMesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
336         } else {
337             ++pMesh->mNumFaces;
338             if (inp->m_vertices.size() > 3) {
339                 pMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
340             } else {
341                 pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
342             }
343         }
344     }
345 
346     unsigned int uiIdxCount( 0u );
347     if ( pMesh->mNumFaces > 0 ) {
348         pMesh->mFaces = new aiFace[ pMesh->mNumFaces ];
349         if ( pObjMesh->m_uiMaterialIndex != ObjFile::Mesh::NoMaterial ) {
350             pMesh->mMaterialIndex = pObjMesh->m_uiMaterialIndex;
351         }
352 
353         unsigned int outIndex( 0 );
354 
355         // Copy all data from all stored meshes
356         for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++) {
357             ObjFile::Face* const inp = pObjMesh->m_Faces[ index ];
358             if (inp->m_PrimitiveType == aiPrimitiveType_LINE) {
359                 for(size_t i = 0; i < inp->m_vertices.size() - 1; ++i) {
360                     aiFace& f = pMesh->mFaces[ outIndex++ ];
361                     uiIdxCount += f.mNumIndices = 2;
362                     f.mIndices = new unsigned int[2];
363                 }
364                 continue;
365             }
366             else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) {
367                 for(size_t i = 0; i < inp->m_vertices.size(); ++i) {
368                     aiFace& f = pMesh->mFaces[ outIndex++ ];
369                     uiIdxCount += f.mNumIndices = 1;
370                     f.mIndices = new unsigned int[1];
371                 }
372                 continue;
373             }
374 
375             aiFace *pFace = &pMesh->mFaces[ outIndex++ ];
376             const unsigned int uiNumIndices = (unsigned int) pObjMesh->m_Faces[ index ]->m_vertices.size();
377             uiIdxCount += pFace->mNumIndices = (unsigned int) uiNumIndices;
378             if (pFace->mNumIndices > 0) {
379                 pFace->mIndices = new unsigned int[ uiNumIndices ];
380             }
381         }
382     }
383 
384     // Create mesh vertices
385     createVertexArray(pModel, pData, meshIndex, pMesh, uiIdxCount);
386 
387     return pMesh;
388 }
389 
390 // ------------------------------------------------------------------------------------------------
391 //  Creates a vertex array
createVertexArray(const ObjFile::Model * pModel,const ObjFile::Object * pCurrentObject,unsigned int uiMeshIndex,aiMesh * pMesh,unsigned int numIndices)392 void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
393                                         const ObjFile::Object* pCurrentObject,
394                                         unsigned int uiMeshIndex,
395                                         aiMesh* pMesh,
396                                         unsigned int numIndices) {
397     // Checking preconditions
398     ai_assert( NULL != pCurrentObject );
399 
400     // Break, if no faces are stored in object
401     if ( pCurrentObject->m_Meshes.empty() )
402         return;
403 
404     // Get current mesh
405     ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ];
406     if ( NULL == pObjMesh || pObjMesh->m_uiNumIndices < 1 ) {
407         return;
408     }
409 
410     // Copy vertices of this mesh instance
411     pMesh->mNumVertices = numIndices;
412     if (pMesh->mNumVertices == 0) {
413         throw DeadlyImportError( "OBJ: no vertices" );
414     } else if (pMesh->mNumVertices > AI_MAX_ALLOC(aiVector3D)) {
415         throw DeadlyImportError( "OBJ: Too many vertices, would run out of memory" );
416     }
417     pMesh->mVertices = new aiVector3D[ pMesh->mNumVertices ];
418 
419     // Allocate buffer for normal vectors
420     if ( !pModel->m_Normals.empty() && pObjMesh->m_hasNormals )
421         pMesh->mNormals = new aiVector3D[ pMesh->mNumVertices ];
422 
423     // Allocate buffer for vertex-color vectors
424     if ( !pModel->m_VertexColors.empty() )
425         pMesh->mColors[0] = new aiColor4D[ pMesh->mNumVertices ];
426 
427     // Allocate buffer for texture coordinates
428     if ( !pModel->m_TextureCoord.empty() && pObjMesh->m_uiUVCoordinates[0] )
429     {
430         pMesh->mNumUVComponents[ 0 ] = 2;
431         pMesh->mTextureCoords[ 0 ] = new aiVector3D[ pMesh->mNumVertices ];
432     }
433 
434     // Copy vertices, normals and textures into aiMesh instance
435     unsigned int newIndex = 0, outIndex = 0;
436     for ( size_t index=0; index < pObjMesh->m_Faces.size(); index++ ) {
437         // Get source face
438         ObjFile::Face *pSourceFace = pObjMesh->m_Faces[ index ];
439 
440         // Copy all index arrays
441         for ( size_t vertexIndex = 0, outVertexIndex = 0; vertexIndex < pSourceFace->m_vertices.size(); vertexIndex++ ) {
442             const unsigned int vertex = pSourceFace->m_vertices.at( vertexIndex );
443             if ( vertex >= pModel->m_Vertices.size() ) {
444                 throw DeadlyImportError( "OBJ: vertex index out of range" );
445             }
446 
447             pMesh->mVertices[ newIndex ] = pModel->m_Vertices[ vertex ];
448 
449             // Copy all normals
450             if ( !pModel->m_Normals.empty() && vertexIndex < pSourceFace->m_normals.size()) {
451                 const unsigned int normal = pSourceFace->m_normals.at( vertexIndex );
452                 if ( normal >= pModel->m_Normals.size() ) {
453                     throw DeadlyImportError( "OBJ: vertex normal index out of range" );
454                 }
455                 pMesh->mNormals[ newIndex ] = pModel->m_Normals[ normal ];
456             }
457 
458             // Copy all vertex colors
459             if ( !pModel->m_VertexColors.empty())
460             {
461                 const aiVector3D color = pModel->m_VertexColors[ vertex ];
462                 pMesh->mColors[0][ newIndex ] = aiColor4D(color.x, color.y, color.z, 1.0);
463             }
464 
465             // Copy all texture coordinates
466             if ( !pModel->m_TextureCoord.empty() && vertexIndex < pSourceFace->m_texturCoords.size())
467             {
468                 const unsigned int tex = pSourceFace->m_texturCoords.at( vertexIndex );
469                 ai_assert( tex < pModel->m_TextureCoord.size() );
470 
471                 if ( tex >= pModel->m_TextureCoord.size() )
472                     throw DeadlyImportError("OBJ: texture coordinate index out of range");
473 
474                 const aiVector3D &coord3d = pModel->m_TextureCoord[ tex ];
475                 pMesh->mTextureCoords[ 0 ][ newIndex ] = aiVector3D( coord3d.x, coord3d.y, coord3d.z );
476             }
477 
478             if ( pMesh->mNumVertices <= newIndex ) {
479                 throw DeadlyImportError("OBJ: bad vertex index");
480             }
481 
482             // Get destination face
483             aiFace *pDestFace = &pMesh->mFaces[ outIndex ];
484 
485             const bool last = ( vertexIndex == pSourceFace->m_vertices.size() - 1 );
486             if (pSourceFace->m_PrimitiveType != aiPrimitiveType_LINE || !last) {
487                 pDestFace->mIndices[ outVertexIndex ] = newIndex;
488                 outVertexIndex++;
489             }
490 
491             if (pSourceFace->m_PrimitiveType == aiPrimitiveType_POINT) {
492                 outIndex++;
493                 outVertexIndex = 0;
494             } else if (pSourceFace->m_PrimitiveType == aiPrimitiveType_LINE) {
495                 outVertexIndex = 0;
496 
497                 if(!last)
498                     outIndex++;
499 
500                 if (vertexIndex) {
501                     if(!last) {
502                         pMesh->mVertices[ newIndex+1 ] = pMesh->mVertices[ newIndex ];
503                         if ( !pSourceFace->m_normals.empty() && !pModel->m_Normals.empty()) {
504                             pMesh->mNormals[ newIndex+1 ] = pMesh->mNormals[newIndex ];
505                         }
506                         if ( !pModel->m_TextureCoord.empty() ) {
507                             for ( size_t i=0; i < pMesh->GetNumUVChannels(); i++ ) {
508                                 pMesh->mTextureCoords[ i ][ newIndex+1 ] = pMesh->mTextureCoords[ i ][ newIndex ];
509                             }
510                         }
511                         ++newIndex;
512                     }
513 
514                     pDestFace[-1].mIndices[1] = newIndex;
515                 }
516             }
517             else if (last) {
518                 outIndex++;
519             }
520             ++newIndex;
521         }
522     }
523 }
524 
525 // ------------------------------------------------------------------------------------------------
526 //  Counts all stored meshes
countObjects(const std::vector<ObjFile::Object * > & rObjects,int & iNumMeshes)527 void ObjFileImporter::countObjects(const std::vector<ObjFile::Object*> &rObjects, int &iNumMeshes)
528 {
529     iNumMeshes = 0;
530     if ( rObjects.empty() )
531         return;
532 
533     iNumMeshes += static_cast<unsigned int>( rObjects.size() );
534     for (std::vector<ObjFile::Object*>::const_iterator it = rObjects.begin();
535         it != rObjects.end();
536         ++it)
537     {
538         if (!(*it)->m_SubObjects.empty())
539         {
540             countObjects((*it)->m_SubObjects, iNumMeshes);
541         }
542     }
543 }
544 
545 // ------------------------------------------------------------------------------------------------
546 //   Add clamp mode property to material if necessary
addTextureMappingModeProperty(aiMaterial * mat,aiTextureType type,int clampMode,int index)547 void ObjFileImporter::addTextureMappingModeProperty( aiMaterial* mat, aiTextureType type, int clampMode, int index) {
548     if ( nullptr == mat ) {
549         return;
550     }
551 
552     mat->AddProperty<int>( &clampMode, 1, AI_MATKEY_MAPPINGMODE_U( type, index ) );
553     mat->AddProperty<int>( &clampMode, 1, AI_MATKEY_MAPPINGMODE_V( type, index ) );
554 }
555 
556 // ------------------------------------------------------------------------------------------------
557 //  Creates the material
createMaterials(const ObjFile::Model * pModel,aiScene * pScene)558 void ObjFileImporter::createMaterials(const ObjFile::Model* pModel, aiScene* pScene ) {
559     if ( NULL == pScene ) {
560         return;
561     }
562 
563     const unsigned int numMaterials = (unsigned int) pModel->m_MaterialLib.size();
564     pScene->mNumMaterials = 0;
565     if ( pModel->m_MaterialLib.empty() ) {
566         DefaultLogger::get()->debug("OBJ: no materials specified");
567         return;
568     }
569 
570     pScene->mMaterials = new aiMaterial*[ numMaterials ];
571     for ( unsigned int matIndex = 0; matIndex < numMaterials; matIndex++ )
572     {
573         // Store material name
574         std::map<std::string, ObjFile::Material*>::const_iterator it;
575         it = pModel->m_MaterialMap.find( pModel->m_MaterialLib[ matIndex ] );
576 
577         // No material found, use the default material
578         if ( pModel->m_MaterialMap.end() == it )
579             continue;
580 
581         aiMaterial* mat = new aiMaterial;
582         ObjFile::Material *pCurrentMaterial = (*it).second;
583         mat->AddProperty( &pCurrentMaterial->MaterialName, AI_MATKEY_NAME );
584 
585         // convert illumination model
586         int sm = 0;
587         switch (pCurrentMaterial->illumination_model)
588         {
589         case 0:
590             sm = aiShadingMode_NoShading;
591             break;
592         case 1:
593             sm = aiShadingMode_Gouraud;
594             break;
595         case 2:
596             sm = aiShadingMode_Phong;
597             break;
598         default:
599             sm = aiShadingMode_Gouraud;
600             DefaultLogger::get()->error("OBJ: unexpected illumination model (0-2 recognized)");
601         }
602 
603         mat->AddProperty<int>( &sm, 1, AI_MATKEY_SHADING_MODEL);
604 
605         // Adding material colors
606         mat->AddProperty( &pCurrentMaterial->ambient, 1, AI_MATKEY_COLOR_AMBIENT );
607         mat->AddProperty( &pCurrentMaterial->diffuse, 1, AI_MATKEY_COLOR_DIFFUSE );
608         mat->AddProperty( &pCurrentMaterial->specular, 1, AI_MATKEY_COLOR_SPECULAR );
609         mat->AddProperty( &pCurrentMaterial->emissive, 1, AI_MATKEY_COLOR_EMISSIVE );
610         mat->AddProperty( &pCurrentMaterial->shineness, 1, AI_MATKEY_SHININESS );
611         mat->AddProperty( &pCurrentMaterial->alpha, 1, AI_MATKEY_OPACITY );
612         mat->AddProperty( &pCurrentMaterial->transparent,1,AI_MATKEY_COLOR_TRANSPARENT);
613 
614         // Adding refraction index
615         mat->AddProperty( &pCurrentMaterial->ior, 1, AI_MATKEY_REFRACTI );
616 
617         // Adding textures
618         const int uvwIndex = 0;
619 
620         if ( 0 != pCurrentMaterial->texture.length )
621         {
622             mat->AddProperty( &pCurrentMaterial->texture, AI_MATKEY_TEXTURE_DIFFUSE(0));
623             mat->AddProperty( &uvwIndex, 1, AI_MATKEY_UVWSRC_DIFFUSE(0) );
624             if (pCurrentMaterial->clamp[ObjFile::Material::TextureDiffuseType])
625             {
626                 addTextureMappingModeProperty(mat, aiTextureType_DIFFUSE);
627             }
628         }
629 
630         if ( 0 != pCurrentMaterial->textureAmbient.length )
631         {
632             mat->AddProperty( &pCurrentMaterial->textureAmbient, AI_MATKEY_TEXTURE_AMBIENT(0));
633             mat->AddProperty( &uvwIndex, 1, AI_MATKEY_UVWSRC_AMBIENT(0) );
634             if (pCurrentMaterial->clamp[ObjFile::Material::TextureAmbientType])
635             {
636                 addTextureMappingModeProperty(mat, aiTextureType_AMBIENT);
637             }
638         }
639 
640         if ( 0 != pCurrentMaterial->textureEmissive.length )
641         {
642             mat->AddProperty( &pCurrentMaterial->textureEmissive, AI_MATKEY_TEXTURE_EMISSIVE(0));
643             mat->AddProperty( &uvwIndex, 1, AI_MATKEY_UVWSRC_EMISSIVE(0) );
644         }
645 
646         if ( 0 != pCurrentMaterial->textureSpecular.length )
647         {
648             mat->AddProperty( &pCurrentMaterial->textureSpecular, AI_MATKEY_TEXTURE_SPECULAR(0));
649             mat->AddProperty( &uvwIndex, 1, AI_MATKEY_UVWSRC_SPECULAR(0) );
650             if (pCurrentMaterial->clamp[ObjFile::Material::TextureSpecularType])
651             {
652                 addTextureMappingModeProperty(mat, aiTextureType_SPECULAR);
653             }
654         }
655 
656         if ( 0 != pCurrentMaterial->textureBump.length )
657         {
658             mat->AddProperty( &pCurrentMaterial->textureBump, AI_MATKEY_TEXTURE_HEIGHT(0));
659             mat->AddProperty( &uvwIndex, 1, AI_MATKEY_UVWSRC_HEIGHT(0) );
660             if (pCurrentMaterial->clamp[ObjFile::Material::TextureBumpType])
661             {
662                 addTextureMappingModeProperty(mat, aiTextureType_HEIGHT);
663             }
664         }
665 
666         if ( 0 != pCurrentMaterial->textureNormal.length )
667         {
668             mat->AddProperty( &pCurrentMaterial->textureNormal, AI_MATKEY_TEXTURE_NORMALS(0));
669             mat->AddProperty( &uvwIndex, 1, AI_MATKEY_UVWSRC_NORMALS(0) );
670             if (pCurrentMaterial->clamp[ObjFile::Material::TextureNormalType])
671             {
672                 addTextureMappingModeProperty(mat, aiTextureType_NORMALS);
673             }
674         }
675 
676         if( 0 != pCurrentMaterial->textureReflection[0].length )
677         {
678             ObjFile::Material::TextureType type = 0 != pCurrentMaterial->textureReflection[1].length ?
679                 ObjFile::Material::TextureReflectionCubeTopType :
680                 ObjFile::Material::TextureReflectionSphereType;
681 
682             unsigned count = type == ObjFile::Material::TextureReflectionSphereType ? 1 : 6;
683             for( unsigned i = 0; i < count; i++ )
684             {
685                 mat->AddProperty(&pCurrentMaterial->textureReflection[i], AI_MATKEY_TEXTURE_REFLECTION(i));
686                 mat->AddProperty( &uvwIndex, 1, AI_MATKEY_UVWSRC_REFLECTION(i) );
687 
688                 if(pCurrentMaterial->clamp[type])
689                     addTextureMappingModeProperty(mat, aiTextureType_REFLECTION, 1, i);
690             }
691         }
692 
693         if ( 0 != pCurrentMaterial->textureDisp.length )
694         {
695             mat->AddProperty( &pCurrentMaterial->textureDisp, AI_MATKEY_TEXTURE_DISPLACEMENT(0) );
696             mat->AddProperty( &uvwIndex, 1, AI_MATKEY_UVWSRC_DISPLACEMENT(0) );
697             if (pCurrentMaterial->clamp[ObjFile::Material::TextureDispType])
698             {
699                 addTextureMappingModeProperty(mat, aiTextureType_DISPLACEMENT);
700             }
701         }
702 
703         if ( 0 != pCurrentMaterial->textureOpacity.length )
704         {
705             mat->AddProperty( &pCurrentMaterial->textureOpacity, AI_MATKEY_TEXTURE_OPACITY(0));
706             mat->AddProperty( &uvwIndex, 1, AI_MATKEY_UVWSRC_OPACITY(0) );
707             if (pCurrentMaterial->clamp[ObjFile::Material::TextureOpacityType])
708             {
709                 addTextureMappingModeProperty(mat, aiTextureType_OPACITY);
710             }
711         }
712 
713         if ( 0 != pCurrentMaterial->textureSpecularity.length )
714         {
715             mat->AddProperty( &pCurrentMaterial->textureSpecularity, AI_MATKEY_TEXTURE_SHININESS(0));
716             mat->AddProperty( &uvwIndex, 1, AI_MATKEY_UVWSRC_SHININESS(0) );
717             if (pCurrentMaterial->clamp[ObjFile::Material::TextureSpecularityType])
718             {
719                 addTextureMappingModeProperty(mat, aiTextureType_SHININESS);
720             }
721         }
722 
723         // Store material property info in material array in scene
724         pScene->mMaterials[ pScene->mNumMaterials ] = mat;
725         pScene->mNumMaterials++;
726     }
727 
728     // Test number of created materials.
729     ai_assert( pScene->mNumMaterials == numMaterials );
730 }
731 
732 // ------------------------------------------------------------------------------------------------
733 //  Appends this node to the parent node
appendChildToParentNode(aiNode * pParent,aiNode * pChild)734 void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild)
735 {
736     // Checking preconditions
737     ai_assert( NULL != pParent );
738     ai_assert( NULL != pChild );
739 
740     // Assign parent to child
741     pChild->mParent = pParent;
742 
743     // If already children was assigned to the parent node, store them in a
744     std::vector<aiNode*> temp;
745     if (pParent->mChildren != NULL)
746     {
747         ai_assert( 0 != pParent->mNumChildren );
748         for (size_t index = 0; index < pParent->mNumChildren; index++)
749         {
750             temp.push_back(pParent->mChildren [ index ] );
751         }
752         delete [] pParent->mChildren;
753     }
754 
755     // Copy node instances into parent node
756     pParent->mNumChildren++;
757     pParent->mChildren = new aiNode*[ pParent->mNumChildren ];
758     for (size_t index = 0; index < pParent->mNumChildren-1; index++)
759     {
760         pParent->mChildren[ index ] = temp [ index ];
761     }
762     pParent->mChildren[ pParent->mNumChildren-1 ] = pChild;
763 }
764 
765 // ------------------------------------------------------------------------------------------------
766 
767 }   // Namespace Assimp
768 
769 #endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
770