| /* |
| --------------------------------------------------------------------------- |
| 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. |
| --------------------------------------------------------------------------- |
| */ |
| |
| /** @file Implementation of the IrrMesh importer class */ |
| |
| |
| |
| #ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER |
| |
| #include "IRRMeshLoader.h" |
| #include "ParsingUtils.h" |
| #include "fast_atof.h" |
| #include <memory> |
| #include <assimp/IOSystem.hpp> |
| #include <assimp/mesh.h> |
| #include <assimp/DefaultLogger.hpp> |
| #include <assimp/material.h> |
| #include <assimp/scene.h> |
| #include <assimp/importerdesc.h> |
| #include "Macros.h" |
| |
| using namespace Assimp; |
| using namespace irr; |
| using namespace irr::io; |
| |
| static const aiImporterDesc desc = { |
| "Irrlicht Mesh Reader", |
| "", |
| "", |
| "http://irrlicht.sourceforge.net/", |
| aiImporterFlags_SupportTextFlavour, |
| 0, |
| 0, |
| 0, |
| 0, |
| "xml irrmesh" |
| }; |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Constructor to be privately used by Importer |
| IRRMeshImporter::IRRMeshImporter() |
| {} |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Destructor, private as well |
| IRRMeshImporter::~IRRMeshImporter() |
| {} |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Returns whether the class can handle the format of the given file. |
| bool IRRMeshImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const |
| { |
| /* NOTE: A simple check for the file extension is not enough |
| * here. Irrmesh and irr are easy, but xml is too generic |
| * and could be collada, too. So we need to open the file and |
| * search for typical tokens. |
| */ |
| const std::string extension = GetExtension(pFile); |
| |
| if (extension == "irrmesh")return true; |
| else if (extension == "xml" || checkSig) |
| { |
| /* If CanRead() is called to check whether the loader |
| * supports a specific file extension in general we |
| * must return true here. |
| */ |
| if (!pIOHandler)return true; |
| const char* tokens[] = {"irrmesh"}; |
| return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); |
| } |
| return false; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Get a list of all file extensions which are handled by this class |
| const aiImporterDesc* IRRMeshImporter::GetInfo () const |
| { |
| return &desc; |
| } |
| |
| static void releaseMaterial( aiMaterial **mat ) { |
| if(*mat!= nullptr) { |
| delete *mat; |
| *mat = nullptr; |
| } |
| } |
| |
| static void releaseMesh( aiMesh **mesh ) { |
| if (*mesh != nullptr){ |
| delete *mesh; |
| *mesh = nullptr; |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Imports the given file into the given scene structure. |
| void IRRMeshImporter::InternReadFile( const std::string& pFile, |
| aiScene* pScene, IOSystem* pIOHandler) |
| { |
| std::unique_ptr<IOStream> file( pIOHandler->Open( pFile)); |
| |
| // Check whether we can read from the file |
| if( file.get() == NULL) |
| throw DeadlyImportError( "Failed to open IRRMESH file " + pFile + ""); |
| |
| // Construct the irrXML parser |
| CIrrXML_IOStreamReader st(file.get()); |
| reader = createIrrXMLReader((IFileReadCallBack*) &st); |
| |
| // final data |
| std::vector<aiMaterial*> materials; |
| std::vector<aiMesh*> meshes; |
| materials.reserve (5); |
| meshes.reserve(5); |
| |
| // temporary data - current mesh buffer |
| aiMaterial* curMat = nullptr; |
| aiMesh* curMesh = nullptr; |
| unsigned int curMatFlags = 0; |
| |
| std::vector<aiVector3D> curVertices,curNormals,curTangents,curBitangents; |
| std::vector<aiColor4D> curColors; |
| std::vector<aiVector3D> curUVs,curUV2s; |
| |
| // some temporary variables |
| int textMeaning = 0; |
| int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents |
| bool useColors = false; |
| |
| // Parse the XML file |
| while (reader->read()) { |
| switch (reader->getNodeType()) { |
| case EXN_ELEMENT: |
| |
| if (!ASSIMP_stricmp(reader->getNodeName(),"buffer") && (curMat || curMesh)) { |
| // end of previous buffer. A material and a mesh should be there |
| if ( !curMat || !curMesh) { |
| DefaultLogger::get()->error("IRRMESH: A buffer must contain a mesh and a material"); |
| releaseMaterial( &curMat ); |
| releaseMesh( &curMesh ); |
| } else { |
| materials.push_back(curMat); |
| meshes.push_back(curMesh); |
| } |
| curMat = nullptr; |
| curMesh = nullptr; |
| |
| curVertices.clear(); |
| curColors.clear(); |
| curNormals.clear(); |
| curUV2s.clear(); |
| curUVs.clear(); |
| curTangents.clear(); |
| curBitangents.clear(); |
| } |
| |
| |
| if (!ASSIMP_stricmp(reader->getNodeName(),"material")) { |
| if (curMat) { |
| DefaultLogger::get()->warn("IRRMESH: Only one material description per buffer, please"); |
| releaseMaterial( &curMat ); |
| } |
| curMat = ParseMaterial(curMatFlags); |
| } |
| /* no else here! */ if (!ASSIMP_stricmp(reader->getNodeName(),"vertices")) |
| { |
| int num = reader->getAttributeValueAsInt("vertexCount"); |
| |
| if (!num) { |
| // This is possible ... remove the mesh from the list and skip further reading |
| DefaultLogger::get()->warn("IRRMESH: Found mesh with zero vertices"); |
| |
| releaseMaterial( &curMat ); |
| releaseMesh( &curMesh ); |
| textMeaning = 0; |
| continue; |
| } |
| |
| curVertices.reserve(num); |
| curNormals.reserve(num); |
| curColors.reserve(num); |
| curUVs.reserve(num); |
| |
| // Determine the file format |
| const char* t = reader->getAttributeValueSafe("type"); |
| if (!ASSIMP_stricmp("2tcoords", t)) { |
| curUV2s.reserve (num); |
| vertexFormat = 1; |
| |
| if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) { |
| // ********************************************************* |
| // We have a second texture! So use this UV channel |
| // for it. The 2nd texture can be either a normal |
| // texture (solid_2layer or lightmap_xxx) or a normal |
| // map (normal_..., parallax_...) |
| // ********************************************************* |
| int idx = 1; |
| aiMaterial* mat = ( aiMaterial* ) curMat; |
| |
| if (curMatFlags & AI_IRRMESH_MAT_lightmap){ |
| mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_LIGHTMAP(0)); |
| } |
| else if (curMatFlags & AI_IRRMESH_MAT_normalmap_solid){ |
| mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_NORMALS(0)); |
| } |
| else if (curMatFlags & AI_IRRMESH_MAT_solid_2layer) { |
| mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_DIFFUSE(1)); |
| } |
| } |
| } |
| else if (!ASSIMP_stricmp("tangents", t)) { |
| curTangents.reserve (num); |
| curBitangents.reserve (num); |
| vertexFormat = 2; |
| } |
| else if (ASSIMP_stricmp("standard", t)) { |
| releaseMaterial( &curMat ); |
| DefaultLogger::get()->warn("IRRMESH: Unknown vertex format"); |
| } |
| else vertexFormat = 0; |
| textMeaning = 1; |
| } |
| else if (!ASSIMP_stricmp(reader->getNodeName(),"indices")) { |
| if (curVertices.empty() && curMat) { |
| releaseMaterial( &curMat ); |
| throw DeadlyImportError("IRRMESH: indices must come after vertices"); |
| } |
| |
| textMeaning = 2; |
| |
| // start a new mesh |
| curMesh = new aiMesh(); |
| |
| // allocate storage for all faces |
| curMesh->mNumVertices = reader->getAttributeValueAsInt("indexCount"); |
| if (!curMesh->mNumVertices) { |
| // This is possible ... remove the mesh from the list and skip further reading |
| DefaultLogger::get()->warn("IRRMESH: Found mesh with zero indices"); |
| |
| // mesh - away |
| releaseMesh( &curMesh ); |
| |
| // material - away |
| releaseMaterial( &curMat ); |
| |
| textMeaning = 0; |
| continue; |
| } |
| |
| if (curMesh->mNumVertices % 3) { |
| DefaultLogger::get()->warn("IRRMESH: Number if indices isn't divisible by 3"); |
| } |
| |
| curMesh->mNumFaces = curMesh->mNumVertices / 3; |
| curMesh->mFaces = new aiFace[curMesh->mNumFaces]; |
| |
| // setup some members |
| curMesh->mMaterialIndex = (unsigned int)materials.size(); |
| curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; |
| |
| // allocate storage for all vertices |
| curMesh->mVertices = new aiVector3D[curMesh->mNumVertices]; |
| |
| if (curNormals.size() == curVertices.size()) { |
| curMesh->mNormals = new aiVector3D[curMesh->mNumVertices]; |
| } |
| if (curTangents.size() == curVertices.size()) { |
| curMesh->mTangents = new aiVector3D[curMesh->mNumVertices]; |
| } |
| if (curBitangents.size() == curVertices.size()) { |
| curMesh->mBitangents = new aiVector3D[curMesh->mNumVertices]; |
| } |
| if (curColors.size() == curVertices.size() && useColors) { |
| curMesh->mColors[0] = new aiColor4D[curMesh->mNumVertices]; |
| } |
| if (curUVs.size() == curVertices.size()) { |
| curMesh->mTextureCoords[0] = new aiVector3D[curMesh->mNumVertices]; |
| } |
| if (curUV2s.size() == curVertices.size()) { |
| curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices]; |
| } |
| } |
| break; |
| |
| case EXN_TEXT: |
| { |
| const char* sz = reader->getNodeData(); |
| if (textMeaning == 1) { |
| textMeaning = 0; |
| |
| // read vertices |
| do { |
| SkipSpacesAndLineEnd(&sz); |
| aiVector3D temp;aiColor4D c; |
| |
| // Read the vertex position |
| sz = fast_atoreal_move<float>(sz,(float&)temp.x); |
| SkipSpaces(&sz); |
| |
| sz = fast_atoreal_move<float>(sz,(float&)temp.y); |
| SkipSpaces(&sz); |
| |
| sz = fast_atoreal_move<float>(sz,(float&)temp.z); |
| SkipSpaces(&sz); |
| curVertices.push_back(temp); |
| |
| // Read the vertex normals |
| sz = fast_atoreal_move<float>(sz,(float&)temp.x); |
| SkipSpaces(&sz); |
| |
| sz = fast_atoreal_move<float>(sz,(float&)temp.y); |
| SkipSpaces(&sz); |
| |
| sz = fast_atoreal_move<float>(sz,(float&)temp.z); |
| SkipSpaces(&sz); |
| curNormals.push_back(temp); |
| |
| // read the vertex colors |
| uint32_t clr = strtoul16(sz,&sz); |
| ColorFromARGBPacked(clr,c); |
| |
| if (!curColors.empty() && c != *(curColors.end()-1)) |
| useColors = true; |
| |
| curColors.push_back(c); |
| SkipSpaces(&sz); |
| |
| |
| // read the first UV coordinate set |
| sz = fast_atoreal_move<float>(sz,(float&)temp.x); |
| SkipSpaces(&sz); |
| |
| sz = fast_atoreal_move<float>(sz,(float&)temp.y); |
| SkipSpaces(&sz); |
| temp.z = 0.f; |
| temp.y = 1.f - temp.y; // DX to OGL |
| curUVs.push_back(temp); |
| |
| // read the (optional) second UV coordinate set |
| if (vertexFormat == 1) { |
| sz = fast_atoreal_move<float>(sz,(float&)temp.x); |
| SkipSpaces(&sz); |
| |
| sz = fast_atoreal_move<float>(sz,(float&)temp.y); |
| temp.y = 1.f - temp.y; // DX to OGL |
| curUV2s.push_back(temp); |
| } |
| // read optional tangent and bitangent vectors |
| else if (vertexFormat == 2) { |
| // tangents |
| sz = fast_atoreal_move<float>(sz,(float&)temp.x); |
| SkipSpaces(&sz); |
| |
| sz = fast_atoreal_move<float>(sz,(float&)temp.z); |
| SkipSpaces(&sz); |
| |
| sz = fast_atoreal_move<float>(sz,(float&)temp.y); |
| SkipSpaces(&sz); |
| temp.y *= -1.0f; |
| curTangents.push_back(temp); |
| |
| // bitangents |
| sz = fast_atoreal_move<float>(sz,(float&)temp.x); |
| SkipSpaces(&sz); |
| |
| sz = fast_atoreal_move<float>(sz,(float&)temp.z); |
| SkipSpaces(&sz); |
| |
| sz = fast_atoreal_move<float>(sz,(float&)temp.y); |
| SkipSpaces(&sz); |
| temp.y *= -1.0f; |
| curBitangents.push_back(temp); |
| } |
| } |
| |
| /* IMPORTANT: We assume that each vertex is specified in one |
| line. So we can skip the rest of the line - unknown vertex |
| elements are ignored. |
| */ |
| |
| while (SkipLine(&sz)); |
| } |
| else if (textMeaning == 2) { |
| textMeaning = 0; |
| |
| // read indices |
| aiFace* curFace = curMesh->mFaces; |
| aiFace* const faceEnd = curMesh->mFaces + curMesh->mNumFaces; |
| |
| aiVector3D* pcV = curMesh->mVertices; |
| aiVector3D* pcN = curMesh->mNormals; |
| aiVector3D* pcT = curMesh->mTangents; |
| aiVector3D* pcB = curMesh->mBitangents; |
| aiColor4D* pcC0 = curMesh->mColors[0]; |
| aiVector3D* pcT0 = curMesh->mTextureCoords[0]; |
| aiVector3D* pcT1 = curMesh->mTextureCoords[1]; |
| |
| unsigned int curIdx = 0; |
| unsigned int total = 0; |
| while(SkipSpacesAndLineEnd(&sz)) { |
| if (curFace >= faceEnd) { |
| DefaultLogger::get()->error("IRRMESH: Too many indices"); |
| break; |
| } |
| if (!curIdx) { |
| curFace->mNumIndices = 3; |
| curFace->mIndices = new unsigned int[3]; |
| } |
| |
| unsigned int idx = strtoul10(sz,&sz); |
| if (idx >= curVertices.size()) { |
| DefaultLogger::get()->error("IRRMESH: Index out of range"); |
| idx = 0; |
| } |
| |
| curFace->mIndices[curIdx] = total++; |
| |
| *pcV++ = curVertices[idx]; |
| if (pcN)*pcN++ = curNormals[idx]; |
| if (pcT)*pcT++ = curTangents[idx]; |
| if (pcB)*pcB++ = curBitangents[idx]; |
| if (pcC0)*pcC0++ = curColors[idx]; |
| if (pcT0)*pcT0++ = curUVs[idx]; |
| if (pcT1)*pcT1++ = curUV2s[idx]; |
| |
| if (++curIdx == 3) { |
| ++curFace; |
| curIdx = 0; |
| } |
| } |
| |
| if (curFace != faceEnd) |
| DefaultLogger::get()->error("IRRMESH: Not enough indices"); |
| |
| // Finish processing the mesh - do some small material workarounds |
| if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) { |
| // Take the opacity value of the current material |
| // from the common vertex color alpha |
| aiMaterial* mat = (aiMaterial*)curMat; |
| mat->AddProperty(&curColors[0].a,1,AI_MATKEY_OPACITY); |
| } |
| }} |
| break; |
| |
| default: |
| // GCC complains here ... |
| break; |
| |
| }; |
| } |
| |
| // End of the last buffer. A material and a mesh should be there |
| if (curMat || curMesh) { |
| if ( !curMat || !curMesh) { |
| DefaultLogger::get()->error("IRRMESH: A buffer must contain a mesh and a material"); |
| releaseMaterial( &curMat ); |
| releaseMesh( &curMesh ); |
| } |
| else { |
| materials.push_back(curMat); |
| meshes.push_back(curMesh); |
| } |
| } |
| |
| if (materials.empty()) |
| throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file"); |
| |
| |
| // now generate the output scene |
| pScene->mNumMeshes = (unsigned int)meshes.size(); |
| pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; |
| for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { |
| pScene->mMeshes[i] = meshes[i]; |
| |
| // clean this value ... |
| pScene->mMeshes[i]->mNumUVComponents[3] = 0; |
| } |
| |
| pScene->mNumMaterials = (unsigned int)materials.size(); |
| pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; |
| ::memcpy(pScene->mMaterials,&materials[0],sizeof(void*)*pScene->mNumMaterials); |
| |
| pScene->mRootNode = new aiNode(); |
| pScene->mRootNode->mName.Set("<IRRMesh>"); |
| pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; |
| pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; |
| |
| for (unsigned int i = 0; i < pScene->mNumMeshes;++i) |
| pScene->mRootNode->mMeshes[i] = i; |
| |
| // clean up and return |
| delete reader; |
| AI_DEBUG_INVALIDATE_PTR(reader); |
| } |
| |
| #endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER |