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