vrshoot

view libs/assimp/ObjFileImporter.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
line source
1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
6 Copyright (c) 2006-2012, assimp team
8 All rights reserved.
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:
14 * Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
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.
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.
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 */
42 #include "AssimpPCH.h"
43 #ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
45 #include "DefaultIOSystem.h"
46 #include "ObjFileImporter.h"
47 #include "ObjFileParser.h"
48 #include "ObjFileData.h"
50 static const aiImporterDesc desc = {
51 "Wavefront Object Importer",
52 "",
53 "",
54 "surfaces not supported",
55 aiImporterFlags_SupportTextFlavour,
56 0,
57 0,
58 0,
59 0,
60 "obj"
61 };
64 namespace Assimp {
66 using namespace std;
68 // ------------------------------------------------------------------------------------------------
69 // Default constructor
70 ObjFileImporter::ObjFileImporter() :
71 m_Buffer(),
72 m_pRootObject( NULL ),
73 m_strAbsPath( "" )
74 {
75 DefaultIOSystem io;
76 m_strAbsPath = io.getOsSeparator();
77 }
79 // ------------------------------------------------------------------------------------------------
80 // Destructor.
81 ObjFileImporter::~ObjFileImporter()
82 {
83 // Release root object instance
84 if (NULL != m_pRootObject)
85 {
86 delete m_pRootObject;
87 m_pRootObject = NULL;
88 }
89 }
91 // ------------------------------------------------------------------------------------------------
92 // Returns true, if file is an obj file.
93 bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler , bool checkSig ) const
94 {
95 if(!checkSig) //Check File Extension
96 {
97 return SimpleExtensionCheck(pFile,"obj");
98 }
99 else //Check file Header
100 {
101 static const char *pTokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " };
102 return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 9 );
103 }
104 }
106 // ------------------------------------------------------------------------------------------------
107 const aiImporterDesc* ObjFileImporter::GetInfo () const
108 {
109 return &desc;
110 }
112 // ------------------------------------------------------------------------------------------------
113 // Obj-file import implementation
114 void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
115 {
116 DefaultIOSystem io;
118 // Read file into memory
119 const std::string mode = "rb";
120 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, mode));
121 if (NULL == file.get())
122 throw DeadlyImportError( "Failed to open file " + pFile + ".");
124 // Get the file-size and validate it, throwing an exception when fails
125 size_t fileSize = file->FileSize();
126 if( fileSize < 16)
127 throw DeadlyImportError( "OBJ-file is too small.");
129 // Allocate buffer and read file into it
130 TextFileToBuffer(file.get(),m_Buffer);
132 // Get the model name
133 std::string strModelName;
134 std::string::size_type pos = pFile.find_last_of( "\\/" );
135 if ( pos != std::string::npos )
136 {
137 strModelName = pFile.substr(pos+1, pFile.size() - pos - 1);
138 }
139 else
140 {
141 strModelName = pFile;
142 }
144 // parse the file into a temporary representation
145 ObjFileParser parser(m_Buffer, strModelName, pIOHandler);
147 // And create the proper return structures out of it
148 CreateDataFromImport(parser.GetModel(), pScene);
150 // Clean up allocated storage for the next import
151 m_Buffer.clear();
152 }
154 // ------------------------------------------------------------------------------------------------
155 // Create the data from parsed obj-file
156 void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene)
157 {
158 if (0L == pModel)
159 return;
161 // Create the root node of the scene
162 pScene->mRootNode = new aiNode;
163 if ( !pModel->m_ModelName.empty() )
164 {
165 // Set the name of the scene
166 pScene->mRootNode->mName.Set(pModel->m_ModelName);
167 }
168 else
169 {
170 // This is an error, so break down the application
171 ai_assert(false);
172 }
174 // Create nodes for the whole scene
175 std::vector<aiMesh*> MeshArray;
176 for (size_t index = 0; index < pModel->m_Objects.size(); index++)
177 {
178 createNodes(pModel, pModel->m_Objects[ index ], index, pScene->mRootNode, pScene, MeshArray);
179 }
181 // Create mesh pointer buffer for this scene
182 if (pScene->mNumMeshes > 0)
183 {
184 pScene->mMeshes = new aiMesh*[ MeshArray.size() ];
185 for (size_t index =0; index < MeshArray.size(); index++)
186 {
187 pScene->mMeshes [ index ] = MeshArray[ index ];
188 }
189 }
191 // Create all materials
192 createMaterials( pModel, pScene );
193 }
195 // ------------------------------------------------------------------------------------------------
196 // Creates all nodes of the model
197 aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pObject,
198 unsigned int /*uiMeshIndex*/,
199 aiNode *pParent, aiScene* pScene,
200 std::vector<aiMesh*> &MeshArray )
201 {
202 ai_assert( NULL != pModel );
203 if ( NULL == pObject )
204 return NULL;
206 // Store older mesh size to be able to computes mesh offsets for new mesh instances
207 const size_t oldMeshSize = MeshArray.size();
208 aiNode *pNode = new aiNode;
210 pNode->mName = pObject->m_strObjName;
212 // If we have a parent node, store it
213 if (pParent != NULL)
214 appendChildToParentNode(pParent, pNode);
216 for ( unsigned int i=0; i< pObject->m_Meshes.size(); i++ )
217 {
218 unsigned int meshId = pObject->m_Meshes[ i ];
219 aiMesh *pMesh = new aiMesh;
220 createTopology( pModel, pObject, meshId, pMesh );
221 if ( pMesh->mNumVertices > 0 )
222 {
223 MeshArray.push_back( pMesh );
224 }
225 else
226 {
227 delete pMesh;
228 }
229 }
231 // Create all nodes from the sub-objects stored in the current object
232 if ( !pObject->m_SubObjects.empty() )
233 {
234 size_t numChilds = pObject->m_SubObjects.size();
235 pNode->mNumChildren = static_cast<unsigned int>( numChilds );
236 pNode->mChildren = new aiNode*[ numChilds ];
237 pNode->mNumMeshes = 1;
238 pNode->mMeshes = new unsigned int[ 1 ];
239 }
241 // Set mesh instances into scene- and node-instances
242 const size_t meshSizeDiff = MeshArray.size()- oldMeshSize;
243 if ( meshSizeDiff > 0 )
244 {
245 pNode->mMeshes = new unsigned int[ meshSizeDiff ];
246 pNode->mNumMeshes = static_cast<unsigned int>( meshSizeDiff );
247 size_t index = 0;
248 for (size_t i = oldMeshSize; i < MeshArray.size(); i++)
249 {
250 pNode->mMeshes[ index ] = pScene->mNumMeshes;
251 pScene->mNumMeshes++;
252 index++;
253 }
254 }
256 return pNode;
257 }
259 // ------------------------------------------------------------------------------------------------
260 // Create topology data
261 void ObjFileImporter::createTopology(const ObjFile::Model* pModel,
262 const ObjFile::Object* pData,
263 unsigned int uiMeshIndex,
264 aiMesh* pMesh )
265 {
266 // Checking preconditions
267 ai_assert( NULL != pModel );
268 if (NULL == pData)
269 return;
271 // Create faces
272 ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ];
273 ai_assert( NULL != pObjMesh );
275 pMesh->mNumFaces = 0;
276 for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++)
277 {
278 ObjFile::Face* const inp = pObjMesh->m_Faces[ index ];
279 if (inp->m_PrimitiveType == aiPrimitiveType_LINE) {
280 pMesh->mNumFaces += inp->m_pVertices->size() - 1;
281 }
282 else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) {
283 pMesh->mNumFaces += inp->m_pVertices->size();
284 }
285 else {
286 ++pMesh->mNumFaces;
287 }
288 }
290 unsigned int uiIdxCount = 0u;
291 if ( pMesh->mNumFaces > 0 )
292 {
293 pMesh->mFaces = new aiFace[ pMesh->mNumFaces ];
294 if ( pObjMesh->m_uiMaterialIndex != ObjFile::Mesh::NoMaterial )
295 {
296 pMesh->mMaterialIndex = pObjMesh->m_uiMaterialIndex;
297 }
299 unsigned int outIndex = 0;
301 // Copy all data from all stored meshes
302 for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++)
303 {
304 ObjFile::Face* const inp = pObjMesh->m_Faces[ index ];
305 if (inp->m_PrimitiveType == aiPrimitiveType_LINE) {
306 for(size_t i = 0; i < inp->m_pVertices->size() - 1; ++i) {
307 aiFace& f = pMesh->mFaces[ outIndex++ ];
308 uiIdxCount += f.mNumIndices = 2;
309 f.mIndices = new unsigned int[2];
310 }
311 continue;
312 }
313 else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) {
314 for(size_t i = 0; i < inp->m_pVertices->size(); ++i) {
315 aiFace& f = pMesh->mFaces[ outIndex++ ];
316 uiIdxCount += f.mNumIndices = 1;
317 f.mIndices = new unsigned int[1];
318 }
319 continue;
320 }
322 aiFace *pFace = &pMesh->mFaces[ outIndex++ ];
323 const unsigned int uiNumIndices = (unsigned int) pObjMesh->m_Faces[ index ]->m_pVertices->size();
324 uiIdxCount += pFace->mNumIndices = (unsigned int) uiNumIndices;
325 if (pFace->mNumIndices > 0) {
326 pFace->mIndices = new unsigned int[ uiNumIndices ];
327 }
328 }
329 }
331 // Create mesh vertices
332 createVertexArray(pModel, pData, uiMeshIndex, pMesh, uiIdxCount);
333 }
335 // ------------------------------------------------------------------------------------------------
336 // Creates a vertex array
337 void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
338 const ObjFile::Object* pCurrentObject,
339 unsigned int uiMeshIndex,
340 aiMesh* pMesh,
341 unsigned int uiIdxCount)
342 {
343 // Checking preconditions
344 ai_assert( NULL != pCurrentObject );
346 // Break, if no faces are stored in object
347 if ( pCurrentObject->m_Meshes.empty() )
348 return;
350 // Get current mesh
351 ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ];
352 if ( NULL == pObjMesh || pObjMesh->m_uiNumIndices < 1)
353 return;
355 // Copy vertices of this mesh instance
356 pMesh->mNumVertices = uiIdxCount;
357 pMesh->mVertices = new aiVector3D[ pMesh->mNumVertices ];
359 // Allocate buffer for normal vectors
360 if ( !pModel->m_Normals.empty() && pObjMesh->m_hasNormals )
361 pMesh->mNormals = new aiVector3D[ pMesh->mNumVertices ];
363 // Allocate buffer for texture coordinates
364 if ( !pModel->m_TextureCoord.empty() && pObjMesh->m_uiUVCoordinates[0] )
365 {
366 pMesh->mNumUVComponents[ 0 ] = 2;
367 pMesh->mTextureCoords[ 0 ] = new aiVector3D[ pMesh->mNumVertices ];
368 }
370 // Copy vertices, normals and textures into aiMesh instance
371 unsigned int newIndex = 0, outIndex = 0;
372 for ( size_t index=0; index < pObjMesh->m_Faces.size(); index++ )
373 {
374 // Get source face
375 ObjFile::Face *pSourceFace = pObjMesh->m_Faces[ index ];
377 // Copy all index arrays
378 for ( size_t vertexIndex = 0, outVertexIndex = 0; vertexIndex < pSourceFace->m_pVertices->size(); vertexIndex++ )
379 {
380 const unsigned int vertex = pSourceFace->m_pVertices->at( vertexIndex );
381 if ( vertex >= pModel->m_Vertices.size() )
382 throw DeadlyImportError( "OBJ: vertex index out of range" );
384 pMesh->mVertices[ newIndex ] = pModel->m_Vertices[ vertex ];
386 // Copy all normals
387 if ( !pSourceFace->m_pNormals->empty() && !pModel->m_Normals.empty())
388 {
389 const unsigned int normal = pSourceFace->m_pNormals->at( vertexIndex );
390 if ( normal >= pModel->m_Normals.size() )
391 throw DeadlyImportError("OBJ: vertex normal index out of range");
393 pMesh->mNormals[ newIndex ] = pModel->m_Normals[ normal ];
394 }
396 // Copy all texture coordinates
397 if ( !pModel->m_TextureCoord.empty() )
398 {
399 if ( !pSourceFace->m_pTexturCoords->empty() )
400 {
401 const unsigned int tex = pSourceFace->m_pTexturCoords->at( vertexIndex );
402 ai_assert( tex < pModel->m_TextureCoord.size() );
403 for ( size_t i=0; i < pMesh->GetNumUVChannels(); i++ )
404 {
405 if ( tex >= pModel->m_TextureCoord.size() )
406 throw DeadlyImportError("OBJ: texture coord index out of range");
408 aiVector2D coord2d = pModel->m_TextureCoord[ tex ];
409 pMesh->mTextureCoords[ i ][ newIndex ] = aiVector3D( coord2d.x, coord2d.y, 0.0 );
410 }
411 }
412 }
414 ai_assert( pMesh->mNumVertices > newIndex );
416 // Get destination face
417 aiFace *pDestFace = &pMesh->mFaces[ outIndex ];
419 const bool last = ( vertexIndex == pSourceFace->m_pVertices->size() - 1 );
420 if (pSourceFace->m_PrimitiveType != aiPrimitiveType_LINE || !last)
421 {
422 pDestFace->mIndices[ outVertexIndex ] = newIndex;
423 outVertexIndex++;
424 }
426 if (pSourceFace->m_PrimitiveType == aiPrimitiveType_POINT)
427 {
428 outIndex++;
429 outVertexIndex = 0;
430 }
431 else if (pSourceFace->m_PrimitiveType == aiPrimitiveType_LINE)
432 {
433 outVertexIndex = 0;
435 if(!last)
436 outIndex++;
438 if (vertexIndex) {
439 if(!last) {
440 pMesh->mVertices[ newIndex+1 ] = pMesh->mVertices[ newIndex ];
441 if ( !pSourceFace->m_pNormals->empty() && !pModel->m_Normals.empty()) {
442 pMesh->mNormals[ newIndex+1 ] = pMesh->mNormals[newIndex ];
443 }
444 if ( !pModel->m_TextureCoord.empty() ) {
445 for ( size_t i=0; i < pMesh->GetNumUVChannels(); i++ ) {
446 pMesh->mTextureCoords[ i ][ newIndex+1 ] = pMesh->mTextureCoords[ i ][ newIndex ];
447 }
448 }
449 ++newIndex;
450 }
452 pDestFace[-1].mIndices[1] = newIndex;
453 }
454 }
455 else if (last) {
456 outIndex++;
457 }
458 ++newIndex;
459 }
460 }
461 }
463 // ------------------------------------------------------------------------------------------------
464 // Counts all stored meshes
465 void ObjFileImporter::countObjects(const std::vector<ObjFile::Object*> &rObjects, int &iNumMeshes)
466 {
467 iNumMeshes = 0;
468 if ( rObjects.empty() )
469 return;
471 iNumMeshes += static_cast<unsigned int>( rObjects.size() );
472 for (std::vector<ObjFile::Object*>::const_iterator it = rObjects.begin();
473 it != rObjects.end();
474 ++it)
475 {
476 if (!(*it)->m_SubObjects.empty())
477 {
478 countObjects((*it)->m_SubObjects, iNumMeshes);
479 }
480 }
481 }
483 // ------------------------------------------------------------------------------------------------
484 // Creates the material
485 void ObjFileImporter::createMaterials(const ObjFile::Model* pModel, aiScene* pScene )
486 {
487 ai_assert( NULL != pScene );
488 if ( NULL == pScene )
489 return;
491 const unsigned int numMaterials = (unsigned int) pModel->m_MaterialLib.size();
492 pScene->mNumMaterials = 0;
493 if ( pModel->m_MaterialLib.empty() ) {
494 DefaultLogger::get()->debug("OBJ: no materials specified");
495 return;
496 }
498 pScene->mMaterials = new aiMaterial*[ numMaterials ];
499 for ( unsigned int matIndex = 0; matIndex < numMaterials; matIndex++ )
500 {
501 // Store material name
502 std::map<std::string, ObjFile::Material*>::const_iterator it;
503 it = pModel->m_MaterialMap.find( pModel->m_MaterialLib[ matIndex ] );
505 // No material found, use the default material
506 if ( pModel->m_MaterialMap.end() == it )
507 continue;
509 aiMaterial* mat = new aiMaterial;
510 ObjFile::Material *pCurrentMaterial = (*it).second;
511 mat->AddProperty( &pCurrentMaterial->MaterialName, AI_MATKEY_NAME );
513 // convert illumination model
514 int sm = 0;
515 switch (pCurrentMaterial->illumination_model)
516 {
517 case 0:
518 sm = aiShadingMode_NoShading;
519 break;
520 case 1:
521 sm = aiShadingMode_Gouraud;
522 break;
523 case 2:
524 sm = aiShadingMode_Phong;
525 break;
526 default:
527 sm = aiShadingMode_Gouraud;
528 DefaultLogger::get()->error("OBJ: unexpected illumination model (0-2 recognized)");
529 }
531 mat->AddProperty<int>( &sm, 1, AI_MATKEY_SHADING_MODEL);
533 // multiplying the specular exponent with 2 seems to yield better results
534 pCurrentMaterial->shineness *= 4.f;
536 // Adding material colors
537 mat->AddProperty( &pCurrentMaterial->ambient, 1, AI_MATKEY_COLOR_AMBIENT );
538 mat->AddProperty( &pCurrentMaterial->diffuse, 1, AI_MATKEY_COLOR_DIFFUSE );
539 mat->AddProperty( &pCurrentMaterial->specular, 1, AI_MATKEY_COLOR_SPECULAR );
540 mat->AddProperty( &pCurrentMaterial->shineness, 1, AI_MATKEY_SHININESS );
541 mat->AddProperty( &pCurrentMaterial->alpha, 1, AI_MATKEY_OPACITY );
543 // Adding refraction index
544 mat->AddProperty( &pCurrentMaterial->ior, 1, AI_MATKEY_REFRACTI );
546 // Adding textures
547 if ( 0 != pCurrentMaterial->texture.length )
548 mat->AddProperty( &pCurrentMaterial->texture, AI_MATKEY_TEXTURE_DIFFUSE(0));
550 if ( 0 != pCurrentMaterial->textureAmbient.length )
551 mat->AddProperty( &pCurrentMaterial->textureAmbient, AI_MATKEY_TEXTURE_AMBIENT(0));
553 if ( 0 != pCurrentMaterial->textureSpecular.length )
554 mat->AddProperty( &pCurrentMaterial->textureSpecular, AI_MATKEY_TEXTURE_SPECULAR(0));
556 if ( 0 != pCurrentMaterial->textureBump.length )
557 mat->AddProperty( &pCurrentMaterial->textureBump, AI_MATKEY_TEXTURE_HEIGHT(0));
559 if ( 0 != pCurrentMaterial->textureNormal.length )
560 mat->AddProperty( &pCurrentMaterial->textureNormal, AI_MATKEY_TEXTURE_NORMALS(0));
562 if ( 0 != pCurrentMaterial->textureDisp.length )
563 mat->AddProperty( &pCurrentMaterial->textureDisp, AI_MATKEY_TEXTURE_DISPLACEMENT(0) );
565 if ( 0 != pCurrentMaterial->textureOpacity.length )
566 mat->AddProperty( &pCurrentMaterial->textureOpacity, AI_MATKEY_TEXTURE_OPACITY(0));
568 if ( 0 != pCurrentMaterial->textureSpecularity.length )
569 mat->AddProperty( &pCurrentMaterial->textureSpecularity, AI_MATKEY_TEXTURE_SHININESS(0));
571 // Store material property info in material array in scene
572 pScene->mMaterials[ pScene->mNumMaterials ] = mat;
573 pScene->mNumMaterials++;
574 }
576 // Test number of created materials.
577 ai_assert( pScene->mNumMaterials == numMaterials );
578 }
580 // ------------------------------------------------------------------------------------------------
581 // Appends this node to the parent node
582 void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild)
583 {
584 // Checking preconditions
585 ai_assert( NULL != pParent );
586 ai_assert( NULL != pChild );
588 // Assign parent to child
589 pChild->mParent = pParent;
590 size_t sNumChildren = 0;
591 (void)sNumChildren; // remove warning on release build
593 // If already children was assigned to the parent node, store them in a
594 std::vector<aiNode*> temp;
595 if (pParent->mChildren != NULL)
596 {
597 sNumChildren = pParent->mNumChildren;
598 ai_assert( 0 != sNumChildren );
599 for (size_t index = 0; index < pParent->mNumChildren; index++)
600 {
601 temp.push_back(pParent->mChildren [ index ] );
602 }
603 delete [] pParent->mChildren;
604 }
606 // Copy node instances into parent node
607 pParent->mNumChildren++;
608 pParent->mChildren = new aiNode*[ pParent->mNumChildren ];
609 for (size_t index = 0; index < pParent->mNumChildren-1; index++)
610 {
611 pParent->mChildren[ index ] = temp [ index ];
612 }
613 pParent->mChildren[ pParent->mNumChildren-1 ] = pChild;
614 }
616 // ------------------------------------------------------------------------------------------------
618 } // Namespace Assimp
620 #endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER