vrshoot

view libs/assimp/ObjFileParser.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 "ObjFileParser.h"
46 #include "ObjFileMtlImporter.h"
47 #include "ObjTools.h"
48 #include "ObjFileData.h"
49 #include "ParsingUtils.h"
50 #include "assimp/types.h"
51 #include "DefaultIOSystem.h"
53 namespace Assimp
54 {
56 // -------------------------------------------------------------------
57 const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME;
59 // -------------------------------------------------------------------
60 // Constructor with loaded data and directories.
61 ObjFileParser::ObjFileParser(std::vector<char> &Data,const std::string &strModelName, IOSystem *io ) :
62 m_DataIt(Data.begin()),
63 m_DataItEnd(Data.end()),
64 m_pModel(NULL),
65 m_uiLine(0),
66 m_pIO( io )
67 {
68 std::fill_n(m_buffer,BUFFERSIZE,0);
70 // Create the model instance to store all the data
71 m_pModel = new ObjFile::Model();
72 m_pModel->m_ModelName = strModelName;
74 m_pModel->m_pDefaultMaterial = new ObjFile::Material();
75 m_pModel->m_pDefaultMaterial->MaterialName.Set( DEFAULT_MATERIAL );
76 m_pModel->m_MaterialLib.push_back( DEFAULT_MATERIAL );
77 m_pModel->m_MaterialMap[ DEFAULT_MATERIAL ] = m_pModel->m_pDefaultMaterial;
79 // Start parsing the file
80 parseFile();
81 }
83 // -------------------------------------------------------------------
84 // Destructor
85 ObjFileParser::~ObjFileParser()
86 {
87 /*delete m_pModel->m_pDefaultMaterial;
88 m_pModel->m_pDefaultMaterial = NULL;*/
90 delete m_pModel;
91 m_pModel = NULL;
92 }
94 // -------------------------------------------------------------------
95 // Returns a pointer to the model instance.
96 ObjFile::Model *ObjFileParser::GetModel() const
97 {
98 return m_pModel;
99 }
101 // -------------------------------------------------------------------
102 // File parsing method.
103 void ObjFileParser::parseFile()
104 {
105 if (m_DataIt == m_DataItEnd)
106 return;
108 while (m_DataIt != m_DataItEnd)
109 {
110 switch (*m_DataIt)
111 {
112 case 'v': // Parse a vertex texture coordinate
113 {
114 ++m_DataIt;
115 if (*m_DataIt == ' ')
116 {
117 // Read in vertex definition
118 getVector3(m_pModel->m_Vertices);
119 }
120 else if (*m_DataIt == 't')
121 {
122 // Read in texture coordinate (2D)
123 ++m_DataIt;
124 getVector2(m_pModel->m_TextureCoord);
125 }
126 else if (*m_DataIt == 'n')
127 {
128 // Read in normal vector definition
129 ++m_DataIt;
130 getVector3( m_pModel->m_Normals );
131 }
132 }
133 break;
135 case 'p': // Parse a face, line or point statement
136 case 'l':
137 case 'f':
138 {
139 getFace(*m_DataIt == 'f' ? aiPrimitiveType_POLYGON : (*m_DataIt == 'l'
140 ? aiPrimitiveType_LINE : aiPrimitiveType_POINT));
141 }
142 break;
144 case '#': // Parse a comment
145 {
146 getComment();
147 }
148 break;
150 case 'u': // Parse a material desc. setter
151 {
152 getMaterialDesc();
153 }
154 break;
156 case 'm': // Parse a material library
157 {
158 getMaterialLib();
159 }
160 break;
162 case 'g': // Parse group name
163 {
164 getGroupName();
165 }
166 break;
168 case 's': // Parse group number
169 {
170 getGroupNumber();
171 }
172 break;
174 case 'o': // Parse object name
175 {
176 getObjectName();
177 }
178 break;
180 default:
181 {
182 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
183 }
184 break;
185 }
186 }
187 }
189 // -------------------------------------------------------------------
190 // Copy the next word in a temporary buffer
191 void ObjFileParser::copyNextWord(char *pBuffer, size_t length)
192 {
193 size_t index = 0;
194 m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
195 while ( m_DataIt != m_DataItEnd && !isSeparator(*m_DataIt) )
196 {
197 pBuffer[index] = *m_DataIt;
198 index++;
199 if (index == length-1)
200 break;
201 ++m_DataIt;
202 }
203 pBuffer[index] = '\0';
204 }
206 // -------------------------------------------------------------------
207 // Copy the next line into a temporary buffer
208 void ObjFileParser::copyNextLine(char *pBuffer, size_t length)
209 {
210 size_t index = 0;
211 while (m_DataIt != m_DataItEnd)
212 {
213 if (*m_DataIt == '\n' || *m_DataIt == '\r' || index == length-1)
214 break;
216 pBuffer[ index ] = *m_DataIt;
217 ++index;
218 ++m_DataIt;
219 }
220 pBuffer[ index ] = '\0';
221 }
223 // -------------------------------------------------------------------
224 // Get values for a new 3D vector instance
225 void ObjFileParser::getVector3(std::vector<aiVector3D> &point3d_array)
226 {
227 float x, y, z;
228 copyNextWord(m_buffer, BUFFERSIZE);
229 x = (float) fast_atof(m_buffer);
231 copyNextWord(m_buffer, BUFFERSIZE);
232 y = (float) fast_atof(m_buffer);
234 copyNextWord(m_buffer, BUFFERSIZE);
235 z = (float) fast_atof(m_buffer);
237 point3d_array.push_back( aiVector3D( x, y, z ) );
238 //skipLine();
239 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
240 }
242 // -------------------------------------------------------------------
243 // Get values for a new 2D vector instance
244 void ObjFileParser::getVector2( std::vector<aiVector2D> &point2d_array )
245 {
246 float x, y;
247 copyNextWord(m_buffer, BUFFERSIZE);
248 x = (float) fast_atof(m_buffer);
250 copyNextWord(m_buffer, BUFFERSIZE);
251 y = (float) fast_atof(m_buffer);
253 point2d_array.push_back(aiVector2D(x, y));
255 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
256 }
258 // -------------------------------------------------------------------
259 // Get values for a new face instance
260 void ObjFileParser::getFace(aiPrimitiveType type)
261 {
262 copyNextLine(m_buffer, BUFFERSIZE);
263 if (m_DataIt == m_DataItEnd)
264 return;
266 char *pPtr = m_buffer;
267 char *pEnd = &pPtr[BUFFERSIZE];
268 pPtr = getNextToken<char*>(pPtr, pEnd);
269 if (pPtr == pEnd || *pPtr == '\0')
270 return;
272 std::vector<unsigned int> *pIndices = new std::vector<unsigned int>;
273 std::vector<unsigned int> *pTexID = new std::vector<unsigned int>;
274 std::vector<unsigned int> *pNormalID = new std::vector<unsigned int>;
275 bool hasNormal = false;
277 const bool vt = (!m_pModel->m_TextureCoord.empty());
278 const bool vn = (!m_pModel->m_Normals.empty());
279 int iStep = 0, iPos = 0;
280 while (pPtr != pEnd)
281 {
282 iStep = 1;
284 if (IsLineEnd(*pPtr))
285 break;
287 if (*pPtr=='/' )
288 {
289 if (type == aiPrimitiveType_POINT) {
290 DefaultLogger::get()->error("Obj: Separator unexpected in point statement");
291 }
292 if (iPos == 0)
293 {
294 //if there are no texture coordinates in the file, but normals
295 if (!vt && vn) {
296 iPos = 1;
297 iStep++;
298 }
299 }
300 iPos++;
301 }
302 else if ( isSeparator(*pPtr) )
303 {
304 iPos = 0;
305 }
306 else
307 {
308 //OBJ USES 1 Base ARRAYS!!!!
309 const int iVal = atoi( pPtr );
310 int tmp = iVal;
311 while ( ( tmp = tmp / 10 )!=0 )
312 ++iStep;
314 if ( iVal > 0 )
315 {
316 // Store parsed index
317 if ( 0 == iPos )
318 {
319 pIndices->push_back( iVal-1 );
320 }
321 else if ( 1 == iPos )
322 {
323 pTexID->push_back( iVal-1 );
324 }
325 else if ( 2 == iPos )
326 {
327 pNormalID->push_back( iVal-1 );
328 hasNormal = true;
329 }
330 else
331 {
332 reportErrorTokenInFace();
333 }
334 }
335 }
336 pPtr += iStep;
337 }
339 if ( pIndices->empty() )
340 {
341 DefaultLogger::get()->error("Obj: Ignoring empty face");
342 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
343 return;
344 }
346 ObjFile::Face *face = new ObjFile::Face( pIndices, pNormalID, pTexID, type );
348 // Set active material, if one set
349 if (NULL != m_pModel->m_pCurrentMaterial)
350 face->m_pMaterial = m_pModel->m_pCurrentMaterial;
351 else
352 face->m_pMaterial = m_pModel->m_pDefaultMaterial;
354 // Create a default object, if nothing is there
355 if ( NULL == m_pModel->m_pCurrent )
356 createObject( "defaultobject" );
358 // Assign face to mesh
359 if ( NULL == m_pModel->m_pCurrentMesh )
360 {
361 createMesh();
362 }
364 // Store the face
365 m_pModel->m_pCurrentMesh->m_Faces.push_back( face );
366 m_pModel->m_pCurrentMesh->m_uiNumIndices += (unsigned int)face->m_pVertices->size();
367 m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += (unsigned int)face->m_pTexturCoords[0].size();
368 if( !m_pModel->m_pCurrentMesh->m_hasNormals && hasNormal )
369 {
370 m_pModel->m_pCurrentMesh->m_hasNormals = true;
371 }
372 // Skip the rest of the line
373 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
374 }
376 // -------------------------------------------------------------------
377 // Get values for a new material description
378 void ObjFileParser::getMaterialDesc()
379 {
380 // Each material request a new object.
381 // Sometimes the object is already created (see 'o' tag by example), but it is not initialized !
382 // So, we create a new object only if the current on is already initialized !
383 if (m_pModel->m_pCurrent != NULL &&
384 ( m_pModel->m_pCurrent->m_Meshes.size() > 1 ||
385 (m_pModel->m_pCurrent->m_Meshes.size() == 1 && m_pModel->m_Meshes[m_pModel->m_pCurrent->m_Meshes[0]]->m_Faces.size() != 0) )
386 )
387 m_pModel->m_pCurrent = NULL;
389 // Get next data for material data
390 m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
391 if (m_DataIt == m_DataItEnd)
392 return;
394 char *pStart = &(*m_DataIt);
395 while ( m_DataIt != m_DataItEnd && !isSeparator(*m_DataIt) )
396 ++m_DataIt;
398 // Get name
399 std::string strName(pStart, &(*m_DataIt));
400 if ( strName.empty())
401 return;
403 // Search for material
404 std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strName );
405 if ( it == m_pModel->m_MaterialMap.end() )
406 {
407 // Not found, use default material
408 m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
409 DefaultLogger::get()->error("OBJ: failed to locate material " + strName + ", skipping");
410 }
411 else
412 {
413 // Found, using detected material
414 m_pModel->m_pCurrentMaterial = (*it).second;
415 if ( needsNewMesh( strName ))
416 {
417 createMesh();
418 }
419 m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strName );
420 }
422 // Skip rest of line
423 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
424 }
426 // -------------------------------------------------------------------
427 // Get a comment, values will be skipped
428 void ObjFileParser::getComment()
429 {
430 while (m_DataIt != m_DataItEnd)
431 {
432 if ( '\n' == (*m_DataIt))
433 {
434 ++m_DataIt;
435 break;
436 }
437 else
438 {
439 ++m_DataIt;
440 }
441 }
442 }
444 // -------------------------------------------------------------------
445 // Get material library from file.
446 void ObjFileParser::getMaterialLib()
447 {
448 // Translate tuple
449 m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
450 if (m_DataIt == m_DataItEnd)
451 return;
453 char *pStart = &(*m_DataIt);
454 while (m_DataIt != m_DataItEnd && !isNewLine(*m_DataIt))
455 m_DataIt++;
457 // Check for existence
458 const std::string strMatName(pStart, &(*m_DataIt));
459 IOStream *pFile = m_pIO->Open(strMatName);
461 if (!pFile )
462 {
463 DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName);
464 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
465 return;
466 }
468 // Import material library data from file
469 std::vector<char> buffer;
470 BaseImporter::TextFileToBuffer(pFile,buffer);
471 m_pIO->Close( pFile );
473 // Importing the material library
474 ObjFileMtlImporter mtlImporter( buffer, strMatName, m_pModel );
475 }
477 // -------------------------------------------------------------------
478 // Set a new material definition as the current material.
479 void ObjFileParser::getNewMaterial()
480 {
481 m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
482 m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
483 if ( m_DataIt == m_DataItEnd )
484 return;
486 char *pStart = &(*m_DataIt);
487 std::string strMat( pStart, *m_DataIt );
488 while ( m_DataIt != m_DataItEnd && isSeparator( *m_DataIt ) )
489 m_DataIt++;
490 std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strMat );
491 if ( it == m_pModel->m_MaterialMap.end() )
492 {
493 // Show a warning, if material was not found
494 DefaultLogger::get()->warn("OBJ: Unsupported material requested: " + strMat);
495 m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
496 }
497 else
498 {
499 // Set new material
500 if ( needsNewMesh( strMat ) )
501 {
502 createMesh();
503 }
504 m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strMat );
505 }
507 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
508 }
510 // -------------------------------------------------------------------
511 int ObjFileParser::getMaterialIndex( const std::string &strMaterialName )
512 {
513 int mat_index = -1;
514 if ( strMaterialName.empty() )
515 return mat_index;
516 for (size_t index = 0; index < m_pModel->m_MaterialLib.size(); ++index)
517 {
518 if ( strMaterialName == m_pModel->m_MaterialLib[ index ])
519 {
520 mat_index = (int)index;
521 break;
522 }
523 }
524 return mat_index;
525 }
527 // -------------------------------------------------------------------
528 // Getter for a group name.
529 void ObjFileParser::getGroupName()
530 {
531 std::string strGroupName;
533 m_DataIt = getName<DataArrayIt>(m_DataIt, m_DataItEnd, strGroupName);
534 if ( isEndOfBuffer( m_DataIt, m_DataItEnd ) )
535 return;
537 // Change active group, if necessary
538 if ( m_pModel->m_strActiveGroup != strGroupName )
539 {
540 // Search for already existing entry
541 ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(strGroupName);
543 // We are mapping groups into the object structure
544 createObject( strGroupName );
546 // New group name, creating a new entry
547 if (it == m_pModel->m_Groups.end())
548 {
549 std::vector<unsigned int> *pFaceIDArray = new std::vector<unsigned int>;
550 m_pModel->m_Groups[ strGroupName ] = pFaceIDArray;
551 m_pModel->m_pGroupFaceIDs = (pFaceIDArray);
552 }
553 else
554 {
555 m_pModel->m_pGroupFaceIDs = (*it).second;
556 }
557 m_pModel->m_strActiveGroup = strGroupName;
558 }
559 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
560 }
562 // -------------------------------------------------------------------
563 // Not supported
564 void ObjFileParser::getGroupNumber()
565 {
566 // Not used
568 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
569 }
571 // -------------------------------------------------------------------
572 // Stores values for a new object instance, name will be used to
573 // identify it.
574 void ObjFileParser::getObjectName()
575 {
576 m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
577 if (m_DataIt == m_DataItEnd)
578 return;
579 char *pStart = &(*m_DataIt);
580 while ( m_DataIt != m_DataItEnd && !isSeparator( *m_DataIt ) )
581 ++m_DataIt;
583 std::string strObjectName(pStart, &(*m_DataIt));
584 if (!strObjectName.empty())
585 {
586 // Reset current object
587 m_pModel->m_pCurrent = NULL;
589 // Search for actual object
590 for (std::vector<ObjFile::Object*>::const_iterator it = m_pModel->m_Objects.begin();
591 it != m_pModel->m_Objects.end();
592 ++it)
593 {
594 if ((*it)->m_strObjName == strObjectName)
595 {
596 m_pModel->m_pCurrent = *it;
597 break;
598 }
599 }
601 // Allocate a new object, if current one was not found before
602 if ( NULL == m_pModel->m_pCurrent )
603 createObject(strObjectName);
604 }
605 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
606 }
607 // -------------------------------------------------------------------
608 // Creates a new object instance
609 void ObjFileParser::createObject(const std::string &strObjectName)
610 {
611 ai_assert( NULL != m_pModel );
612 //ai_assert( !strObjectName.empty() );
614 m_pModel->m_pCurrent = new ObjFile::Object;
615 m_pModel->m_pCurrent->m_strObjName = strObjectName;
616 m_pModel->m_Objects.push_back( m_pModel->m_pCurrent );
619 createMesh();
621 if( m_pModel->m_pCurrentMaterial )
622 {
623 m_pModel->m_pCurrentMesh->m_uiMaterialIndex =
624 getMaterialIndex( m_pModel->m_pCurrentMaterial->MaterialName.data );
625 m_pModel->m_pCurrentMesh->m_pMaterial = m_pModel->m_pCurrentMaterial;
626 }
627 }
628 // -------------------------------------------------------------------
629 // Creates a new mesh
630 void ObjFileParser::createMesh()
631 {
632 ai_assert( NULL != m_pModel );
633 m_pModel->m_pCurrentMesh = new ObjFile::Mesh;
634 m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh );
635 unsigned int meshId = m_pModel->m_Meshes.size()-1;
636 if ( NULL != m_pModel->m_pCurrent )
637 {
638 m_pModel->m_pCurrent->m_Meshes.push_back( meshId );
639 }
640 else
641 {
642 DefaultLogger::get()->error("OBJ: No object detected to attach a new mesh instance.");
643 }
644 }
646 // -------------------------------------------------------------------
647 // Returns true, if a new mesh must be created.
648 bool ObjFileParser::needsNewMesh( const std::string &rMaterialName )
649 {
650 if(m_pModel->m_pCurrentMesh == 0)
651 {
652 // No mesh data yet
653 return true;
654 }
655 bool newMat = false;
656 int matIdx = getMaterialIndex( rMaterialName );
657 int curMatIdx = m_pModel->m_pCurrentMesh->m_uiMaterialIndex;
658 if ( curMatIdx != int(ObjFile::Mesh::NoMaterial) || curMatIdx != matIdx )
659 {
660 // New material -> only one material per mesh, so we need to create a new
661 // material
662 newMat = true;
663 }
664 return newMat;
665 }
667 // -------------------------------------------------------------------
668 // Shows an error in parsing process.
669 void ObjFileParser::reportErrorTokenInFace()
670 {
671 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
672 DefaultLogger::get()->error("OBJ: Not supported token in face description detected");
673 }
675 // -------------------------------------------------------------------
677 } // Namespace Assimp
679 #endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER