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