assimp/code/ObjFileParser.cpp

606 lines
16 KiB
C++
Raw Blame History

#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 <iostream>
#include <vector>
#include <cassert>
namespace Assimp
{
// -------------------------------------------------------------------
const std::string ObjFileParser::DEFAULT_MATERIAL = "defaultmaterial";
// -------------------------------------------------------------------
// Constructor with loaded data and directories.
ObjFileParser::ObjFileParser(std::vector<char> &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<DataArrayIt>( 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<DataArrayIt>(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<aiVector3D*> &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<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
}
// -------------------------------------------------------------------
// Get values for a new 2D vector instance
void ObjFileParser::getVector2( std::vector<aiVector2D*> &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<DataArrayIt>( 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<char*>(pPtr, pEnd);
if (pPtr == '\0')
return;
std::vector<unsigned int> *pIndices = new std::vector<unsigned int>;
std::vector<unsigned int> *pTexID = new std::vector<unsigned int>;
std::vector<unsigned int> *pNormalID = new std::vector<unsigned int>;
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;
iStep++;
}
}
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; i<iStep; i++ )
++pPtr;
}
ObjFile::Face *face = new ObjFile::Face(pIndices, pNormalID, pTexID);
// 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 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<DataArrayIt>( 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<DataArrayIt>(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<std::string, ObjFile::Material*>::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<DataArrayIt>( 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<DataArrayIt>(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<DataArrayIt>( 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<char> 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<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
}
// -------------------------------------------------------------------
// 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 (isSpace(*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
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<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()
{
// Get next word from data buffer
m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
m_DataIt = getNextWord<DataArrayIt>(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<unsigned int> *pFaceIDArray = new std::vector<unsigned int>;
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<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
}
// -------------------------------------------------------------------
// Not supported
void ObjFileParser::getGroupNumber()
{
// 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 (!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<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 wasn<73>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<DataArrayIt>( 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<DataArrayIt>( 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