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