#include "ObjFileParser.h" #include "ObjFileMtlImporter.h" #include "ObjTools.h" #include "ObjFileData.h" #include "DefaultIOSystem.h" #include "../include/IOStream.h" #include "../include/aiTypes.h" #include "../include/aiAssert.h" #include "fast_atof.h" #include #include #include namespace Assimp { // ------------------------------------------------------------------- const std::string ObjFileParser::DEFAULT_MATERIAL = "defaultmaterial"; // ------------------------------------------------------------------- // Constructor with loaded data and directories. ObjFileParser::ObjFileParser(std::vector &Data, const std::string &strAbsPath, const std::string &strModelName) : m_strAbsPath(strAbsPath), m_DataIt(Data.begin()), m_DataItEnd(Data.end()), m_pModel(NULL), m_uiLine(0) { // Create the model instance to store all the data m_pModel = new ObjFile::Model(); m_pModel->m_ModelName = strModelName; 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(); } // ------------------------------------------------------------------- ObjFileParser::~ObjFileParser() { // empty } // ------------------------------------------------------------------- ObjFile::Model *ObjFileParser::GetModel() const { return m_pModel; } // ------------------------------------------------------------------- void ObjFileParser::parseFile() { if (m_DataIt == m_DataItEnd) return; while (m_DataIt != m_DataItEnd) { switch (*m_DataIt) { case 'v': // Parse a vertex texture coordinate { ++m_DataIt; if (*m_DataIt == ' ') { // Read in vertex definition getVector3(m_pModel->m_Vertices); } else if (*m_DataIt == 't') { // Read in texture coordinate (2D) ++m_DataIt; getVector2(m_pModel->m_TextureCoord); } else if (*m_DataIt == 'n') { // Read in normal vector definition ++m_DataIt; getVector3(m_pModel->m_Normals); } } break; case 'f': // Parse a face { getFace(); } break; case '#': // Parse a comment { getComment(); } break; case 'u': // Parse a material desc. setter { getMaterialDesc(); } break; case 'm': // Parse a material library { getMaterialLib(); } break; case 'g': // Parse group name { getGroupName(); } break; case 's': // Parse group number { getGroupNumber(); } break; case 'o': // Parse object name { getObjectName(); } break; default: { m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } break; } } } // ------------------------------------------------------------------- // Copy the next word in a temporary buffer void ObjFileParser::copyNextWord(char *pBuffer, size_t length) { size_t index = 0; m_DataIt = getNextWord(m_DataIt, m_DataItEnd); while (!isSpace(*m_DataIt) && m_DataIt != m_DataItEnd) { pBuffer[index] = *m_DataIt; index++; if (index == length-1) break; ++m_DataIt; } pBuffer[index] = '\0'; } // ------------------------------------------------------------------- // Copy the next line into a temporary buffer void ObjFileParser::copyNextLine(char *pBuffer, size_t length) { size_t index = 0; while (m_DataIt != m_DataItEnd) { if (*m_DataIt == '\n' || *m_DataIt == '\r') break; assert (index+1 <= length); pBuffer[ index ] = *m_DataIt; ++index; ++m_DataIt; } pBuffer[ index ] = '\0'; } // ------------------------------------------------------------------- // Get values for a new 3D vector instance void ObjFileParser::getVector3(std::vector &point3d_array) { float x, y, z; copyNextWord(m_buffer, BUFFERSIZE); x = (float) fast_atof(m_buffer); copyNextWord(m_buffer, BUFFERSIZE); y = (float) fast_atof(m_buffer); copyNextWord(m_buffer, BUFFERSIZE); z = (float) fast_atof(m_buffer); point3d_array.push_back(new aiVector3D(x,y,z)); //skipLine(); m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Get values for a new 2D vector instance void ObjFileParser::getVector2( std::vector &point2d_array ) { float x, y; copyNextWord(m_buffer, BUFFERSIZE); x = (float) fast_atof(m_buffer); copyNextWord(m_buffer, BUFFERSIZE); y = (float) fast_atof(m_buffer); point2d_array.push_back(new aiVector2D(x, y)); m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Get values for a new face instance void ObjFileParser::getFace() { copyNextLine(m_buffer, BUFFERSIZE); if (m_DataIt == m_DataItEnd) return; char *pPtr = m_buffer; char *pEnd = &pPtr[BUFFERSIZE]; pPtr = getNextToken(pPtr, pEnd); if (pPtr == '\0') return; std::vector *pIndices = new std::vector; std::vector *pTexID = new std::vector; std::vector *pNormalID = new std::vector; bool vt = (!m_pModel->m_TextureCoord.empty()); bool vn = (!m_pModel->m_Normals.empty()); int iStep = 0, iPos = 0; while (pPtr != pEnd) { iStep = 1; if (*pPtr == '\0') break; if (*pPtr=='\r') break; if (*pPtr=='/' ) { if (iPos == 0) { //if there are no texturecoordinates in the obj file but normals if (!vt && vn) iPos = 1; } iPos++; } else if (isSpace(*pPtr)) { iPos = 0; } else { //OBJ USES 1 Base ARRAYS!!!! const int iVal = atoi( pPtr ); int tmp = iVal; while ((tmp = tmp / 10)!=0) ++iStep; if ( 0 != iVal ) { // Store parsed index if ( 0 == iPos ) { pIndices->push_back( iVal-1 ); } else if ( 1 == iPos ) { pTexID->push_back( iVal-1 ); } else if ( 2 == iPos ) { pNormalID->push_back( iVal-1 ); } else { reportErrorTokenInFace(); } } } for ( int i=0; im_pCurrentMaterial) face->m_pMaterial = m_pModel->m_pCurrentMaterial; else face->m_pMaterial = m_pModel->m_pDefaultMaterial; // Create a default object, if nothing there if ( NULL == m_pModel->m_pCurrent ) createObject("defaultobject"); // Store the new instance m_pModel->m_pCurrent->m_Faces.push_back(face); // Assign face to mesh if ( NULL == m_pModel->m_pCurrentMesh ) { m_pModel->m_pCurrentMesh = new ObjFile::Mesh(); m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh ); } // Store the face m_pModel->m_pCurrentMesh->m_Faces.push_back( face ); m_pModel->m_pCurrentMesh->m_uiNumIndices += (unsigned int)face->m_pVertices->size(); m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += (unsigned int)face->m_pTexturCoords[0].size(); // Skip the rest of the line m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Get values for a new material description void ObjFileParser::getMaterialDesc() { // Get next data for material data m_DataIt = getNextToken(m_DataIt, m_DataItEnd); if (m_DataIt == m_DataItEnd) return; char *pStart = &(*m_DataIt); while ( !isSpace(*m_DataIt) && m_DataIt != m_DataItEnd ) ++m_DataIt; // Get name std::string strName(pStart, &(*m_DataIt)); if ( strName.empty() ) return; // Search for material std::map::iterator it = m_pModel->m_MaterialMap.find( strName ); if ( it == m_pModel->m_MaterialMap.end() ) { // Not found, use default material m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial; m_pModel->m_pCurrentMesh = new ObjFile::Mesh(); m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh ); m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( DEFAULT_MATERIAL ); } else { // Found, using detected material m_pModel->m_pCurrentMaterial = (*it).second; // Create a new mesh for a new material m_pModel->m_pCurrentMesh = new ObjFile::Mesh(); m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh ); m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strName ); } // Skip rest of line m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Get a comment, values will be skipped void ObjFileParser::getComment() { while (true) { if ('\n' == (*m_DataIt) || m_DataIt == m_DataItEnd) { ++m_DataIt; break; } else { ++m_DataIt; } } } // ------------------------------------------------------------------- // Get material library from file. void ObjFileParser::getMaterialLib() { // Translate tuple m_DataIt = getNextToken(m_DataIt, m_DataItEnd); if (m_DataIt == m_DataItEnd) return; char *pStart = &(*m_DataIt); while (!isSpace(*m_DataIt)) m_DataIt++; // Check for existence DefaultIOSystem IOSystem; std::string strMatName(pStart, &(*m_DataIt)); std::string absName = m_strAbsPath + IOSystem.getOsSeparator() + strMatName; if ( !IOSystem.Exists(absName) ) { m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); return; } // Extract the extention std::string strExt(""); extractExtension( strMatName, strExt ); static const std::string mat = "mtl"; // Load the material library DefaultIOSystem FileSystem; IOStream *pFile = FileSystem.Open(absName); if (0L != pFile) { // Import material library data from file size_t size = pFile->FileSize(); std::vector buffer; buffer.resize( size ); pFile->Read( &buffer[ 0 ], sizeof( char ), size ); FileSystem.Close( pFile ); // Importing the material library ObjFileMtlImporter mtlImporter( buffer, absName, m_pModel ); } // Skip rest of line m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Set a new material definition as the current material. void ObjFileParser::getNewMaterial() { m_DataIt = getNextToken(m_DataIt, m_DataItEnd); m_DataIt = getNextWord(m_DataIt, m_DataItEnd); if ( m_DataIt == m_DataItEnd ) return; char *pStart = &(*m_DataIt); std::string strMat(pStart, *m_DataIt); while (isSpace(*m_DataIt)) m_DataIt++; std::map::iterator it = m_pModel->m_MaterialMap.find( strMat ); if (it == m_pModel->m_MaterialMap.end()) { // Show a warning, if material was not found std::string strWarn ("Unsupported material requested: "); strWarn += strMat; std::cerr << "Warning : " << strWarn << std::endl; m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial; } else { // Set new material m_pModel->m_pCurrentMaterial = (*it).second; m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strMat ); } m_DataIt = skipLine( 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() { // Get next word from data buffer m_DataIt = getNextToken(m_DataIt, m_DataItEnd); m_DataIt = getNextWord(m_DataIt, m_DataItEnd); if ( m_DataIt == m_DataItEnd ) return; // Store groupname in group library char *pStart = &(*m_DataIt); while (!isSpace(*m_DataIt)) m_DataIt++; std::string strGroupName(pStart, &(*m_DataIt)); // Change active group, if necessary if (m_pModel->m_strActiveGroup != strGroupName) { // Search for already existing entry ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(&strGroupName); // New group name, creating a new entry //ObjFile::Object *pObject = m_pModel->m_pCurrent; if (it == m_pModel->m_Groups.end()) { std::vector *pFaceIDArray = new std::vector; m_pModel->m_Groups[ &strGroupName ] = pFaceIDArray; m_pModel->m_pGroupFaceIDs = (pFaceIDArray); } else { m_pModel->m_pGroupFaceIDs = (*it).second; } m_pModel->m_strActiveGroup = strGroupName; } m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Not supported void ObjFileParser::getGroupNumber() { // Not used m_DataIt = skipLine( 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(m_DataIt, m_DataItEnd); if (m_DataIt == m_DataItEnd) return; char *pStart = &(*m_DataIt); while (!isSpace(*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::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 wasn´t found before if (m_pModel->m_pCurrent == NULL) { createObject(strObjectName); /*m_pModel->m_pCurrent = new ObjFile::Object(); m_pModel->m_pCurrent->m_strObjName = strObjectName; m_pModel->m_Objects.push_back(m_pModel->m_pCurrent);*/ } } m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); } // ------------------------------------------------------------------- // Creates a new object instance void ObjFileParser::createObject(const std::string &strObjectName) { ai_assert (NULL != m_pModel); ai_assert (!strObjectName.empty()); m_pModel->m_pCurrent = new ObjFile::Object(); m_pModel->m_pCurrent->m_strObjName = strObjectName; m_pModel->m_Objects.push_back(m_pModel->m_pCurrent); } // ------------------------------------------------------------------- // Shows an error in parsing process. void ObjFileParser::reportErrorTokenInFace() { std::string strErr(""); m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); std::cerr << "Not supported token in face desc. detected : " << strErr << std::endl; } // ------------------------------------------------------------------- // Extracts the extention from a filename void ObjFileParser::extractExtension(const std::string &strFile, std::string &strExt) { strExt = ""; if (strFile.empty()) return; // Search for extention delimiter std::string::size_type pos = strFile.find_last_of("."); if ( pos == std::string::npos ) return; strExt = strFile.substr(pos, strFile.size() - pos); } // ------------------------------------------------------------------- } // Namespace Assimp