1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions 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 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF 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 | |
57 | static 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 | |
70 | static const unsigned int ObjMinSize = 16; |
71 | |
72 | namespace Assimp { |
73 | |
74 | using namespace std; |
75 | |
76 | // ------------------------------------------------------------------------------------------------ |
77 | // Default constructor |
78 | ObjFileImporter::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. |
89 | ObjFileImporter::~ObjFileImporter() |
90 | { |
91 | delete m_pRootObject; |
92 | m_pRootObject = NULL; |
93 | } |
94 | |
95 | // ------------------------------------------------------------------------------------------------ |
96 | // Returns true, if file is an obj file. |
97 | bool 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 | // ------------------------------------------------------------------------------------------------ |
111 | const aiImporterDesc* ObjFileImporter::GetInfo () const |
112 | { |
113 | return &desc; |
114 | } |
115 | |
116 | // ------------------------------------------------------------------------------------------------ |
117 | // Obj-file import implementation |
118 | void 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 |
202 | void 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 |
243 | aiNode *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 |
302 | aiMesh *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 |
392 | void 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 |
527 | void 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 |
547 | void 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 |
558 | void 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 |
734 | void 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 | |