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