1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5
6 Copyright (c) 2006-2020, 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 #ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
43
44 #include "ObjFileImporter.h"
45 #include "ObjFileData.h"
46 #include "ObjFileParser.h"
47 #include <assimp/DefaultIOSystem.h>
48 #include <assimp/IOStreamBuffer.h>
49 #include <assimp/ai_assert.h>
50 #include <assimp/importerdesc.h>
51 #include <assimp/scene.h>
52 #include <assimp/DefaultLogger.hpp>
53 #include <assimp/Importer.hpp>
54 #include <memory>
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(nullptr),
80 m_strAbsPath(std::string(1, DefaultIOSystem().getOsSeparator())) {}
81
82 // ------------------------------------------------------------------------------------------------
83 // Destructor.
~ObjFileImporter()84 ObjFileImporter::~ObjFileImporter() {
85 delete m_pRootObject;
86 m_pRootObject = nullptr;
87 }
88
89 // ------------------------------------------------------------------------------------------------
90 // Returns true, if file is an obj file.
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const91 bool ObjFileImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
92 if (!checkSig) {
93 //Check File Extension
94 return SimpleExtensionCheck(pFile, "obj");
95 } else {
96 // Check file Header
97 static const char *pTokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " };
98 return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 9, 200, false, true);
99 }
100 }
101
102 // ------------------------------------------------------------------------------------------------
GetInfo() const103 const aiImporterDesc *ObjFileImporter::GetInfo() const {
104 return &desc;
105 }
106
107 // ------------------------------------------------------------------------------------------------
108 // Obj-file import implementation
InternReadFile(const std::string & file,aiScene * pScene,IOSystem * pIOHandler)109 void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
110 // Read file into memory
111 static const std::string mode = "rb";
112 auto streamCloser = [&](IOStream *pStream) {
113 pIOHandler->Close(pStream);
114 };
115 std::unique_ptr<IOStream, decltype(streamCloser)> fileStream(pIOHandler->Open(file, mode), streamCloser);
116 if (!fileStream.get()) {
117 throw DeadlyImportError("Failed to open file ", file, ".");
118 }
119
120 // Get the file-size and validate it, throwing an exception when fails
121 size_t fileSize = fileStream->FileSize();
122 if (fileSize < ObjMinSize) {
123 throw DeadlyImportError("OBJ-file is too small.");
124 }
125
126 IOStreamBuffer<char> streamedBuffer;
127 streamedBuffer.open(fileStream.get());
128
129 // Allocate buffer and read file into it
130 //TextFileToBuffer( fileStream.get(),m_Buffer);
131
132 // Get the model name
133 std::string modelName, folderName;
134 std::string::size_type pos = file.find_last_of("\\/");
135 if (pos != std::string::npos) {
136 modelName = file.substr(pos + 1, file.size() - pos - 1);
137 folderName = file.substr(0, pos);
138 if (!folderName.empty()) {
139 pIOHandler->PushDirectory(folderName);
140 }
141 } else {
142 modelName = file;
143 }
144
145 // parse the file into a temporary representation
146 ObjFileParser parser(streamedBuffer, modelName, pIOHandler, m_progress, file);
147
148 // And create the proper return structures out of it
149 CreateDataFromImport(parser.GetModel(), pScene);
150
151 streamedBuffer.close();
152
153 // Clean up allocated storage for the next import
154 m_Buffer.clear();
155
156 // Pop directory stack
157 if (pIOHandler->StackSize() > 0) {
158 pIOHandler->PopDirectory();
159 }
160 }
161
162 // ------------------------------------------------------------------------------------------------
163 // Create the data from parsed obj-file
CreateDataFromImport(const ObjFile::Model * pModel,aiScene * pScene)164 void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene *pScene) {
165 if (nullptr == pModel) {
166 return;
167 }
168
169 // Create the root node of the scene
170 pScene->mRootNode = new aiNode;
171 if (!pModel->m_ModelName.empty()) {
172 // Set the name of the scene
173 pScene->mRootNode->mName.Set(pModel->m_ModelName);
174 } else {
175 // This is a fatal error, so break down the application
176 ai_assert(false);
177 }
178
179 if (!pModel->m_Objects.empty()) {
180
181 unsigned int meshCount = 0;
182 unsigned int childCount = 0;
183
184 for (auto object : pModel->m_Objects) {
185 if (object) {
186 ++childCount;
187 meshCount += (unsigned int)object->m_Meshes.size();
188 }
189 }
190
191 // Allocate space for the child nodes on the root node
192 pScene->mRootNode->mChildren = new aiNode *[childCount];
193
194 // Create nodes for the whole scene
195 std::vector<aiMesh *> MeshArray;
196 MeshArray.reserve(meshCount);
197 for (size_t index = 0; index < pModel->m_Objects.size(); ++index) {
198 createNodes(pModel, pModel->m_Objects[index], pScene->mRootNode, pScene, MeshArray);
199 }
200
201 ai_assert(pScene->mRootNode->mNumChildren == childCount);
202
203 // Create mesh pointer buffer for this scene
204 if (pScene->mNumMeshes > 0) {
205 pScene->mMeshes = new aiMesh *[MeshArray.size()];
206 for (size_t index = 0; index < MeshArray.size(); ++index) {
207 pScene->mMeshes[index] = MeshArray[index];
208 }
209 }
210
211 // Create all materials
212 createMaterials(pModel, pScene);
213 } else {
214 if (pModel->m_Vertices.empty()) {
215 return;
216 }
217
218 std::unique_ptr<aiMesh> mesh(new aiMesh);
219 mesh->mPrimitiveTypes = aiPrimitiveType_POINT;
220 unsigned int n = (unsigned int)pModel->m_Vertices.size();
221 mesh->mNumVertices = n;
222
223 mesh->mVertices = new aiVector3D[n];
224 memcpy(mesh->mVertices, pModel->m_Vertices.data(), n * sizeof(aiVector3D));
225
226 if (!pModel->m_Normals.empty()) {
227 mesh->mNormals = new aiVector3D[n];
228 if (pModel->m_Normals.size() < n) {
229 throw DeadlyImportError("OBJ: vertex normal index out of range");
230 }
231 memcpy(mesh->mNormals, pModel->m_Normals.data(), n * sizeof(aiVector3D));
232 }
233
234 if (!pModel->m_VertexColors.empty()) {
235 mesh->mColors[0] = new aiColor4D[mesh->mNumVertices];
236 for (unsigned int i = 0; i < n; ++i) {
237 if (i < pModel->m_VertexColors.size()) {
238 const aiVector3D &color = pModel->m_VertexColors[i];
239 mesh->mColors[0][i] = aiColor4D(color.x, color.y, color.z, 1.0);
240 } else {
241 throw DeadlyImportError("OBJ: vertex color index out of range");
242 }
243 }
244 }
245
246 pScene->mRootNode->mNumMeshes = 1;
247 pScene->mRootNode->mMeshes = new unsigned int[1];
248 pScene->mRootNode->mMeshes[0] = 0;
249 pScene->mMeshes = new aiMesh *[1];
250 pScene->mNumMeshes = 1;
251 pScene->mMeshes[0] = mesh.release();
252 }
253 }
254
255 // ------------------------------------------------------------------------------------------------
256 // Creates all nodes of the model
createNodes(const ObjFile::Model * pModel,const ObjFile::Object * pObject,aiNode * pParent,aiScene * pScene,std::vector<aiMesh * > & MeshArray)257 aiNode *ObjFileImporter::createNodes(const ObjFile::Model *pModel, const ObjFile::Object *pObject,
258 aiNode *pParent, aiScene *pScene,
259 std::vector<aiMesh *> &MeshArray) {
260 ai_assert(nullptr != pModel);
261 if (nullptr == pObject) {
262 return nullptr;
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(nullptr != 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(nullptr != pModel);
317
318 if (nullptr == pData) {
319 return nullptr;
320 }
321
322 // Create faces
323 ObjFile::Mesh *pObjMesh = pModel->m_Meshes[meshIndex];
324 if (!pObjMesh) {
325 return nullptr;
326 }
327
328 if (pObjMesh->m_Faces.empty()) {
329 return nullptr;
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 ObjFile::Face *const inp = pObjMesh->m_Faces[index];
339 ai_assert(nullptr != inp);
340
341 if (inp->m_PrimitiveType == aiPrimitiveType_LINE) {
342 pMesh->mNumFaces += static_cast<unsigned int>(inp->m_vertices.size() - 1);
343 pMesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
344 } else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) {
345 pMesh->mNumFaces += static_cast<unsigned int>(inp->m_vertices.size());
346 pMesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
347 } else {
348 ++pMesh->mNumFaces;
349 if (inp->m_vertices.size() > 3) {
350 pMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
351 } else {
352 pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
353 }
354 }
355 }
356
357 unsigned int uiIdxCount(0u);
358 if (pMesh->mNumFaces > 0) {
359 pMesh->mFaces = new aiFace[pMesh->mNumFaces];
360 if (pObjMesh->m_uiMaterialIndex != ObjFile::Mesh::NoMaterial) {
361 pMesh->mMaterialIndex = pObjMesh->m_uiMaterialIndex;
362 }
363
364 unsigned int outIndex(0);
365
366 // Copy all data from all stored meshes
367 for (auto &face : pObjMesh->m_Faces) {
368 ObjFile::Face *const inp = face;
369 if (inp->m_PrimitiveType == aiPrimitiveType_LINE) {
370 for (size_t i = 0; i < inp->m_vertices.size() - 1; ++i) {
371 aiFace &f = pMesh->mFaces[outIndex++];
372 uiIdxCount += f.mNumIndices = 2;
373 f.mIndices = new unsigned int[2];
374 }
375 continue;
376 } else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) {
377 for (size_t i = 0; i < inp->m_vertices.size(); ++i) {
378 aiFace &f = pMesh->mFaces[outIndex++];
379 uiIdxCount += f.mNumIndices = 1;
380 f.mIndices = new unsigned int[1];
381 }
382 continue;
383 }
384
385 aiFace *pFace = &pMesh->mFaces[outIndex++];
386 const unsigned int uiNumIndices = (unsigned int)face->m_vertices.size();
387 uiIdxCount += pFace->mNumIndices = (unsigned int)uiNumIndices;
388 if (pFace->mNumIndices > 0) {
389 pFace->mIndices = new unsigned int[uiNumIndices];
390 }
391 }
392 }
393
394 // Create mesh vertices
395 createVertexArray(pModel, pData, meshIndex, pMesh.get(), uiIdxCount);
396
397 return pMesh.release();
398 }
399
400 // ------------------------------------------------------------------------------------------------
401 // Creates a vertex array
createVertexArray(const ObjFile::Model * pModel,const ObjFile::Object * pCurrentObject,unsigned int uiMeshIndex,aiMesh * pMesh,unsigned int numIndices)402 void ObjFileImporter::createVertexArray(const ObjFile::Model *pModel,
403 const ObjFile::Object *pCurrentObject,
404 unsigned int uiMeshIndex,
405 aiMesh *pMesh,
406 unsigned int numIndices) {
407 // Checking preconditions
408 ai_assert(nullptr != pCurrentObject);
409
410 // Break, if no faces are stored in object
411 if (pCurrentObject->m_Meshes.empty())
412 return;
413
414 // Get current mesh
415 ObjFile::Mesh *pObjMesh = pModel->m_Meshes[uiMeshIndex];
416 if (nullptr == pObjMesh || pObjMesh->m_uiNumIndices < 1) {
417 return;
418 }
419
420 // Copy vertices of this mesh instance
421 pMesh->mNumVertices = numIndices;
422 if (pMesh->mNumVertices == 0) {
423 throw DeadlyImportError("OBJ: no vertices");
424 } else if (pMesh->mNumVertices > AI_MAX_VERTICES) {
425 throw DeadlyImportError("OBJ: Too many vertices");
426 }
427 pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
428
429 // Allocate buffer for normal vectors
430 if (!pModel->m_Normals.empty() && pObjMesh->m_hasNormals)
431 pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
432
433 // Allocate buffer for vertex-color vectors
434 if (!pModel->m_VertexColors.empty())
435 pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
436
437 // Allocate buffer for texture coordinates
438 if (!pModel->m_TextureCoord.empty() && pObjMesh->m_uiUVCoordinates[0]) {
439 pMesh->mNumUVComponents[0] = pModel->m_TextureCoordDim;
440 pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices];
441 }
442
443 // Copy vertices, normals and textures into aiMesh instance
444 bool normalsok = true, uvok = true;
445 unsigned int newIndex = 0, outIndex = 0;
446 for (auto sourceFace : pObjMesh->m_Faces) {
447 // Copy all index arrays
448 for (size_t vertexIndex = 0, outVertexIndex = 0; vertexIndex < sourceFace->m_vertices.size(); vertexIndex++) {
449 const unsigned int vertex = sourceFace->m_vertices.at(vertexIndex);
450 if (vertex >= pModel->m_Vertices.size()) {
451 throw DeadlyImportError("OBJ: vertex index out of range");
452 }
453
454 if (pMesh->mNumVertices <= newIndex) {
455 throw DeadlyImportError("OBJ: bad vertex index");
456 }
457
458 pMesh->mVertices[newIndex] = pModel->m_Vertices[vertex];
459
460 // Copy all normals
461 if (normalsok && !pModel->m_Normals.empty() && vertexIndex < sourceFace->m_normals.size()) {
462 const unsigned int normal = sourceFace->m_normals.at(vertexIndex);
463 if (normal >= pModel->m_Normals.size()) {
464 normalsok = false;
465 } else {
466 pMesh->mNormals[newIndex] = pModel->m_Normals[normal];
467 }
468 }
469
470 // Copy all vertex colors
471 if (vertex < pModel->m_VertexColors.size()) {
472 const aiVector3D &color = pModel->m_VertexColors[vertex];
473 pMesh->mColors[0][newIndex] = aiColor4D(color.x, color.y, color.z, 1.0);
474 }
475
476 // Copy all texture coordinates
477 if (uvok && !pModel->m_TextureCoord.empty() && vertexIndex < sourceFace->m_texturCoords.size()) {
478 const unsigned int tex = sourceFace->m_texturCoords.at(vertexIndex);
479
480 if (tex >= pModel->m_TextureCoord.size()) {
481 uvok = false;
482 } else {
483 const aiVector3D &coord3d = pModel->m_TextureCoord[tex];
484 pMesh->mTextureCoords[0][newIndex] = aiVector3D(coord3d.x, coord3d.y, coord3d.z);
485 }
486 }
487
488 // Get destination face
489 aiFace *pDestFace = &pMesh->mFaces[outIndex];
490
491 const bool last = (vertexIndex == sourceFace->m_vertices.size() - 1);
492 if (sourceFace->m_PrimitiveType != aiPrimitiveType_LINE || !last) {
493 pDestFace->mIndices[outVertexIndex] = newIndex;
494 outVertexIndex++;
495 }
496
497 if (sourceFace->m_PrimitiveType == aiPrimitiveType_POINT) {
498 outIndex++;
499 outVertexIndex = 0;
500 } else if (sourceFace->m_PrimitiveType == aiPrimitiveType_LINE) {
501 outVertexIndex = 0;
502
503 if (!last)
504 outIndex++;
505
506 if (vertexIndex) {
507 if (!last) {
508 pMesh->mVertices[newIndex + 1] = pMesh->mVertices[newIndex];
509 if (!sourceFace->m_normals.empty() && !pModel->m_Normals.empty()) {
510 pMesh->mNormals[newIndex + 1] = pMesh->mNormals[newIndex];
511 }
512 if (!pModel->m_TextureCoord.empty()) {
513 for (size_t i = 0; i < pMesh->GetNumUVChannels(); i++) {
514 pMesh->mTextureCoords[i][newIndex + 1] = pMesh->mTextureCoords[i][newIndex];
515 }
516 }
517 ++newIndex;
518 }
519
520 pDestFace[-1].mIndices[1] = newIndex;
521 }
522 } else if (last) {
523 outIndex++;
524 }
525 ++newIndex;
526 }
527 }
528
529 if (!normalsok) {
530 delete[] pMesh->mNormals;
531 pMesh->mNormals = nullptr;
532 }
533
534 if (!uvok) {
535 delete[] pMesh->mTextureCoords[0];
536 pMesh->mTextureCoords[0] = nullptr;
537 }
538 }
539
540 // ------------------------------------------------------------------------------------------------
541 // Counts all stored meshes
countObjects(const std::vector<ObjFile::Object * > & rObjects,int & iNumMeshes)542 void ObjFileImporter::countObjects(const std::vector<ObjFile::Object *> &rObjects, int &iNumMeshes) {
543 iNumMeshes = 0;
544 if (rObjects.empty())
545 return;
546
547 iNumMeshes += static_cast<unsigned int>(rObjects.size());
548 for (auto object : rObjects) {
549 if (!object->m_SubObjects.empty()) {
550 countObjects(object->m_SubObjects, iNumMeshes);
551 }
552 }
553 }
554
555 // ------------------------------------------------------------------------------------------------
556 // Add clamp mode property to material if necessary
addTextureMappingModeProperty(aiMaterial * mat,aiTextureType type,int clampMode,int index)557 void ObjFileImporter::addTextureMappingModeProperty(aiMaterial *mat, aiTextureType type, int clampMode, int index) {
558 if (nullptr == mat) {
559 return;
560 }
561
562 mat->AddProperty<int>(&clampMode, 1, AI_MATKEY_MAPPINGMODE_U(type, index));
563 mat->AddProperty<int>(&clampMode, 1, AI_MATKEY_MAPPINGMODE_V(type, index));
564 }
565
566 // ------------------------------------------------------------------------------------------------
567 // Creates the material
createMaterials(const ObjFile::Model * pModel,aiScene * pScene)568 void ObjFileImporter::createMaterials(const ObjFile::Model *pModel, aiScene *pScene) {
569 if (nullptr == pScene) {
570 return;
571 }
572
573 const unsigned int numMaterials = (unsigned int)pModel->m_MaterialLib.size();
574 pScene->mNumMaterials = 0;
575 if (pModel->m_MaterialLib.empty()) {
576 ASSIMP_LOG_DEBUG("OBJ: no materials specified");
577 return;
578 }
579
580 pScene->mMaterials = new aiMaterial *[numMaterials];
581 for (unsigned int matIndex = 0; matIndex < numMaterials; matIndex++) {
582 // Store material name
583 std::map<std::string, ObjFile::Material *>::const_iterator it;
584 it = pModel->m_MaterialMap.find(pModel->m_MaterialLib[matIndex]);
585
586 // No material found, use the default material
587 if (pModel->m_MaterialMap.end() == it)
588 continue;
589
590 aiMaterial *mat = new aiMaterial;
591 ObjFile::Material *pCurrentMaterial = (*it).second;
592 mat->AddProperty(&pCurrentMaterial->MaterialName, AI_MATKEY_NAME);
593
594 // convert illumination model
595 int sm = 0;
596 switch (pCurrentMaterial->illumination_model) {
597 case 0:
598 sm = aiShadingMode_NoShading;
599 break;
600 case 1:
601 sm = aiShadingMode_Gouraud;
602 break;
603 case 2:
604 sm = aiShadingMode_Phong;
605 break;
606 default:
607 sm = aiShadingMode_Gouraud;
608 ASSIMP_LOG_ERROR("OBJ: unexpected illumination model (0-2 recognized)");
609 }
610
611 mat->AddProperty<int>(&sm, 1, AI_MATKEY_SHADING_MODEL);
612
613 // Adding material colors
614 mat->AddProperty(&pCurrentMaterial->ambient, 1, AI_MATKEY_COLOR_AMBIENT);
615 mat->AddProperty(&pCurrentMaterial->diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
616 mat->AddProperty(&pCurrentMaterial->specular, 1, AI_MATKEY_COLOR_SPECULAR);
617 mat->AddProperty(&pCurrentMaterial->emissive, 1, AI_MATKEY_COLOR_EMISSIVE);
618 mat->AddProperty(&pCurrentMaterial->shineness, 1, AI_MATKEY_SHININESS);
619 mat->AddProperty(&pCurrentMaterial->alpha, 1, AI_MATKEY_OPACITY);
620 mat->AddProperty(&pCurrentMaterial->transparent, 1, AI_MATKEY_COLOR_TRANSPARENT);
621
622 // Adding refraction index
623 mat->AddProperty(&pCurrentMaterial->ior, 1, AI_MATKEY_REFRACTI);
624
625 // Adding textures
626 const int uvwIndex = 0;
627
628 if (0 != pCurrentMaterial->texture.length) {
629 mat->AddProperty(&pCurrentMaterial->texture, AI_MATKEY_TEXTURE_DIFFUSE(0));
630 mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_DIFFUSE(0));
631 if (pCurrentMaterial->clamp[ObjFile::Material::TextureDiffuseType]) {
632 addTextureMappingModeProperty(mat, aiTextureType_DIFFUSE);
633 }
634 }
635
636 if (0 != pCurrentMaterial->textureAmbient.length) {
637 mat->AddProperty(&pCurrentMaterial->textureAmbient, AI_MATKEY_TEXTURE_AMBIENT(0));
638 mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_AMBIENT(0));
639 if (pCurrentMaterial->clamp[ObjFile::Material::TextureAmbientType]) {
640 addTextureMappingModeProperty(mat, aiTextureType_AMBIENT);
641 }
642 }
643
644 if (0 != pCurrentMaterial->textureEmissive.length) {
645 mat->AddProperty(&pCurrentMaterial->textureEmissive, AI_MATKEY_TEXTURE_EMISSIVE(0));
646 mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_EMISSIVE(0));
647 }
648
649 if (0 != pCurrentMaterial->textureSpecular.length) {
650 mat->AddProperty(&pCurrentMaterial->textureSpecular, AI_MATKEY_TEXTURE_SPECULAR(0));
651 mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_SPECULAR(0));
652 if (pCurrentMaterial->clamp[ObjFile::Material::TextureSpecularType]) {
653 addTextureMappingModeProperty(mat, aiTextureType_SPECULAR);
654 }
655 }
656
657 if (0 != pCurrentMaterial->textureBump.length) {
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 addTextureMappingModeProperty(mat, aiTextureType_HEIGHT);
662 }
663 }
664
665 if (0 != pCurrentMaterial->textureNormal.length) {
666 mat->AddProperty(&pCurrentMaterial->textureNormal, AI_MATKEY_TEXTURE_NORMALS(0));
667 mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_NORMALS(0));
668 if (pCurrentMaterial->clamp[ObjFile::Material::TextureNormalType]) {
669 addTextureMappingModeProperty(mat, aiTextureType_NORMALS);
670 }
671 }
672
673 if (0 != pCurrentMaterial->textureReflection[0].length) {
674 ObjFile::Material::TextureType type = 0 != pCurrentMaterial->textureReflection[1].length ?
675 ObjFile::Material::TextureReflectionCubeTopType :
676 ObjFile::Material::TextureReflectionSphereType;
677
678 unsigned count = type == ObjFile::Material::TextureReflectionSphereType ? 1 : 6;
679 for (unsigned i = 0; i < count; i++) {
680 mat->AddProperty(&pCurrentMaterial->textureReflection[i], AI_MATKEY_TEXTURE_REFLECTION(i));
681 mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_REFLECTION(i));
682
683 if (pCurrentMaterial->clamp[type])
684 addTextureMappingModeProperty(mat, aiTextureType_REFLECTION, 1, i);
685 }
686 }
687
688 if (0 != pCurrentMaterial->textureDisp.length) {
689 mat->AddProperty(&pCurrentMaterial->textureDisp, AI_MATKEY_TEXTURE_DISPLACEMENT(0));
690 mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_DISPLACEMENT(0));
691 if (pCurrentMaterial->clamp[ObjFile::Material::TextureDispType]) {
692 addTextureMappingModeProperty(mat, aiTextureType_DISPLACEMENT);
693 }
694 }
695
696 if (0 != pCurrentMaterial->textureOpacity.length) {
697 mat->AddProperty(&pCurrentMaterial->textureOpacity, AI_MATKEY_TEXTURE_OPACITY(0));
698 mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_OPACITY(0));
699 if (pCurrentMaterial->clamp[ObjFile::Material::TextureOpacityType]) {
700 addTextureMappingModeProperty(mat, aiTextureType_OPACITY);
701 }
702 }
703
704 if (0 != pCurrentMaterial->textureSpecularity.length) {
705 mat->AddProperty(&pCurrentMaterial->textureSpecularity, AI_MATKEY_TEXTURE_SHININESS(0));
706 mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_SHININESS(0));
707 if (pCurrentMaterial->clamp[ObjFile::Material::TextureSpecularityType]) {
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 // Checking preconditions
725 ai_assert(nullptr != pParent);
726 ai_assert(nullptr != pChild);
727
728 // Assign parent to child
729 pChild->mParent = pParent;
730
731 // Copy node instances into parent node
732 pParent->mNumChildren++;
733 pParent->mChildren[pParent->mNumChildren - 1] = pChild;
734 }
735
736 // ------------------------------------------------------------------------------------------------
737
738 } // Namespace Assimp
739
740 #endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
741