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