| /* |
| --------------------------------------------------------------------------- |
| Open Asset Import Library (assimp) |
| --------------------------------------------------------------------------- |
| |
| Copyright (c) 2006-2017, assimp team |
| |
| |
| All rights reserved. |
| |
| Redistribution and use of this software in source and binary forms, |
| with or without modification, are permitted provided that the following |
| conditions are met: |
| |
| * Redistributions of source code must retain the above |
| copyright notice, this list of conditions and the |
| following disclaimer. |
| |
| * Redistributions in binary form must reproduce the above |
| copyright notice, this list of conditions and the |
| following disclaimer in the documentation and/or other |
| materials provided with the distribution. |
| |
| * Neither the name of the assimp team, nor the names of its |
| contributors may be used to endorse or promote products |
| derived from this software without specific prior |
| written permission of the assimp team. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| --------------------------------------------------------------------------- |
| */ |
| #ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER |
| |
| #include "ObjFileParser.h" |
| #include "ObjFileMtlImporter.h" |
| #include "ObjTools.h" |
| #include "ObjFileData.h" |
| #include "ParsingUtils.h" |
| #include "BaseImporter.h" |
| #include <assimp/DefaultIOSystem.h> |
| #include <assimp/DefaultLogger.hpp> |
| #include <assimp/material.h> |
| #include <assimp/Importer.hpp> |
| #include <cstdlib> |
| |
| namespace Assimp { |
| |
| const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME; |
| |
| ObjFileParser::ObjFileParser() |
| : m_DataIt() |
| , m_DataItEnd() |
| , m_pModel( NULL ) |
| , m_uiLine( 0 ) |
| , m_pIO( nullptr ) |
| , m_progress( nullptr ) |
| , m_originalObjFileName() { |
| // empty |
| } |
| |
| ObjFileParser::ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::string &modelName, |
| IOSystem *io, ProgressHandler* progress, |
| const std::string &originalObjFileName) : |
| m_DataIt(), |
| m_DataItEnd(), |
| m_pModel(NULL), |
| m_uiLine(0), |
| m_pIO( io ), |
| m_progress(progress), |
| m_originalObjFileName(originalObjFileName) |
| { |
| std::fill_n(m_buffer,Buffersize,0); |
| |
| // Create the model instance to store all the data |
| m_pModel = new ObjFile::Model(); |
| m_pModel->m_ModelName = modelName; |
| |
| // create default material and store it |
| m_pModel->m_pDefaultMaterial = new ObjFile::Material; |
| m_pModel->m_pDefaultMaterial->MaterialName.Set( DEFAULT_MATERIAL ); |
| m_pModel->m_MaterialLib.push_back( DEFAULT_MATERIAL ); |
| m_pModel->m_MaterialMap[ DEFAULT_MATERIAL ] = m_pModel->m_pDefaultMaterial; |
| |
| // Start parsing the file |
| parseFile( streamBuffer ); |
| } |
| |
| ObjFileParser::~ObjFileParser() { |
| delete m_pModel; |
| m_pModel = NULL; |
| } |
| |
| void ObjFileParser::setBuffer( std::vector<char> &buffer ) { |
| m_DataIt = buffer.begin(); |
| m_DataItEnd = buffer.end(); |
| } |
| |
| ObjFile::Model *ObjFileParser::GetModel() const { |
| return m_pModel; |
| } |
| |
| void ObjFileParser::parseFile( IOStreamBuffer<char> &streamBuffer ) { |
| // only update every 100KB or it'll be too slow |
| //const unsigned int updateProgressEveryBytes = 100 * 1024; |
| unsigned int progressCounter = 0; |
| const unsigned int bytesToProcess = static_cast<unsigned int>(streamBuffer.size()); |
| const unsigned int progressTotal = 3 * bytesToProcess; |
| const unsigned int progressOffset = bytesToProcess; |
| unsigned int processed = 0; |
| size_t lastFilePos( 0 ); |
| |
| std::vector<char> buffer; |
| while ( streamBuffer.getNextDataLine( buffer, '\\' ) ) { |
| m_DataIt = buffer.begin(); |
| m_DataItEnd = buffer.end(); |
| |
| // Handle progress reporting |
| const size_t filePos( streamBuffer.getFilePos() ); |
| if ( lastFilePos < filePos ) { |
| processed += static_cast<unsigned int>(filePos); |
| lastFilePos = filePos; |
| progressCounter++; |
| m_progress->UpdateFileRead( progressOffset + processed * 2, progressTotal ); |
| } |
| |
| // parse line |
| switch (*m_DataIt) { |
| case 'v': // Parse a vertex texture coordinate |
| { |
| ++m_DataIt; |
| if (*m_DataIt == ' ' || *m_DataIt == '\t') { |
| size_t numComponents = getNumComponentsInDataDefinition(); |
| if (numComponents == 3) { |
| // read in vertex definition |
| getVector3(m_pModel->m_Vertices); |
| } else if (numComponents == 4) { |
| // read in vertex definition (homogeneous coords) |
| getHomogeneousVector3(m_pModel->m_Vertices); |
| } else if (numComponents == 6) { |
| // read vertex and vertex-color |
| getTwoVectors3(m_pModel->m_Vertices, m_pModel->m_VertexColors); |
| } |
| } else if (*m_DataIt == 't') { |
| // read in texture coordinate ( 2D or 3D ) |
| ++m_DataIt; |
| getVector( m_pModel->m_TextureCoord ); |
| } else if (*m_DataIt == 'n') { |
| // Read in normal vector definition |
| ++m_DataIt; |
| getVector3( m_pModel->m_Normals ); |
| } |
| } |
| break; |
| |
| case 'p': // Parse a face, line or point statement |
| case 'l': |
| case 'f': |
| { |
| getFace(*m_DataIt == 'f' ? aiPrimitiveType_POLYGON : (*m_DataIt == 'l' |
| ? aiPrimitiveType_LINE : aiPrimitiveType_POINT)); |
| } |
| break; |
| |
| case '#': // Parse a comment |
| { |
| getComment(); |
| } |
| break; |
| |
| case 'u': // Parse a material desc. setter |
| { |
| std::string name; |
| |
| getNameNoSpace(m_DataIt, m_DataItEnd, name); |
| |
| size_t nextSpace = name.find(" "); |
| if (nextSpace != std::string::npos) |
| name = name.substr(0, nextSpace); |
| |
| if(name == "usemtl") |
| { |
| getMaterialDesc(); |
| } |
| } |
| break; |
| |
| case 'm': // Parse a material library or merging group ('mg') |
| { |
| std::string name; |
| |
| getNameNoSpace(m_DataIt, m_DataItEnd, name); |
| |
| size_t nextSpace = name.find(" "); |
| if (nextSpace != std::string::npos) |
| name = name.substr(0, nextSpace); |
| |
| if (name == "mg") |
| getGroupNumberAndResolution(); |
| else if(name == "mtllib") |
| getMaterialLib(); |
| else |
| goto pf_skip_line; |
| } |
| break; |
| |
| case 'g': // Parse group name |
| { |
| getGroupName(); |
| } |
| break; |
| |
| case 's': // Parse group number |
| { |
| getGroupNumber(); |
| } |
| break; |
| |
| case 'o': // Parse object name |
| { |
| getObjectName(); |
| } |
| break; |
| |
| default: |
| { |
| pf_skip_line: |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| break; |
| } |
| } |
| } |
| |
| void ObjFileParser::copyNextWord(char *pBuffer, size_t length) { |
| size_t index = 0; |
| m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd); |
| if ( *m_DataIt == '\\' ) { |
| m_DataIt++; |
| m_DataIt++; |
| m_DataIt = getNextWord<DataArrayIt>( m_DataIt, m_DataItEnd ); |
| } |
| while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) { |
| pBuffer[index] = *m_DataIt; |
| index++; |
| if( index == length - 1 ) { |
| break; |
| } |
| ++m_DataIt; |
| } |
| |
| ai_assert(index < length); |
| pBuffer[index] = '\0'; |
| } |
| |
| static bool isDataDefinitionEnd( const char *tmp ) { |
| if ( *tmp == '\\' ) { |
| tmp++; |
| if ( IsLineEnd( *tmp ) ) { |
| tmp++; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| size_t ObjFileParser::getNumComponentsInDataDefinition() { |
| size_t numComponents( 0 ); |
| const char* tmp( &m_DataIt[0] ); |
| bool end_of_definition = false; |
| while ( !end_of_definition ) { |
| if ( isDataDefinitionEnd( tmp ) ) { |
| tmp += 2; |
| } else if ( IsLineEnd( *tmp ) ) { |
| end_of_definition = true; |
| } |
| if ( !SkipSpaces( &tmp ) ) { |
| break; |
| } |
| const bool isNum( IsNumeric( *tmp ) ); |
| SkipToken( tmp ); |
| if ( isNum ) { |
| ++numComponents; |
| } |
| if ( !SkipSpaces( &tmp ) ) { |
| break; |
| } |
| } |
| return numComponents; |
| } |
| |
| void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) { |
| size_t numComponents = getNumComponentsInDataDefinition(); |
| ai_real x, y, z; |
| if( 2 == numComponents ) { |
| copyNextWord( m_buffer, Buffersize ); |
| x = ( ai_real ) fast_atof( m_buffer ); |
| |
| copyNextWord( m_buffer, Buffersize ); |
| y = ( ai_real ) fast_atof( m_buffer ); |
| z = 0.0; |
| } else if( 3 == numComponents ) { |
| copyNextWord( m_buffer, Buffersize ); |
| x = ( ai_real ) fast_atof( m_buffer ); |
| |
| copyNextWord( m_buffer, Buffersize ); |
| y = ( ai_real ) fast_atof( m_buffer ); |
| |
| copyNextWord( m_buffer, Buffersize ); |
| z = ( ai_real ) fast_atof( m_buffer ); |
| } else { |
| throw DeadlyImportError( "OBJ: Invalid number of components" ); |
| } |
| point3d_array.push_back( aiVector3D( x, y, z ) ); |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| |
| void ObjFileParser::getVector3( std::vector<aiVector3D> &point3d_array ) { |
| ai_real x, y, z; |
| copyNextWord(m_buffer, Buffersize); |
| x = (ai_real) fast_atof(m_buffer); |
| |
| copyNextWord(m_buffer, Buffersize); |
| y = (ai_real) fast_atof(m_buffer); |
| |
| copyNextWord( m_buffer, Buffersize ); |
| z = ( ai_real ) fast_atof( m_buffer ); |
| |
| point3d_array.push_back( aiVector3D( x, y, z ) ); |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| |
| void ObjFileParser::getHomogeneousVector3( std::vector<aiVector3D> &point3d_array ) { |
| ai_real x, y, z, w; |
| copyNextWord(m_buffer, Buffersize); |
| x = (ai_real) fast_atof(m_buffer); |
| |
| copyNextWord(m_buffer, Buffersize); |
| y = (ai_real) fast_atof(m_buffer); |
| |
| copyNextWord( m_buffer, Buffersize ); |
| z = ( ai_real ) fast_atof( m_buffer ); |
| |
| copyNextWord( m_buffer, Buffersize ); |
| w = ( ai_real ) fast_atof( m_buffer ); |
| |
| ai_assert( w != 0 ); |
| |
| point3d_array.push_back( aiVector3D( x/w, y/w, z/w ) ); |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| |
| void ObjFileParser::getTwoVectors3( std::vector<aiVector3D> &point3d_array_a, std::vector<aiVector3D> &point3d_array_b ) { |
| ai_real x, y, z; |
| copyNextWord(m_buffer, Buffersize); |
| x = (ai_real) fast_atof(m_buffer); |
| |
| copyNextWord(m_buffer, Buffersize); |
| y = (ai_real) fast_atof(m_buffer); |
| |
| copyNextWord( m_buffer, Buffersize ); |
| z = ( ai_real ) fast_atof( m_buffer ); |
| |
| point3d_array_a.push_back( aiVector3D( x, y, z ) ); |
| |
| copyNextWord(m_buffer, Buffersize); |
| x = (ai_real) fast_atof(m_buffer); |
| |
| copyNextWord(m_buffer, Buffersize); |
| y = (ai_real) fast_atof(m_buffer); |
| |
| copyNextWord( m_buffer, Buffersize ); |
| z = ( ai_real ) fast_atof( m_buffer ); |
| |
| point3d_array_b.push_back( aiVector3D( x, y, z ) ); |
| |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| |
| void ObjFileParser::getVector2( std::vector<aiVector2D> &point2d_array ) { |
| ai_real x, y; |
| copyNextWord(m_buffer, Buffersize); |
| x = (ai_real) fast_atof(m_buffer); |
| |
| copyNextWord(m_buffer, Buffersize); |
| y = (ai_real) fast_atof(m_buffer); |
| |
| point2d_array.push_back(aiVector2D(x, y)); |
| |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| |
| static const std::string DefaultObjName = "defaultobject"; |
| |
| void ObjFileParser::getFace( aiPrimitiveType type ) { |
| m_DataIt = getNextToken<DataArrayIt>( m_DataIt, m_DataItEnd ); |
| if ( m_DataIt == m_DataItEnd || *m_DataIt == '\0' ) { |
| return; |
| } |
| |
| ObjFile::Face *face = new ObjFile::Face( type ); |
| bool hasNormal = false; |
| |
| const int vSize = static_cast<unsigned int>(m_pModel->m_Vertices.size()); |
| const int vtSize = static_cast<unsigned int>(m_pModel->m_TextureCoord.size()); |
| const int vnSize = static_cast<unsigned int>(m_pModel->m_Normals.size()); |
| |
| const bool vt = (!m_pModel->m_TextureCoord.empty()); |
| const bool vn = (!m_pModel->m_Normals.empty()); |
| int iStep = 0, iPos = 0; |
| while ( m_DataIt != m_DataItEnd ) { |
| iStep = 1; |
| |
| if ( IsLineEnd( *m_DataIt ) ) { |
| break; |
| } |
| |
| if ( *m_DataIt =='/' ) { |
| if (type == aiPrimitiveType_POINT) { |
| DefaultLogger::get()->error("Obj: Separator unexpected in point statement"); |
| } |
| if (iPos == 0) { |
| //if there are no texture coordinates in the file, but normals |
| if (!vt && vn) { |
| iPos = 1; |
| iStep++; |
| } |
| } |
| iPos++; |
| } else if( IsSpaceOrNewLine( *m_DataIt ) ) { |
| iPos = 0; |
| } else { |
| //OBJ USES 1 Base ARRAYS!!!! |
| const int iVal( ::atoi( & ( *m_DataIt ) ) ); |
| |
| // increment iStep position based off of the sign and # of digits |
| int tmp = iVal; |
| if ( iVal < 0 ) { |
| ++iStep; |
| } |
| while ( ( tmp = tmp / 10 ) != 0 ) { |
| ++iStep; |
| } |
| |
| if ( iVal > 0 ) { |
| // Store parsed index |
| if ( 0 == iPos ) { |
| face->m_vertices.push_back( iVal - 1 ); |
| } else if ( 1 == iPos ) { |
| face->m_texturCoords.push_back( iVal - 1 ); |
| } else if ( 2 == iPos ) { |
| face->m_normals.push_back( iVal - 1 ); |
| hasNormal = true; |
| } else { |
| reportErrorTokenInFace(); |
| } |
| } else if ( iVal < 0 ) { |
| // Store relatively index |
| if ( 0 == iPos ) { |
| face->m_vertices.push_back( vSize + iVal ); |
| } else if ( 1 == iPos ) { |
| face->m_texturCoords.push_back( vtSize + iVal ); |
| } else if ( 2 == iPos ) { |
| face->m_normals.push_back( vnSize + iVal ); |
| hasNormal = true; |
| } else { |
| reportErrorTokenInFace(); |
| } |
| } else { |
| //On error, std::atoi will return 0 which is not a valid value |
| delete face; |
| delete m_pModel; |
| m_pModel = nullptr; |
| throw DeadlyImportError("OBJ: Invalid face indice"); |
| } |
| |
| } |
| m_DataIt += iStep; |
| } |
| |
| if ( face->m_vertices.empty() ) { |
| DefaultLogger::get()->error("Obj: Ignoring empty face"); |
| // skip line and clean up |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| delete face; |
| return; |
| } |
| |
| // Set active material, if one set |
| if( NULL != m_pModel->m_pCurrentMaterial ) { |
| face->m_pMaterial = m_pModel->m_pCurrentMaterial; |
| } else { |
| face->m_pMaterial = m_pModel->m_pDefaultMaterial; |
| } |
| |
| // Create a default object, if nothing is there |
| if( NULL == m_pModel->m_pCurrent ) { |
| createObject( DefaultObjName ); |
| } |
| |
| // Assign face to mesh |
| if ( NULL == m_pModel->m_pCurrentMesh ) { |
| createMesh( DefaultObjName ); |
| } |
| |
| // Store the face |
| m_pModel->m_pCurrentMesh->m_Faces.push_back( face ); |
| m_pModel->m_pCurrentMesh->m_uiNumIndices += (unsigned int) face->m_vertices.size(); |
| m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += (unsigned int) face->m_texturCoords.size(); |
| if( !m_pModel->m_pCurrentMesh->m_hasNormals && hasNormal ) { |
| m_pModel->m_pCurrentMesh->m_hasNormals = true; |
| } |
| // Skip the rest of the line |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| |
| void ObjFileParser::getMaterialDesc() { |
| // Get next data for material data |
| m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
| if (m_DataIt == m_DataItEnd) { |
| return; |
| } |
| |
| char *pStart = &(*m_DataIt); |
| while( m_DataIt != m_DataItEnd && !IsLineEnd( *m_DataIt ) ) { |
| ++m_DataIt; |
| } |
| |
| // In some cases we should ignore this 'usemtl' command, this variable helps us to do so |
| bool skip = false; |
| |
| // Get name |
| std::string strName(pStart, &(*m_DataIt)); |
| strName = trim_whitespaces(strName); |
| if (strName.empty()) |
| skip = true; |
| |
| // If the current mesh has the same material, we simply ignore that 'usemtl' command |
| // There is no need to create another object or even mesh here |
| if ( m_pModel->m_pCurrentMaterial && m_pModel->m_pCurrentMaterial->MaterialName == aiString( strName ) ) { |
| skip = true; |
| } |
| |
| if (!skip) { |
| // Search for material |
| std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find(strName); |
| if (it == m_pModel->m_MaterialMap.end()) { |
| // Not found, so we don't know anything about the material except for its name. |
| // This may be the case if the material library is missing. We don't want to lose all |
| // materials if that happens, so create a new named material instead of discarding it |
| // completely. |
| DefaultLogger::get()->error("OBJ: failed to locate material " + strName + ", creating new material"); |
| m_pModel->m_pCurrentMaterial = new ObjFile::Material(); |
| m_pModel->m_pCurrentMaterial->MaterialName.Set(strName); |
| m_pModel->m_MaterialLib.push_back(strName); |
| m_pModel->m_MaterialMap[strName] = m_pModel->m_pCurrentMaterial; |
| } else { |
| // Found, using detected material |
| m_pModel->m_pCurrentMaterial = (*it).second; |
| } |
| |
| if ( needsNewMesh( strName ) ) { |
| createMesh( strName ); |
| } |
| |
| m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex(strName); |
| } |
| |
| // Skip rest of line |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // Get a comment, values will be skipped |
| void ObjFileParser::getComment() { |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // Get material library from file. |
| void ObjFileParser::getMaterialLib() { |
| // Translate tuple |
| m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
| if( m_DataIt == m_DataItEnd ) { |
| return; |
| } |
| |
| char *pStart = &(*m_DataIt); |
| while( m_DataIt != m_DataItEnd && !IsLineEnd( *m_DataIt ) ) { |
| ++m_DataIt; |
| } |
| |
| // Check for existence |
| const std::string strMatName(pStart, &(*m_DataIt)); |
| std::string absName; |
| |
| // Check if directive is valid. |
| if ( 0 == strMatName.length() ) { |
| DefaultLogger::get()->warn( "OBJ: no name for material library specified." ); |
| return; |
| } |
| |
| if ( m_pIO->StackSize() > 0 ) { |
| std::string path = m_pIO->CurrentDirectory(); |
| if ( '/' != *path.rbegin() ) { |
| path += '/'; |
| } |
| absName = path + strMatName; |
| } else { |
| absName = strMatName; |
| } |
| IOStream *pFile = m_pIO->Open( absName ); |
| |
| if (!pFile ) { |
| DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName); |
| std::string strMatFallbackName = m_originalObjFileName.substr(0, m_originalObjFileName.length() - 3) + "mtl"; |
| DefaultLogger::get()->info("OBJ: Opening fallback material file " + strMatFallbackName); |
| pFile = m_pIO->Open(strMatFallbackName); |
| if (!pFile) { |
| DefaultLogger::get()->error("OBJ: Unable to locate fallback material file " + strMatFallbackName); |
| m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
| return; |
| } |
| } |
| |
| // Import material library data from file. |
| // Some exporters (e.g. Silo) will happily write out empty |
| // material files if the model doesn't use any materials, so we |
| // allow that. |
| std::vector<char> buffer; |
| BaseImporter::TextFileToBuffer( pFile, buffer, BaseImporter::ALLOW_EMPTY ); |
| m_pIO->Close( pFile ); |
| |
| // Importing the material library |
| ObjFileMtlImporter mtlImporter( buffer, strMatName, m_pModel ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // Set a new material definition as the current material. |
| void ObjFileParser::getNewMaterial() { |
| m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
| m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd); |
| if( m_DataIt == m_DataItEnd ) { |
| return; |
| } |
| |
| char *pStart = &(*m_DataIt); |
| std::string strMat( pStart, *m_DataIt ); |
| while( m_DataIt != m_DataItEnd && IsSpaceOrNewLine( *m_DataIt ) ) { |
| ++m_DataIt; |
| } |
| std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strMat ); |
| if ( it == m_pModel->m_MaterialMap.end() ) { |
| // Show a warning, if material was not found |
| DefaultLogger::get()->warn("OBJ: Unsupported material requested: " + strMat); |
| m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial; |
| } else { |
| // Set new material |
| if ( needsNewMesh( strMat ) ) { |
| createMesh( strMat ); |
| } |
| m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strMat ); |
| } |
| |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| |
| // ------------------------------------------------------------------- |
| int ObjFileParser::getMaterialIndex( const std::string &strMaterialName ) |
| { |
| int mat_index = -1; |
| if( strMaterialName.empty() ) { |
| return mat_index; |
| } |
| for (size_t index = 0; index < m_pModel->m_MaterialLib.size(); ++index) |
| { |
| if ( strMaterialName == m_pModel->m_MaterialLib[ index ]) |
| { |
| mat_index = (int)index; |
| break; |
| } |
| } |
| return mat_index; |
| } |
| |
| // ------------------------------------------------------------------- |
| // Getter for a group name. |
| void ObjFileParser::getGroupName() { |
| std::string groupName; |
| |
| // here we skip 'g ' from line |
| m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
| m_DataIt = getName<DataArrayIt>(m_DataIt, m_DataItEnd, groupName); |
| if( isEndOfBuffer( m_DataIt, m_DataItEnd ) ) { |
| return; |
| } |
| |
| // Change active group, if necessary |
| if ( m_pModel->m_strActiveGroup != groupName ) { |
| // Search for already existing entry |
| ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(groupName); |
| |
| // We are mapping groups into the object structure |
| createObject( groupName ); |
| |
| // New group name, creating a new entry |
| if (it == m_pModel->m_Groups.end()) |
| { |
| std::vector<unsigned int> *pFaceIDArray = new std::vector<unsigned int>; |
| m_pModel->m_Groups[ groupName ] = pFaceIDArray; |
| m_pModel->m_pGroupFaceIDs = (pFaceIDArray); |
| } |
| else |
| { |
| m_pModel->m_pGroupFaceIDs = (*it).second; |
| } |
| m_pModel->m_strActiveGroup = groupName; |
| } |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // Not supported |
| void ObjFileParser::getGroupNumber() |
| { |
| // Not used |
| |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // Not supported |
| void ObjFileParser::getGroupNumberAndResolution() |
| { |
| // Not used |
| |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // Stores values for a new object instance, name will be used to |
| // identify it. |
| void ObjFileParser::getObjectName() |
| { |
| m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
| if( m_DataIt == m_DataItEnd ) { |
| return; |
| } |
| char *pStart = &(*m_DataIt); |
| while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) { |
| ++m_DataIt; |
| } |
| |
| std::string strObjectName(pStart, &(*m_DataIt)); |
| if (!strObjectName.empty()) |
| { |
| // Reset current object |
| m_pModel->m_pCurrent = NULL; |
| |
| // Search for actual object |
| for (std::vector<ObjFile::Object*>::const_iterator it = m_pModel->m_Objects.begin(); |
| it != m_pModel->m_Objects.end(); |
| ++it) |
| { |
| if ((*it)->m_strObjName == strObjectName) |
| { |
| m_pModel->m_pCurrent = *it; |
| break; |
| } |
| } |
| |
| // Allocate a new object, if current one was not found before |
| if( NULL == m_pModel->m_pCurrent ) { |
| createObject( strObjectName ); |
| } |
| } |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| // ------------------------------------------------------------------- |
| // Creates a new object instance |
| void ObjFileParser::createObject(const std::string &objName) |
| { |
| ai_assert( NULL != m_pModel ); |
| |
| m_pModel->m_pCurrent = new ObjFile::Object; |
| m_pModel->m_pCurrent->m_strObjName = objName; |
| m_pModel->m_Objects.push_back( m_pModel->m_pCurrent ); |
| |
| createMesh( objName ); |
| |
| if( m_pModel->m_pCurrentMaterial ) |
| { |
| m_pModel->m_pCurrentMesh->m_uiMaterialIndex = |
| getMaterialIndex( m_pModel->m_pCurrentMaterial->MaterialName.data ); |
| m_pModel->m_pCurrentMesh->m_pMaterial = m_pModel->m_pCurrentMaterial; |
| } |
| } |
| // ------------------------------------------------------------------- |
| // Creates a new mesh |
| void ObjFileParser::createMesh( const std::string &meshName ) |
| { |
| ai_assert( NULL != m_pModel ); |
| m_pModel->m_pCurrentMesh = new ObjFile::Mesh( meshName ); |
| m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh ); |
| unsigned int meshId = static_cast<unsigned int>(m_pModel->m_Meshes.size()-1); |
| if ( NULL != m_pModel->m_pCurrent ) |
| { |
| m_pModel->m_pCurrent->m_Meshes.push_back( meshId ); |
| } |
| else |
| { |
| DefaultLogger::get()->error("OBJ: No object detected to attach a new mesh instance."); |
| } |
| } |
| |
| // ------------------------------------------------------------------- |
| // Returns true, if a new mesh must be created. |
| bool ObjFileParser::needsNewMesh( const std::string &materialName ) |
| { |
| // If no mesh data yet |
| if(m_pModel->m_pCurrentMesh == 0) |
| { |
| return true; |
| } |
| bool newMat = false; |
| int matIdx = getMaterialIndex( materialName ); |
| int curMatIdx = m_pModel->m_pCurrentMesh->m_uiMaterialIndex; |
| if ( curMatIdx != int(ObjFile::Mesh::NoMaterial) |
| && curMatIdx != matIdx |
| // no need create a new mesh if no faces in current |
| // lets say 'usemtl' goes straight after 'g' |
| && m_pModel->m_pCurrentMesh->m_Faces.size() > 0 ) |
| { |
| // New material -> only one material per mesh, so we need to create a new |
| // material |
| newMat = true; |
| } |
| return newMat; |
| } |
| |
| // ------------------------------------------------------------------- |
| // Shows an error in parsing process. |
| void ObjFileParser::reportErrorTokenInFace() |
| { |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| DefaultLogger::get()->error("OBJ: Not supported token in face description detected"); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| } // Namespace Assimp |
| |
| #endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER |