1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2015, 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 
43 #ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
44 
45 #include "ObjFileParser.h"
46 #include "ObjFileMtlImporter.h"
47 #include "ObjTools.h"
48 #include "ObjFileData.h"
49 #include "ParsingUtils.h"
50 #include "DefaultIOSystem.h"
51 #include "BaseImporter.h"
52 #include <assimp/DefaultLogger.hpp>
53 #include <assimp/material.h>
54 #include <assimp/Importer.hpp>
55 #include <cstdlib>
56 
57 
58 namespace Assimp {
59 
60 const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME;
61 
62 // -------------------------------------------------------------------
63 //  Constructor with loaded data and directories.
ObjFileParser(std::vector<char> & data,const std::string & modelName,IOSystem * io)64 ObjFileParser::ObjFileParser(std::vector<char> &data,const std::string &modelName, IOSystem *io ) :
65     m_DataIt(data.begin()),
66     m_DataItEnd(data.end()),
67     m_pModel(NULL),
68     m_uiLine(0),
69     m_pIO( io )
70 {
71     std::fill_n(m_buffer,Buffersize,0);
72 
73     // Create the model instance to store all the data
74     m_pModel = new ObjFile::Model();
75     m_pModel->m_ModelName = modelName;
76 
77     // create default material and store it
78     m_pModel->m_pDefaultMaterial = new ObjFile::Material;
79     m_pModel->m_pDefaultMaterial->MaterialName.Set( DEFAULT_MATERIAL );
80     m_pModel->m_MaterialLib.push_back( DEFAULT_MATERIAL );
81     m_pModel->m_MaterialMap[ DEFAULT_MATERIAL ] = m_pModel->m_pDefaultMaterial;
82 
83     // Start parsing the file
84     parseFile();
85 }
86 
87 // -------------------------------------------------------------------
88 //  Destructor
~ObjFileParser()89 ObjFileParser::~ObjFileParser()
90 {
91     delete m_pModel;
92     m_pModel = NULL;
93 }
94 
95 // -------------------------------------------------------------------
96 //  Returns a pointer to the model instance.
GetModel() const97 ObjFile::Model *ObjFileParser::GetModel() const
98 {
99     return m_pModel;
100 }
101 
102 // -------------------------------------------------------------------
103 //  File parsing method.
parseFile()104 void ObjFileParser::parseFile()
105 {
106     if (m_DataIt == m_DataItEnd)
107         return;
108 
109     while (m_DataIt != m_DataItEnd)
110     {
111         switch (*m_DataIt)
112         {
113         case 'v': // Parse a vertex texture coordinate
114             {
115                 ++m_DataIt;
116                 if (*m_DataIt == ' ' || *m_DataIt == '\t') {
117                     // read in vertex definition
118                     getVector3(m_pModel->m_Vertices);
119                 } else if (*m_DataIt == 't') {
120                     // read in texture coordinate ( 2D or 3D )
121                                         ++m_DataIt;
122                                         getVector( m_pModel->m_TextureCoord );
123                 } else if (*m_DataIt == 'n') {
124                     // Read in normal vector definition
125                     ++m_DataIt;
126                     getVector3( m_pModel->m_Normals );
127                 }
128             }
129             break;
130 
131         case 'p': // Parse a face, line or point statement
132         case 'l':
133         case 'f':
134             {
135                 getFace(*m_DataIt == 'f' ? aiPrimitiveType_POLYGON : (*m_DataIt == 'l'
136                     ? aiPrimitiveType_LINE : aiPrimitiveType_POINT));
137             }
138             break;
139 
140         case '#': // Parse a comment
141             {
142                 getComment();
143             }
144             break;
145 
146         case 'u': // Parse a material desc. setter
147             {
148                 getMaterialDesc();
149             }
150             break;
151 
152         case 'm': // Parse a material library or merging group ('mg')
153             {
154                 if (*(m_DataIt + 1) == 'g')
155                     getGroupNumberAndResolution();
156                 else
157                     getMaterialLib();
158             }
159             break;
160 
161         case 'g': // Parse group name
162             {
163                 getGroupName();
164             }
165             break;
166 
167         case 's': // Parse group number
168             {
169                 getGroupNumber();
170             }
171             break;
172 
173         case 'o': // Parse object name
174             {
175                 getObjectName();
176             }
177             break;
178 
179         default:
180             {
181                 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
182             }
183             break;
184         }
185     }
186 }
187 
188 // -------------------------------------------------------------------
189 //  Copy the next word in a temporary buffer
copyNextWord(char * pBuffer,size_t length)190 void ObjFileParser::copyNextWord(char *pBuffer, size_t length)
191 {
192     size_t index = 0;
193     m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
194     while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) {
195         pBuffer[index] = *m_DataIt;
196         index++;
197         if( index == length - 1 ) {
198             break;
199         }
200         ++m_DataIt;
201     }
202 
203     ai_assert(index < length);
204     pBuffer[index] = '\0';
205 }
206 
207 // -------------------------------------------------------------------
208 // Copy the next line into a temporary buffer
copyNextLine(char * pBuffer,size_t length)209 void ObjFileParser::copyNextLine(char *pBuffer, size_t length)
210 {
211     size_t index = 0u;
212 
213     // some OBJ files have line continuations using \ (such as in C++ et al)
214     bool continuation = false;
215     for (;m_DataIt != m_DataItEnd && index < length-1; ++m_DataIt)
216     {
217         const char c = *m_DataIt;
218         if (c == '\\') {
219             continuation = true;
220             continue;
221         }
222 
223         if (c == '\n' || c == '\r') {
224             if(continuation) {
225                 pBuffer[ index++ ] = ' ';
226                 continue;
227             }
228             break;
229         }
230 
231         continuation = false;
232         pBuffer[ index++ ] = c;
233     }
234     ai_assert(index < length);
235     pBuffer[ index ] = '\0';
236 }
237 
238 // -------------------------------------------------------------------
getVector(std::vector<aiVector3D> & point3d_array)239 void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
240     size_t numComponents( 0 );
241     const char* tmp( &m_DataIt[0] );
242     while( !IsLineEnd( *tmp ) ) {
243         if ( !SkipSpaces( &tmp ) ) {
244             break;
245         }
246         SkipToken( tmp );
247         ++numComponents;
248     }
249     float x, y, z;
250     if( 2 == numComponents ) {
251         copyNextWord( m_buffer, Buffersize );
252         x = ( float ) fast_atof( m_buffer );
253 
254         copyNextWord( m_buffer, Buffersize );
255         y = ( float ) fast_atof( m_buffer );
256         z = 0.0;
257     } else if( 3 == numComponents ) {
258         copyNextWord( m_buffer, Buffersize );
259         x = ( float ) fast_atof( m_buffer );
260 
261         copyNextWord( m_buffer, Buffersize );
262         y = ( float ) fast_atof( m_buffer );
263 
264         copyNextWord( m_buffer, Buffersize );
265         z = ( float ) fast_atof( m_buffer );
266     } else {
267         throw DeadlyImportError( "OBJ: Invalid number of components" );
268     }
269     point3d_array.push_back( aiVector3D( x, y, z ) );
270     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
271 }
272 
273 // -------------------------------------------------------------------
274 //  Get values for a new 3D vector instance
getVector3(std::vector<aiVector3D> & point3d_array)275 void ObjFileParser::getVector3(std::vector<aiVector3D> &point3d_array) {
276     float x, y, z;
277     copyNextWord(m_buffer, Buffersize);
278     x = (float) fast_atof(m_buffer);
279 
280     copyNextWord(m_buffer, Buffersize);
281     y = (float) fast_atof(m_buffer);
282 
283     copyNextWord( m_buffer, Buffersize );
284     z = ( float ) fast_atof( m_buffer );
285 
286     point3d_array.push_back( aiVector3D( x, y, z ) );
287     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
288 }
289 
290 // -------------------------------------------------------------------
291 //  Get values for a new 2D vector instance
getVector2(std::vector<aiVector2D> & point2d_array)292 void ObjFileParser::getVector2( std::vector<aiVector2D> &point2d_array ) {
293     float x, y;
294     copyNextWord(m_buffer, Buffersize);
295     x = (float) fast_atof(m_buffer);
296 
297     copyNextWord(m_buffer, Buffersize);
298     y = (float) fast_atof(m_buffer);
299 
300     point2d_array.push_back(aiVector2D(x, y));
301 
302     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
303 }
304 
305 // -------------------------------------------------------------------
306 //  Get values for a new face instance
getFace(aiPrimitiveType type)307 void ObjFileParser::getFace(aiPrimitiveType type)
308 {
309     copyNextLine(m_buffer, Buffersize);
310     if (m_DataIt == m_DataItEnd)
311         return;
312 
313     char *pPtr = m_buffer;
314     char *pEnd = &pPtr[Buffersize];
315     pPtr = getNextToken<char*>(pPtr, pEnd);
316     if (pPtr == pEnd || *pPtr == '\0')
317         return;
318 
319     std::vector<unsigned int> *pIndices = new std::vector<unsigned int>;
320     std::vector<unsigned int> *pTexID = new std::vector<unsigned int>;
321     std::vector<unsigned int> *pNormalID = new std::vector<unsigned int>;
322     bool hasNormal = false;
323 
324     const int vSize = m_pModel->m_Vertices.size();
325     const int vtSize = m_pModel->m_TextureCoord.size();
326     const int vnSize = m_pModel->m_Normals.size();
327 
328     const bool vt = (!m_pModel->m_TextureCoord.empty());
329     const bool vn = (!m_pModel->m_Normals.empty());
330     int iStep = 0, iPos = 0;
331     while (pPtr != pEnd)
332     {
333         iStep = 1;
334 
335         if (IsLineEnd(*pPtr))
336             break;
337 
338         if (*pPtr=='/' )
339         {
340             if (type == aiPrimitiveType_POINT) {
341                 DefaultLogger::get()->error("Obj: Separator unexpected in point statement");
342             }
343             if (iPos == 0)
344             {
345                 //if there are no texture coordinates in the file, but normals
346                 if (!vt && vn) {
347                     iPos = 1;
348                     iStep++;
349                 }
350             }
351             iPos++;
352         }
353         else if( IsSpaceOrNewLine( *pPtr ) )
354         {
355             iPos = 0;
356         }
357         else
358         {
359             //OBJ USES 1 Base ARRAYS!!!!
360             const int iVal = atoi( pPtr );
361 
362             // increment iStep position based off of the sign and # of digits
363             int tmp = iVal;
364             if (iVal < 0)
365                 ++iStep;
366             while ( ( tmp = tmp / 10 )!=0 )
367                 ++iStep;
368 
369             if ( iVal > 0 )
370             {
371                 // Store parsed index
372                 if ( 0 == iPos )
373                 {
374                     pIndices->push_back( iVal-1 );
375                 }
376                 else if ( 1 == iPos )
377                 {
378                     pTexID->push_back( iVal-1 );
379                 }
380                 else if ( 2 == iPos )
381                 {
382                     pNormalID->push_back( iVal-1 );
383                     hasNormal = true;
384                 }
385                 else
386                 {
387                     reportErrorTokenInFace();
388                 }
389             }
390             else if ( iVal < 0 )
391             {
392                 // Store relatively index
393                 if ( 0 == iPos )
394                 {
395                     pIndices->push_back( vSize + iVal );
396                 }
397                 else if ( 1 == iPos )
398                 {
399                     pTexID->push_back( vtSize + iVal );
400                 }
401                 else if ( 2 == iPos )
402                 {
403                     pNormalID->push_back( vnSize + iVal );
404                     hasNormal = true;
405                 }
406                 else
407                 {
408                     reportErrorTokenInFace();
409                 }
410             }
411         }
412         pPtr += iStep;
413     }
414 
415     if ( pIndices->empty() ) {
416         DefaultLogger::get()->error("Obj: Ignoring empty face");
417         // skip line and clean up
418         m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
419         delete pNormalID;
420         delete pTexID;
421         delete pIndices;
422 
423         return;
424     }
425 
426     ObjFile::Face *face = new ObjFile::Face( pIndices, pNormalID, pTexID, type );
427 
428     // Set active material, if one set
429     if( NULL != m_pModel->m_pCurrentMaterial ) {
430         face->m_pMaterial = m_pModel->m_pCurrentMaterial;
431     } else {
432         face->m_pMaterial = m_pModel->m_pDefaultMaterial;
433     }
434 
435     // Create a default object, if nothing is there
436     if( NULL == m_pModel->m_pCurrent ) {
437         createObject( "defaultobject" );
438     }
439 
440     // Assign face to mesh
441     if ( NULL == m_pModel->m_pCurrentMesh ) {
442         createMesh( "defaultobject" );
443     }
444 
445     // Store the face
446     m_pModel->m_pCurrentMesh->m_Faces.push_back( face );
447     m_pModel->m_pCurrentMesh->m_uiNumIndices += (unsigned int)face->m_pVertices->size();
448     m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += (unsigned int)face->m_pTexturCoords[0].size();
449     if( !m_pModel->m_pCurrentMesh->m_hasNormals && hasNormal ) {
450         m_pModel->m_pCurrentMesh->m_hasNormals = true;
451     }
452     // Skip the rest of the line
453     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
454 }
455 
456 // -------------------------------------------------------------------
457 //  Get values for a new material description
getMaterialDesc()458 void ObjFileParser::getMaterialDesc()
459 {
460     // Each material request a new object.
461     // Sometimes the object is already created (see 'o' tag by example), but it is not initialized !
462     // So, we create a new object only if the current on is already initialized !
463     if (m_pModel->m_pCurrent != NULL &&
464         (   m_pModel->m_pCurrent->m_Meshes.size() > 1 ||
465             (m_pModel->m_pCurrent->m_Meshes.size() == 1 && m_pModel->m_Meshes[m_pModel->m_pCurrent->m_Meshes[0]]->m_Faces.size() != 0)  )
466         )
467         m_pModel->m_pCurrent = NULL;
468 
469     // Get next data for material data
470     m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
471     if (m_DataIt == m_DataItEnd) {
472         return;
473     }
474 
475     char *pStart = &(*m_DataIt);
476     while( m_DataIt != m_DataItEnd && !IsLineEnd( *m_DataIt ) ) {
477         ++m_DataIt;
478     }
479 
480     // Get name
481     std::string strName(pStart, &(*m_DataIt));
482     if ( strName.empty())
483         return;
484 
485     // Search for material
486     std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strName );
487     if ( it == m_pModel->m_MaterialMap.end() ) {
488         // Not found, use default material
489         m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
490         DefaultLogger::get()->error("OBJ: failed to locate material " + strName + ", skipping");
491     } else {
492         // Found, using detected material
493         m_pModel->m_pCurrentMaterial = (*it).second;
494         if ( needsNewMesh( strName ))
495         {
496             createMesh( strName  );
497         }
498         m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strName );
499     }
500 
501     // Skip rest of line
502     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
503 }
504 
505 // -------------------------------------------------------------------
506 //  Get a comment, values will be skipped
getComment()507 void ObjFileParser::getComment()
508 {
509     while (m_DataIt != m_DataItEnd)
510     {
511         if ( '\n' == (*m_DataIt))
512         {
513             ++m_DataIt;
514             break;
515         }
516         else
517         {
518             ++m_DataIt;
519         }
520     }
521 }
522 
523 // -------------------------------------------------------------------
524 //  Get material library from file.
getMaterialLib()525 void ObjFileParser::getMaterialLib()
526 {
527     // Translate tuple
528     m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
529     if( m_DataIt == m_DataItEnd ) {
530         return;
531     }
532 
533     char *pStart = &(*m_DataIt);
534     while( m_DataIt != m_DataItEnd && !IsLineEnd( *m_DataIt ) ) {
535         ++m_DataIt;
536     }
537 
538     // Check for existence
539     const std::string strMatName(pStart, &(*m_DataIt));
540     std::string absName;
541     if ( m_pIO->StackSize() > 0 ) {
542         std::string path = m_pIO->CurrentDirectory();
543         if ( '/' != *path.rbegin() ) {
544           path += '/';
545         }
546         absName = path + strMatName;
547     } else {
548         absName = strMatName;
549     }
550     IOStream *pFile = m_pIO->Open( absName );
551 
552     if (!pFile ) {
553         DefaultLogger::get()->error( "OBJ: Unable to locate material file " + strMatName );
554         m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
555         return;
556     }
557 
558     // Import material library data from file
559     std::vector<char> buffer;
560     BaseImporter::TextFileToBuffer( pFile, buffer );
561     m_pIO->Close( pFile );
562 
563     // Importing the material library
564     ObjFileMtlImporter mtlImporter( buffer, strMatName, m_pModel );
565 }
566 
567 // -------------------------------------------------------------------
568 //  Set a new material definition as the current material.
getNewMaterial()569 void ObjFileParser::getNewMaterial()
570 {
571     m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
572     m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
573     if( m_DataIt == m_DataItEnd ) {
574         return;
575     }
576 
577     char *pStart = &(*m_DataIt);
578     std::string strMat( pStart, *m_DataIt );
579     while( m_DataIt != m_DataItEnd && IsSpaceOrNewLine( *m_DataIt ) ) {
580         ++m_DataIt;
581     }
582     std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strMat );
583     if ( it == m_pModel->m_MaterialMap.end() )
584     {
585         // Show a warning, if material was not found
586         DefaultLogger::get()->warn("OBJ: Unsupported material requested: " + strMat);
587         m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
588     }
589     else
590     {
591         // Set new material
592         if ( needsNewMesh( strMat ) )
593         {
594             createMesh( strMat );
595         }
596         m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strMat );
597     }
598 
599     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
600 }
601 
602 // -------------------------------------------------------------------
getMaterialIndex(const std::string & strMaterialName)603 int ObjFileParser::getMaterialIndex( const std::string &strMaterialName )
604 {
605     int mat_index = -1;
606     if( strMaterialName.empty() ) {
607         return mat_index;
608     }
609     for (size_t index = 0; index < m_pModel->m_MaterialLib.size(); ++index)
610     {
611         if ( strMaterialName == m_pModel->m_MaterialLib[ index ])
612         {
613             mat_index = (int)index;
614             break;
615         }
616     }
617     return mat_index;
618 }
619 
620 // -------------------------------------------------------------------
621 //  Getter for a group name.
getGroupName()622 void ObjFileParser::getGroupName()
623 {
624     std::string strGroupName;
625 
626     m_DataIt = getName<DataArrayIt>(m_DataIt, m_DataItEnd, strGroupName);
627     if( isEndOfBuffer( m_DataIt, m_DataItEnd ) ) {
628         return;
629     }
630 
631     // Change active group, if necessary
632     if ( m_pModel->m_strActiveGroup != strGroupName )
633     {
634         // Search for already existing entry
635         ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(strGroupName);
636 
637         // We are mapping groups into the object structure
638         createObject( strGroupName );
639 
640         // New group name, creating a new entry
641         if (it == m_pModel->m_Groups.end())
642         {
643             std::vector<unsigned int> *pFaceIDArray = new std::vector<unsigned int>;
644             m_pModel->m_Groups[ strGroupName ] = pFaceIDArray;
645             m_pModel->m_pGroupFaceIDs = (pFaceIDArray);
646         }
647         else
648         {
649             m_pModel->m_pGroupFaceIDs = (*it).second;
650         }
651         m_pModel->m_strActiveGroup = strGroupName;
652     }
653     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
654 }
655 
656 // -------------------------------------------------------------------
657 //  Not supported
getGroupNumber()658 void ObjFileParser::getGroupNumber()
659 {
660     // Not used
661 
662     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
663 }
664 
665 // -------------------------------------------------------------------
666 //  Not supported
getGroupNumberAndResolution()667 void ObjFileParser::getGroupNumberAndResolution()
668 {
669     // Not used
670 
671     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
672 }
673 
674 // -------------------------------------------------------------------
675 //  Stores values for a new object instance, name will be used to
676 //  identify it.
getObjectName()677 void ObjFileParser::getObjectName()
678 {
679     m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
680     if( m_DataIt == m_DataItEnd ) {
681         return;
682     }
683     char *pStart = &(*m_DataIt);
684     while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) {
685         ++m_DataIt;
686     }
687 
688     std::string strObjectName(pStart, &(*m_DataIt));
689     if (!strObjectName.empty())
690     {
691         // Reset current object
692         m_pModel->m_pCurrent = NULL;
693 
694         // Search for actual object
695         for (std::vector<ObjFile::Object*>::const_iterator it = m_pModel->m_Objects.begin();
696             it != m_pModel->m_Objects.end();
697             ++it)
698         {
699             if ((*it)->m_strObjName == strObjectName)
700             {
701                 m_pModel->m_pCurrent = *it;
702                 break;
703             }
704         }
705 
706         // Allocate a new object, if current one was not found before
707         if( NULL == m_pModel->m_pCurrent ) {
708             createObject( strObjectName );
709         }
710     }
711     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
712 }
713 // -------------------------------------------------------------------
714 //  Creates a new object instance
createObject(const std::string & objName)715 void ObjFileParser::createObject(const std::string &objName)
716 {
717     ai_assert( NULL != m_pModel );
718 
719     m_pModel->m_pCurrent = new ObjFile::Object;
720     m_pModel->m_pCurrent->m_strObjName = objName;
721     m_pModel->m_Objects.push_back( m_pModel->m_pCurrent );
722 
723     createMesh( objName  );
724 
725     if( m_pModel->m_pCurrentMaterial )
726     {
727         m_pModel->m_pCurrentMesh->m_uiMaterialIndex =
728             getMaterialIndex( m_pModel->m_pCurrentMaterial->MaterialName.data );
729         m_pModel->m_pCurrentMesh->m_pMaterial = m_pModel->m_pCurrentMaterial;
730     }
731 }
732 // -------------------------------------------------------------------
733 //  Creates a new mesh
createMesh(const std::string & meshName)734 void ObjFileParser::createMesh( const std::string &meshName )
735 {
736     ai_assert( NULL != m_pModel );
737     m_pModel->m_pCurrentMesh = new ObjFile::Mesh( meshName );
738     m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh );
739     unsigned int meshId = m_pModel->m_Meshes.size()-1;
740     if ( NULL != m_pModel->m_pCurrent )
741     {
742         m_pModel->m_pCurrent->m_Meshes.push_back( meshId );
743     }
744     else
745     {
746         DefaultLogger::get()->error("OBJ: No object detected to attach a new mesh instance.");
747     }
748 }
749 
750 // -------------------------------------------------------------------
751 //  Returns true, if a new mesh must be created.
needsNewMesh(const std::string & rMaterialName)752 bool ObjFileParser::needsNewMesh( const std::string &rMaterialName )
753 {
754     if(m_pModel->m_pCurrentMesh == 0)
755     {
756         // No mesh data yet
757         return true;
758     }
759     bool newMat = false;
760     int matIdx = getMaterialIndex( rMaterialName );
761     int curMatIdx = m_pModel->m_pCurrentMesh->m_uiMaterialIndex;
762     if ( curMatIdx != int(ObjFile::Mesh::NoMaterial) || curMatIdx != matIdx )
763     {
764         // New material -> only one material per mesh, so we need to create a new
765         // material
766         newMat = true;
767     }
768     return newMat;
769 }
770 
771 // -------------------------------------------------------------------
772 //  Shows an error in parsing process.
reportErrorTokenInFace()773 void ObjFileParser::reportErrorTokenInFace()
774 {
775     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
776     DefaultLogger::get()->error("OBJ: Not supported token in face description detected");
777 }
778 
779 // -------------------------------------------------------------------
780 
781 }   // Namespace Assimp
782 
783 #endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
784