1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2015, 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 <boost/scoped_ptr.hpp>
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 & pFile,aiScene * pScene,IOSystem * pIOHandler)117 void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
118 {
119     // Read file into memory
120     const std::string mode = "rb";
121     boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, mode));
122     if( !file.get() ) {
123         throw DeadlyImportError( "Failed to open file " + pFile + "." );
124     }
125 
126     // Get the file-size and validate it, throwing an exception when fails
127     size_t fileSize = file->FileSize();
128     if( fileSize < ObjMinSize ) {
129         throw DeadlyImportError( "OBJ-file is too small.");
130     }
131 
132     // Allocate buffer and read file into it
133     TextFileToBuffer(file.get(),m_Buffer);
134 
135     // Get the model name
136     std::string  modelName, folderName;
137     std::string::size_type pos = pFile.find_last_of( "\\/" );
138     if ( pos != std::string::npos ) {
139         modelName = pFile.substr(pos+1, pFile.size() - pos - 1);
140         folderName = pFile.substr( 0, pos );
141         if ( !folderName.empty() ) {
142             pIOHandler->PushDirectory( folderName );
143         }
144     } else {
145         modelName = pFile;
146     }
147 
148     // process all '\'
149     std::vector<char> ::iterator iter = m_Buffer.begin();
150     while (iter != m_Buffer.end())
151     {
152         if (*iter == '\\')
153         {
154             // remove '\'
155             iter = m_Buffer.erase(iter);
156             // remove next character
157             while (*iter == '\r' || *iter == '\n')
158                 iter = m_Buffer.erase(iter);
159         }
160         else
161             ++iter;
162     }
163 
164     // parse the file into a temporary representation
165     ObjFileParser parser(m_Buffer, modelName, pIOHandler);
166 
167     // And create the proper return structures out of it
168     CreateDataFromImport(parser.GetModel(), pScene);
169 
170     // Clean up allocated storage for the next import
171     m_Buffer.clear();
172 
173     // Pop directory stack
174     if ( pIOHandler->StackSize() > 0 ) {
175         pIOHandler->PopDirectory();
176     }
177 }
178 
179 // ------------------------------------------------------------------------------------------------
180 //  Create the data from parsed obj-file
CreateDataFromImport(const ObjFile::Model * pModel,aiScene * pScene)181 void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene) {
182     if( 0L == pModel ) {
183         return;
184     }
185 
186     // Create the root node of the scene
187     pScene->mRootNode = new aiNode;
188     if ( !pModel->m_ModelName.empty() )
189     {
190         // Set the name of the scene
191         pScene->mRootNode->mName.Set(pModel->m_ModelName);
192     }
193     else
194     {
195         // This is a fatal error, so break down the application
196         ai_assert(false);
197     }
198 
199     // Create nodes for the whole scene
200     std::vector<aiMesh*> MeshArray;
201     for (size_t index = 0; index < pModel->m_Objects.size(); index++)
202     {
203         createNodes(pModel, pModel->m_Objects[ index ], pScene->mRootNode, pScene, MeshArray);
204     }
205 
206     // Create mesh pointer buffer for this scene
207     if (pScene->mNumMeshes > 0)
208     {
209         pScene->mMeshes = new aiMesh*[ MeshArray.size() ];
210         for (size_t index =0; index < MeshArray.size(); index++)
211         {
212             pScene->mMeshes[ index ] = MeshArray[ index ];
213         }
214     }
215 
216     // Create all materials
217     createMaterials( pModel, pScene );
218 }
219 
220 // ------------------------------------------------------------------------------------------------
221 //  Creates all nodes of the model
createNodes(const ObjFile::Model * pModel,const ObjFile::Object * pObject,aiNode * pParent,aiScene * pScene,std::vector<aiMesh * > & MeshArray)222 aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pObject,
223                                      aiNode *pParent, aiScene* pScene,
224                                      std::vector<aiMesh*> &MeshArray )
225 {
226     ai_assert( NULL != pModel );
227     if( NULL == pObject ) {
228         return NULL;
229     }
230 
231     // Store older mesh size to be able to computes mesh offsets for new mesh instances
232     const size_t oldMeshSize = MeshArray.size();
233     aiNode *pNode = new aiNode;
234 
235     pNode->mName = pObject->m_strObjName;
236 
237     // If we have a parent node, store it
238     if( pParent != NULL ) {
239         appendChildToParentNode( pParent, pNode );
240     }
241 
242     for ( size_t i=0; i< pObject->m_Meshes.size(); i++ )
243     {
244         unsigned int meshId = pObject->m_Meshes[ i ];
245         aiMesh *pMesh = createTopology( pModel, pObject, meshId );
246         if( pMesh && pMesh->mNumFaces > 0 ) {
247             MeshArray.push_back( pMesh );
248         }
249     }
250 
251     // Create all nodes from the sub-objects stored in the current object
252     if ( !pObject->m_SubObjects.empty() )
253     {
254         size_t numChilds = pObject->m_SubObjects.size();
255         pNode->mNumChildren = static_cast<unsigned int>( numChilds );
256         pNode->mChildren = new aiNode*[ numChilds ];
257         pNode->mNumMeshes = 1;
258         pNode->mMeshes = new unsigned int[ 1 ];
259     }
260 
261     // Set mesh instances into scene- and node-instances
262     const size_t meshSizeDiff = MeshArray.size()- oldMeshSize;
263     if ( meshSizeDiff > 0 )
264     {
265         pNode->mMeshes = new unsigned int[ meshSizeDiff ];
266         pNode->mNumMeshes = static_cast<unsigned int>( meshSizeDiff );
267         size_t index = 0;
268         for (size_t i = oldMeshSize; i < MeshArray.size(); i++)
269         {
270             pNode->mMeshes[ index ] = pScene->mNumMeshes;
271             pScene->mNumMeshes++;
272             index++;
273         }
274     }
275 
276     return pNode;
277 }
278 
279 // ------------------------------------------------------------------------------------------------
280 //  Create topology data
createTopology(const ObjFile::Model * pModel,const ObjFile::Object * pData,unsigned int meshIndex)281 aiMesh *ObjFileImporter::createTopology( const ObjFile::Model* pModel, const ObjFile::Object* pData,
282                                          unsigned int meshIndex )
283 {
284     // Checking preconditions
285     ai_assert( NULL != pModel );
286 
287     if( NULL == pData ) {
288         return NULL;
289     }
290 
291     // Create faces
292     ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ meshIndex ];
293     if( !pObjMesh ) {
294         return NULL;
295     }
296     ai_assert( NULL != pObjMesh );
297     aiMesh* pMesh = new aiMesh;
298     if( !pObjMesh->m_name.empty() ) {
299         pMesh->mName.Set( pObjMesh->m_name );
300     }
301 
302     for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++)
303     {
304         ObjFile::Face *const inp = pObjMesh->m_Faces[ index ];
305         ai_assert( NULL != inp  );
306 
307         if (inp->m_PrimitiveType == aiPrimitiveType_LINE) {
308             pMesh->mNumFaces += inp->m_pVertices->size() - 1;
309             pMesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
310         } else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) {
311             pMesh->mNumFaces += inp->m_pVertices->size();
312             pMesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
313         } else {
314             ++pMesh->mNumFaces;
315             if (inp->m_pVertices->size() > 3) {
316                 pMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
317             } else {
318                 pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
319             }
320         }
321     }
322 
323     unsigned int uiIdxCount( 0u );
324     if ( pMesh->mNumFaces > 0 ) {
325         pMesh->mFaces = new aiFace[ pMesh->mNumFaces ];
326         if ( pObjMesh->m_uiMaterialIndex != ObjFile::Mesh::NoMaterial ) {
327             pMesh->mMaterialIndex = pObjMesh->m_uiMaterialIndex;
328         }
329 
330         unsigned int outIndex( 0 );
331 
332         // Copy all data from all stored meshes
333         for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++) {
334             ObjFile::Face* const inp = pObjMesh->m_Faces[ index ];
335             if (inp->m_PrimitiveType == aiPrimitiveType_LINE) {
336                 for(size_t i = 0; i < inp->m_pVertices->size() - 1; ++i) {
337                     aiFace& f = pMesh->mFaces[ outIndex++ ];
338                     uiIdxCount += f.mNumIndices = 2;
339                     f.mIndices = new unsigned int[2];
340                 }
341                 continue;
342             }
343             else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) {
344                 for(size_t i = 0; i < inp->m_pVertices->size(); ++i) {
345                     aiFace& f = pMesh->mFaces[ outIndex++ ];
346                     uiIdxCount += f.mNumIndices = 1;
347                     f.mIndices = new unsigned int[1];
348                 }
349                 continue;
350             }
351 
352             aiFace *pFace = &pMesh->mFaces[ outIndex++ ];
353             const unsigned int uiNumIndices = (unsigned int) pObjMesh->m_Faces[ index ]->m_pVertices->size();
354             uiIdxCount += pFace->mNumIndices = (unsigned int) uiNumIndices;
355             if (pFace->mNumIndices > 0) {
356                 pFace->mIndices = new unsigned int[ uiNumIndices ];
357             }
358         }
359     }
360 
361     // Create mesh vertices
362     createVertexArray(pModel, pData, meshIndex, pMesh, uiIdxCount);
363 
364     return pMesh;
365 }
366 
367 // ------------------------------------------------------------------------------------------------
368 //  Creates a vertex array
createVertexArray(const ObjFile::Model * pModel,const ObjFile::Object * pCurrentObject,unsigned int uiMeshIndex,aiMesh * pMesh,unsigned int numIndices)369 void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
370                                         const ObjFile::Object* pCurrentObject,
371                                         unsigned int uiMeshIndex,
372                                         aiMesh* pMesh,
373                                         unsigned int numIndices)
374 {
375     // Checking preconditions
376     ai_assert( NULL != pCurrentObject );
377 
378     // Break, if no faces are stored in object
379     if ( pCurrentObject->m_Meshes.empty() )
380         return;
381 
382     // Get current mesh
383     ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ];
384     if ( NULL == pObjMesh || pObjMesh->m_uiNumIndices < 1)
385         return;
386 
387     // Copy vertices of this mesh instance
388     pMesh->mNumVertices = numIndices;
389     if (pMesh->mNumVertices == 0) {
390         throw DeadlyImportError( "OBJ: no vertices" );
391     } else if (pMesh->mNumVertices > AI_MAX_ALLOC(aiVector3D)) {
392         throw DeadlyImportError( "OBJ: Too many vertices, would run out of memory" );
393     }
394     pMesh->mVertices = new aiVector3D[ pMesh->mNumVertices ];
395 
396     // Allocate buffer for normal vectors
397     if ( !pModel->m_Normals.empty() && pObjMesh->m_hasNormals )
398         pMesh->mNormals = new aiVector3D[ pMesh->mNumVertices ];
399 
400     // Allocate buffer for texture coordinates
401     if ( !pModel->m_TextureCoord.empty() && pObjMesh->m_uiUVCoordinates[0] )
402     {
403         pMesh->mNumUVComponents[ 0 ] = 2;
404         pMesh->mTextureCoords[ 0 ] = new aiVector3D[ pMesh->mNumVertices ];
405     }
406 
407     // Copy vertices, normals and textures into aiMesh instance
408     unsigned int newIndex = 0, outIndex = 0;
409     for ( size_t index=0; index < pObjMesh->m_Faces.size(); index++ )
410     {
411         // Get source face
412         ObjFile::Face *pSourceFace = pObjMesh->m_Faces[ index ];
413 
414         // Copy all index arrays
415         for ( size_t vertexIndex = 0, outVertexIndex = 0; vertexIndex < pSourceFace->m_pVertices->size(); vertexIndex++ )
416         {
417             const unsigned int vertex = pSourceFace->m_pVertices->at( vertexIndex );
418             if ( vertex >= pModel->m_Vertices.size() )
419                 throw DeadlyImportError( "OBJ: vertex index out of range" );
420 
421             pMesh->mVertices[ newIndex ] = pModel->m_Vertices[ vertex ];
422 
423             // Copy all normals
424             if ( !pModel->m_Normals.empty() && vertexIndex < pSourceFace->m_pNormals->size())
425             {
426                 const unsigned int normal = pSourceFace->m_pNormals->at( vertexIndex );
427                 if ( normal >= pModel->m_Normals.size() )
428                     throw DeadlyImportError("OBJ: vertex normal index out of range");
429 
430                 pMesh->mNormals[ newIndex ] = pModel->m_Normals[ normal ];
431             }
432 
433             // Copy all texture coordinates
434             if ( !pModel->m_TextureCoord.empty() && vertexIndex < pSourceFace->m_pTexturCoords->size())
435             {
436                 const unsigned int tex = pSourceFace->m_pTexturCoords->at( vertexIndex );
437                 ai_assert( tex < pModel->m_TextureCoord.size() );
438 
439                 if ( tex >= pModel->m_TextureCoord.size() )
440                     throw DeadlyImportError("OBJ: texture coordinate index out of range");
441 
442                 const aiVector3D &coord3d = pModel->m_TextureCoord[ tex ];
443                 pMesh->mTextureCoords[ 0 ][ newIndex ] = aiVector3D( coord3d.x, coord3d.y, coord3d.z );
444             }
445 
446             if ( pMesh->mNumVertices <= newIndex ) {
447                 throw DeadlyImportError("OBJ: bad vertex index");
448             }
449 
450             // Get destination face
451             aiFace *pDestFace = &pMesh->mFaces[ outIndex ];
452 
453             const bool last = ( vertexIndex == pSourceFace->m_pVertices->size() - 1 );
454             if (pSourceFace->m_PrimitiveType != aiPrimitiveType_LINE || !last)
455             {
456                 pDestFace->mIndices[ outVertexIndex ] = newIndex;
457                 outVertexIndex++;
458             }
459 
460             if (pSourceFace->m_PrimitiveType == aiPrimitiveType_POINT)
461             {
462                 outIndex++;
463                 outVertexIndex = 0;
464             }
465             else if (pSourceFace->m_PrimitiveType == aiPrimitiveType_LINE)
466             {
467                 outVertexIndex = 0;
468 
469                 if(!last)
470                     outIndex++;
471 
472                 if (vertexIndex) {
473                     if(!last) {
474                         pMesh->mVertices[ newIndex+1 ] = pMesh->mVertices[ newIndex ];
475                         if ( !pSourceFace->m_pNormals->empty() && !pModel->m_Normals.empty()) {
476                             pMesh->mNormals[ newIndex+1 ] = pMesh->mNormals[newIndex ];
477                         }
478                         if ( !pModel->m_TextureCoord.empty() ) {
479                             for ( size_t i=0; i < pMesh->GetNumUVChannels(); i++ ) {
480                                 pMesh->mTextureCoords[ i ][ newIndex+1 ] = pMesh->mTextureCoords[ i ][ newIndex ];
481                             }
482                         }
483                         ++newIndex;
484                     }
485 
486                     pDestFace[-1].mIndices[1] = newIndex;
487                 }
488             }
489             else if (last) {
490                 outIndex++;
491             }
492             ++newIndex;
493         }
494     }
495 }
496 
497 // ------------------------------------------------------------------------------------------------
498 //  Counts all stored meshes
countObjects(const std::vector<ObjFile::Object * > & rObjects,int & iNumMeshes)499 void ObjFileImporter::countObjects(const std::vector<ObjFile::Object*> &rObjects, int &iNumMeshes)
500 {
501     iNumMeshes = 0;
502     if ( rObjects.empty() )
503         return;
504 
505     iNumMeshes += static_cast<unsigned int>( rObjects.size() );
506     for (std::vector<ObjFile::Object*>::const_iterator it = rObjects.begin();
507         it != rObjects.end();
508         ++it)
509     {
510         if (!(*it)->m_SubObjects.empty())
511         {
512             countObjects((*it)->m_SubObjects, iNumMeshes);
513         }
514     }
515 }
516 
517 // ------------------------------------------------------------------------------------------------
518 //   Add clamp mode property to material if necessary
addTextureMappingModeProperty(aiMaterial * mat,aiTextureType type,int clampMode)519 void ObjFileImporter::addTextureMappingModeProperty(aiMaterial* mat, aiTextureType type, int clampMode)
520 {
521     ai_assert( NULL != mat);
522     mat->AddProperty<int>(&clampMode, 1, AI_MATKEY_MAPPINGMODE_U(type, 0));
523     mat->AddProperty<int>(&clampMode, 1, AI_MATKEY_MAPPINGMODE_V(type, 0));
524 }
525 
526 // ------------------------------------------------------------------------------------------------
527 //  Creates the material
createMaterials(const ObjFile::Model * pModel,aiScene * pScene)528 void ObjFileImporter::createMaterials(const ObjFile::Model* pModel, aiScene* pScene )
529 {
530     ai_assert( NULL != pScene );
531     if ( NULL == pScene )
532         return;
533 
534     const unsigned int numMaterials = (unsigned int) pModel->m_MaterialLib.size();
535     pScene->mNumMaterials = 0;
536     if ( pModel->m_MaterialLib.empty() ) {
537         DefaultLogger::get()->debug("OBJ: no materials specified");
538         return;
539     }
540 
541     pScene->mMaterials = new aiMaterial*[ numMaterials ];
542     for ( unsigned int matIndex = 0; matIndex < numMaterials; matIndex++ )
543     {
544         // Store material name
545         std::map<std::string, ObjFile::Material*>::const_iterator it;
546         it = pModel->m_MaterialMap.find( pModel->m_MaterialLib[ matIndex ] );
547 
548         // No material found, use the default material
549         if ( pModel->m_MaterialMap.end() == it )
550             continue;
551 
552         aiMaterial* mat = new aiMaterial;
553         ObjFile::Material *pCurrentMaterial = (*it).second;
554         mat->AddProperty( &pCurrentMaterial->MaterialName, AI_MATKEY_NAME );
555 
556         // convert illumination model
557         int sm = 0;
558         switch (pCurrentMaterial->illumination_model)
559         {
560         case 0:
561             sm = aiShadingMode_NoShading;
562             break;
563         case 1:
564             sm = aiShadingMode_Gouraud;
565             break;
566         case 2:
567             sm = aiShadingMode_Phong;
568             break;
569         default:
570             sm = aiShadingMode_Gouraud;
571             DefaultLogger::get()->error("OBJ: unexpected illumination model (0-2 recognized)");
572         }
573 
574         mat->AddProperty<int>( &sm, 1, AI_MATKEY_SHADING_MODEL);
575 
576         // multiplying the specular exponent with 2 seems to yield better results
577         pCurrentMaterial->shineness *= 4.f;
578 
579         // Adding material colors
580         mat->AddProperty( &pCurrentMaterial->ambient, 1, AI_MATKEY_COLOR_AMBIENT );
581         mat->AddProperty( &pCurrentMaterial->diffuse, 1, AI_MATKEY_COLOR_DIFFUSE );
582         mat->AddProperty( &pCurrentMaterial->specular, 1, AI_MATKEY_COLOR_SPECULAR );
583         mat->AddProperty( &pCurrentMaterial->emissive, 1, AI_MATKEY_COLOR_EMISSIVE );
584         mat->AddProperty( &pCurrentMaterial->shineness, 1, AI_MATKEY_SHININESS );
585         mat->AddProperty( &pCurrentMaterial->alpha, 1, AI_MATKEY_OPACITY );
586 
587         // Adding refraction index
588         mat->AddProperty( &pCurrentMaterial->ior, 1, AI_MATKEY_REFRACTI );
589 
590         // Adding textures
591         if ( 0 != pCurrentMaterial->texture.length )
592         {
593             mat->AddProperty( &pCurrentMaterial->texture, AI_MATKEY_TEXTURE_DIFFUSE(0));
594             if (pCurrentMaterial->clamp[ObjFile::Material::TextureDiffuseType])
595             {
596                 addTextureMappingModeProperty(mat, aiTextureType_DIFFUSE);
597             }
598         }
599 
600         if ( 0 != pCurrentMaterial->textureAmbient.length )
601         {
602             mat->AddProperty( &pCurrentMaterial->textureAmbient, AI_MATKEY_TEXTURE_AMBIENT(0));
603             if (pCurrentMaterial->clamp[ObjFile::Material::TextureAmbientType])
604             {
605                 addTextureMappingModeProperty(mat, aiTextureType_AMBIENT);
606             }
607         }
608 
609         if ( 0 != pCurrentMaterial->textureEmissive.length )
610             mat->AddProperty( &pCurrentMaterial->textureEmissive, AI_MATKEY_TEXTURE_EMISSIVE(0));
611 
612         if ( 0 != pCurrentMaterial->textureSpecular.length )
613         {
614             mat->AddProperty( &pCurrentMaterial->textureSpecular, AI_MATKEY_TEXTURE_SPECULAR(0));
615             if (pCurrentMaterial->clamp[ObjFile::Material::TextureSpecularType])
616             {
617                 addTextureMappingModeProperty(mat, aiTextureType_SPECULAR);
618             }
619         }
620 
621         if ( 0 != pCurrentMaterial->textureBump.length )
622         {
623             mat->AddProperty( &pCurrentMaterial->textureBump, AI_MATKEY_TEXTURE_HEIGHT(0));
624             if (pCurrentMaterial->clamp[ObjFile::Material::TextureBumpType])
625             {
626                 addTextureMappingModeProperty(mat, aiTextureType_HEIGHT);
627             }
628         }
629 
630         if ( 0 != pCurrentMaterial->textureNormal.length )
631         {
632             mat->AddProperty( &pCurrentMaterial->textureNormal, AI_MATKEY_TEXTURE_NORMALS(0));
633             if (pCurrentMaterial->clamp[ObjFile::Material::TextureNormalType])
634             {
635                 addTextureMappingModeProperty(mat, aiTextureType_NORMALS);
636             }
637         }
638 
639         if( 0 != pCurrentMaterial->textureReflection[0].length )
640         {
641             ObjFile::Material::TextureType type = 0 != pCurrentMaterial->textureReflection[1].length ?
642                 ObjFile::Material::TextureReflectionCubeTopType :
643                 ObjFile::Material::TextureReflectionSphereType;
644 
645             unsigned count = type == ObjFile::Material::TextureReflectionSphereType ? 1 : 6;
646             for( unsigned i = 0; i < count; i++ )
647                 mat->AddProperty(&pCurrentMaterial->textureReflection[i], AI_MATKEY_TEXTURE_REFLECTION(i));
648 
649             if(pCurrentMaterial->clamp[type])
650                 //TODO addTextureMappingModeProperty should accept an index to handle clamp option for each
651                 //texture of a cubemap
652                 addTextureMappingModeProperty(mat, aiTextureType_REFLECTION);
653         }
654 
655         if ( 0 != pCurrentMaterial->textureDisp.length )
656         {
657             mat->AddProperty( &pCurrentMaterial->textureDisp, AI_MATKEY_TEXTURE_DISPLACEMENT(0) );
658             if (pCurrentMaterial->clamp[ObjFile::Material::TextureDispType])
659             {
660                 addTextureMappingModeProperty(mat, aiTextureType_DISPLACEMENT);
661             }
662         }
663 
664         if ( 0 != pCurrentMaterial->textureOpacity.length )
665         {
666             mat->AddProperty( &pCurrentMaterial->textureOpacity, AI_MATKEY_TEXTURE_OPACITY(0));
667             if (pCurrentMaterial->clamp[ObjFile::Material::TextureOpacityType])
668             {
669                 addTextureMappingModeProperty(mat, aiTextureType_OPACITY);
670             }
671         }
672 
673         if ( 0 != pCurrentMaterial->textureSpecularity.length )
674         {
675             mat->AddProperty( &pCurrentMaterial->textureSpecularity, AI_MATKEY_TEXTURE_SHININESS(0));
676             if (pCurrentMaterial->clamp[ObjFile::Material::TextureSpecularityType])
677             {
678                 addTextureMappingModeProperty(mat, aiTextureType_SHININESS);
679             }
680         }
681 
682         // Store material property info in material array in scene
683         pScene->mMaterials[ pScene->mNumMaterials ] = mat;
684         pScene->mNumMaterials++;
685     }
686 
687     // Test number of created materials.
688     ai_assert( pScene->mNumMaterials == numMaterials );
689 }
690 
691 // ------------------------------------------------------------------------------------------------
692 //  Appends this node to the parent node
appendChildToParentNode(aiNode * pParent,aiNode * pChild)693 void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild)
694 {
695     // Checking preconditions
696     ai_assert( NULL != pParent );
697     ai_assert( NULL != pChild );
698 
699     // Assign parent to child
700     pChild->mParent = pParent;
701 
702     // If already children was assigned to the parent node, store them in a
703     std::vector<aiNode*> temp;
704     if (pParent->mChildren != NULL)
705     {
706         ai_assert( 0 != pParent->mNumChildren );
707         for (size_t index = 0; index < pParent->mNumChildren; index++)
708         {
709             temp.push_back(pParent->mChildren [ index ] );
710         }
711         delete [] pParent->mChildren;
712     }
713 
714     // Copy node instances into parent node
715     pParent->mNumChildren++;
716     pParent->mChildren = new aiNode*[ pParent->mNumChildren ];
717     for (size_t index = 0; index < pParent->mNumChildren-1; index++)
718     {
719         pParent->mChildren[ index ] = temp [ index ];
720     }
721     pParent->mChildren[ pParent->mNumChildren-1 ] = pChild;
722 }
723 
724 // ------------------------------------------------------------------------------------------------
725 
726 }   // Namespace Assimp
727 
728 #endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
729