#include "ObjFileImporter.h" #include "ObjFileParser.h" #include "ObjFileData.h" #include "../include/IOStream.h" #include "../include/IOSystem.h" #include "../include/aiMesh.h" #include "../include/aiScene.h" #include "../include/aiAssert.h" #include "MaterialSystem.h" #include "../include/DefaultLogger.h" #include #include namespace Assimp { // ------------------------------------------------------------------------------------------------ using namespace std; //! Obj-file-format extention const string ObjFileImporter::OBJ_EXT = "obj"; // ------------------------------------------------------------------------------------------------ // Default constructor ObjFileImporter::ObjFileImporter() : m_pRootObject(NULL), m_strAbsPath("\\") { } // ------------------------------------------------------------------------------------------------ // Destructor ObjFileImporter::~ObjFileImporter() { // Release root object instance if (NULL != m_pRootObject) { delete m_pRootObject; m_pRootObject = NULL; } } // ------------------------------------------------------------------------------------------------ // Returns true, fi file is an obj file bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const { if (pFile.empty()) return false; string::size_type pos = pFile.find_last_of("."); if (string::npos == pos) return false; const string ext = pFile.substr(pos+1, pFile.size() - pos - 1); if (ext == OBJ_EXT) return true; return false; } // ------------------------------------------------------------------------------------------------ // Obj-file import implementation void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { // Read file into memory const std::string mode = "rb"; boost::scoped_ptr file( pIOHandler->Open( pFile, mode)); if (NULL == file.get()) throw new ImportErrorException( "Failed to open file " + pFile + "."); // Get the filesize and vaslidate it, throwing an exception when failes size_t fileSize = file->FileSize(); if( fileSize < 16) throw new ImportErrorException( "OBJ-file is too small."); // Allocate buffer and read file into it m_Buffer.resize( fileSize ); const size_t readsize = file->Read(&m_Buffer.front(), sizeof(char), fileSize); assert (readsize == fileSize); // std::string strDirectory("\\"), strModelName; std::string::size_type pos = pFile.find_last_of("\\"); if (pos != std::string::npos) { strDirectory = pFile.substr(0, pos); strModelName = pFile.substr(pos+1, pFile.size() - pos - 1); } else { strModelName = pFile; } // parse the file into a temporary representation ObjFileParser parser(m_Buffer, strDirectory, strModelName); // And create the proper return structures out of it CreateDataFromImport(parser.GetModel(), pScene); } // ------------------------------------------------------------------------------------------------ // Create the data from parsed obj-file void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene) { if (0L == pModel) return; // Create the root node of the scene pScene->mRootNode = new aiNode(); if (!pModel->m_ModelName.empty()) { // Set the name of the scene pScene->mRootNode->mName.Set(pModel->m_ModelName); } else { // This is an error, so break down the application assert (false); } // Create nodes for the whole scene std::vector MeshArray; for (size_t index = 0; index < pModel->m_Objects.size(); index++) { createNodes(pModel, pModel->m_Objects[ index ], pScene->mRootNode, pScene, MeshArray); } // Create mesh pointer buffer for this scene if (pScene->mNumMeshes > 0) { pScene->mMeshes = new aiMesh*[ MeshArray.size() ]; for (size_t index =0; index < MeshArray.size(); index++) { pScene->mMeshes [ index ] = MeshArray[ index ]; } } // Create all materials for (size_t index = 0; index < pModel->m_Objects.size(); index++) { createMaterial( pModel, pModel->m_Objects[ index ], pScene ); } } // ------------------------------------------------------------------------------------------------ // Creates all nodes of the model aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pData, aiNode *pParent, aiScene* pScene, std::vector &MeshArray) { if (NULL == pData) return NULL; // Store older mesh size to be able to computate mesh offsets for new mesh instances size_t oldMeshSize = MeshArray.size(); aiNode *pNode = new aiNode(); if (pParent != NULL) this->appendChildToParentNode(pParent, pNode); aiMesh *pMesh = NULL; for (unsigned int meshIndex = 0; meshIndex < pModel->m_Meshes.size(); meshIndex++) { pMesh = new aiMesh(); MeshArray.push_back( pMesh ); createTopology( pModel, pData, meshIndex, pMesh ); } // Create all nodes from the subobjects stored in the current object if (!pData->m_SubObjects.empty()) { pNode->mNumChildren = (unsigned int)pData->m_SubObjects.size(); pNode->mChildren = new aiNode*[pData->m_SubObjects.size()]; pNode->mNumMeshes = 1; pNode->mMeshes = new unsigned int[1]; // Loop over all child objects, TODO /*for (size_t index = 0; index < pData->m_SubObjects.size(); index++) { // Create all child nodes pNode->mChildren[ index ] = createNodes( pModel, pData, pNode, pScene, MeshArray ); for (unsigned int meshIndex = 0; meshIndex < pData->m_SubObjects[ index ]->m_Meshes.size(); meshIndex++) { pMesh = new aiMesh(); MeshArray.push_back( pMesh ); createTopology( pModel, pData, meshIndex, pMesh ); } // Create material of this object createMaterial(pModel, pData->m_SubObjects[ index ], pScene); }*/ } // Set mesh instances into scene- and node-instances const size_t meshSizeDiff = MeshArray.size()- oldMeshSize; if ( meshSizeDiff > 0 ) { pNode->mMeshes = new unsigned int[ meshSizeDiff ]; pNode->mNumMeshes = meshSizeDiff; size_t index = 0; for (size_t i = oldMeshSize; i < MeshArray.size(); i++) { pNode->mMeshes[ index ] = pScene->mNumMeshes; pScene->mNumMeshes++; index++; } } return pNode; } // ------------------------------------------------------------------------------------------------ // Create topology data void ObjFileImporter::createTopology(const ObjFile::Model* pModel, const ObjFile::Object* pData, unsigned int uiMeshIndex, aiMesh* pMesh ) { // Checking preconditions ai_assert( NULL != pModel ); if (NULL == pData) return; // Create faces ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ]; pMesh->mNumFaces = (unsigned int) pObjMesh->m_Faces.size(); pMesh->mFaces = new aiFace[ pMesh->mNumFaces ]; pMesh->mMaterialIndex = pObjMesh->m_uiMaterialIndex; // Copy all data from all stored meshes for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++) { aiFace *pFace = &pMesh->mFaces[ index ]; const unsigned int uiNumIndices = (unsigned int) pObjMesh->m_Faces[ index ]->m_pVertices->size(); pFace->mNumIndices = (unsigned int) uiNumIndices; if (pFace->mNumIndices > 0) { pFace->mIndices = new unsigned int[ uiNumIndices ]; ObjFile::Face::IndexArray *pIndexArray = pObjMesh->m_Faces[ index ]->m_pVertices; ai_assert ( NULL != pIndexArray ); for ( size_t a=0; amNumIndices; a++ ) { pFace->mIndices[ a ] = pIndexArray->at( a ); } } else { pFace->mIndices = NULL; } } // Create mesh vertices createVertexArray(pModel, pData, uiMeshIndex, pMesh); } // ------------------------------------------------------------------------------------------------ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel, const ObjFile::Object* pCurrentObject, unsigned int uiMeshIndex, aiMesh* pMesh) { // Checking preconditions ai_assert ( NULL != pCurrentObject ); // Break, if no faces are stored in object if (pCurrentObject->m_Faces.empty()) return; // Get current mesh ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ]; if ( NULL == pObjMesh ) return; // Copy vertices of this mesh instance pMesh->mNumVertices = (unsigned int) pObjMesh->m_uiNumIndices; pMesh->mVertices = new aiVector3D[ pMesh->mNumVertices ]; // Allocate buffer for normal vectors if ( !pModel->m_Normals.empty() ) pMesh->mNormals = new aiVector3D[ pMesh->mNumVertices ]; // Allocate buffer for texture coordinates if ( !pModel->m_TextureCoord.empty() ) { for ( size_t i=0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i++) pMesh->mTextureCoords[ i ] = new aiVector3D[ pModel->m_TextureCoord.size() ]; } // Copy vertices, normals and textures into aiMesh instance unsigned int newIndex = 0; for ( size_t index=0; index < pObjMesh->m_Faces.size(); index++ ) { // get destination face aiFace *pDestFace = &pMesh->mFaces[ index ]; // get source face ObjFile::Face *pSourceFace = pObjMesh->m_Faces[ index ]; // Copy all index arrays for ( size_t vertexIndex = 0; vertexIndex < pSourceFace->m_pVertices->size(); vertexIndex++ ) { unsigned int vertex = pSourceFace->m_pVertices->at( vertexIndex ); assert ( vertex < pModel->m_Vertices.size() ); pMesh->mVertices[ newIndex ] = *pModel->m_Vertices[ vertex ]; if ( !pModel->m_Normals.empty() ) { const unsigned int normal = pSourceFace->m_pNormals->at( vertexIndex ); assert( normal < pModel->m_Normals.size() ); pMesh->mNormals[ newIndex ] = *pModel->m_Normals[ normal ]; } if ( !pModel->m_TextureCoord.empty() ) { const unsigned int tex = pSourceFace->m_pTexturCoords->at( vertexIndex ); ai_assert ( tex < pModel->m_TextureCoord.size() ); for ( size_t i=0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i++) { aiVector2D coord2d = *pModel->m_TextureCoord[ tex ]; pMesh->mTextureCoords[ i ][ newIndex ] = aiVector3D( coord2d.x, coord2d.y, 0.0 ); } } assert( pMesh->mNumVertices > newIndex ); pDestFace->mIndices[ vertexIndex ] = newIndex; newIndex++; } } } // ------------------------------------------------------------------------------------------------ void ObjFileImporter::countObjects(const std::vector &rObjects, int &iNumMeshes) { iNumMeshes = 0; if (rObjects.empty()) return; iNumMeshes += (unsigned int)rObjects.size(); for (std::vector::const_iterator it = rObjects.begin(); it != rObjects.end(); ++it) { if (!(*it)->m_SubObjects.empty()) { countObjects((*it)->m_SubObjects, iNumMeshes); } } } // ------------------------------------------------------------------------------------------------ void ObjFileImporter::createMaterial(const ObjFile::Model* pModel, const ObjFile::Object* pData, aiScene* pScene) { ai_assert (NULL != pScene); if (NULL == pData) return; const unsigned int numMaterials = (unsigned int) pModel->m_MaterialLib.size(); pScene->mNumMaterials = 0; if ( pModel->m_MaterialLib.empty() ) return; pScene->mMaterials = new aiMaterial*[ numMaterials ]; for ( unsigned int matIndex = 0; matIndex < numMaterials; matIndex++ ) { Assimp::MaterialHelper* mat = new Assimp::MaterialHelper(); // Store material name std::map::const_iterator it = pModel->m_MaterialMap.find( pModel->m_MaterialLib[ matIndex ] ); // No material found, use the default material if ( pModel->m_MaterialMap.end() == it) continue; ObjFile::Material *pCurrentMaterial = (*it).second; mat->AddProperty( &pCurrentMaterial->MaterialName, AI_MATKEY_NAME ); mat->AddProperty( &pCurrentMaterial->illumination_model, 1, AI_MATKEY_SHADING_MODEL); // Adding material colors mat->AddProperty( &pCurrentMaterial->ambient, 1, AI_MATKEY_COLOR_AMBIENT ); mat->AddProperty( &pCurrentMaterial->diffuse, 1, AI_MATKEY_COLOR_DIFFUSE ); mat->AddProperty( &pCurrentMaterial->specular, 1, AI_MATKEY_COLOR_SPECULAR ); mat->AddProperty( &pCurrentMaterial->shineness, 1, AI_MATKEY_SHININESS ); // Adding textures if ( 0 != pCurrentMaterial->texture.length ) mat->AddProperty( &pCurrentMaterial->texture, AI_MATKEY_TEXTURE_DIFFUSE(0)); // Store material property info in material array in scene pScene->mMaterials[ pScene->mNumMaterials ] = mat; pScene->mNumMaterials++; } // Test number of created materials. ai_assert( pScene->mNumMaterials == numMaterials ); } // ------------------------------------------------------------------------------------------------ // Appends this node to the parent node void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild) { // Checking preconditions ai_assert (NULL != pParent); ai_assert (NULL != pChild); // Assign parent to child pChild->mParent = pParent; size_t sNumChildren = 0; // If already children was assigned to the parent node, store them in a std::vector temp; if (pParent->mChildren != NULL) { sNumChildren = pParent->mNumChildren; ai_assert (0 != sNumChildren); for (size_t index = 0; index < pParent->mNumChildren; index++) { temp.push_back(pParent->mChildren [ index ] ); } delete [] pParent->mChildren; } // Copy node instances into parent node pParent->mNumChildren++; pParent->mChildren = new aiNode*[ pParent->mNumChildren ]; for (size_t index = 0; index < pParent->mNumChildren-1; index++) { pParent->mChildren[ index ] = temp [ index ]; } pParent->mChildren[ pParent->mNumChildren-1 ] = pChild; } // ------------------------------------------------------------------------------------------------ } // Namespace Assimp