diff --git a/CMakeLists.txt b/CMakeLists.txt index 20c735c4c..46c2a591c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,8 +49,8 @@ option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF) IF(ASSIMP_HUNTER_ENABLED) include("cmake-modules/HunterGate.cmake") HunterGate( - URL "https://github.com/cpp-pm/hunter/archive/v0.24.0.tar.gz" - SHA1 "a3d7f4372b1dcd52faa6ff4a3bd5358e1d0e5efd" + URL "https://github.com/cpp-pm/hunter/archive/v0.24.17.tar.gz" + SHA1 "e6396699e414120e32557fe92db097b7655b760b" ) add_definitions(-DASSIMP_USE_HUNTER) diff --git a/code/AssetLib/HMP/HMPLoader.cpp b/code/AssetLib/HMP/HMPLoader.cpp index 3dd27eb02..431783a6a 100644 --- a/code/AssetLib/HMP/HMPLoader.cpp +++ b/code/AssetLib/HMP/HMPLoader.cpp @@ -115,7 +115,9 @@ void HMPImporter::InternReadFile(const std::string &pFile, throw DeadlyImportError("HMP File is too small."); // Allocate storage and copy the contents of the file to a memory buffer - mBuffer = new uint8_t[fileSize]; + auto deleter=[this](uint8_t* ptr){ delete[] ptr; mBuffer = nullptr; }; + std::unique_ptr buffer(new uint8_t[fileSize], deleter); + mBuffer = buffer.get(); file->Read((void *)mBuffer, 1, fileSize); iFileSize = (unsigned int)fileSize; @@ -143,9 +145,6 @@ void HMPImporter::InternReadFile(const std::string &pFile, // Print the magic word to the logger std::string szBuffer = ai_str_toprintable((const char *)&iMagic, sizeof(iMagic)); - delete[] mBuffer; - mBuffer = nullptr; - // We're definitely unable to load this file throw DeadlyImportError("Unknown HMP subformat ", pFile, ". Magic word (", szBuffer, ") is not known"); @@ -153,9 +152,6 @@ void HMPImporter::InternReadFile(const std::string &pFile, // Set the AI_SCENE_FLAGS_TERRAIN bit pScene->mFlags |= AI_SCENE_FLAGS_TERRAIN; - - delete[] mBuffer; - mBuffer = nullptr; } // ------------------------------------------------------------------------------------------------ @@ -445,11 +441,11 @@ void HMPImporter::ReadFirstSkin(unsigned int iNumSkins, const unsigned char *szC szCursor += sizeof(uint32_t); // allocate an output material - aiMaterial *pcMat = new aiMaterial(); + std::unique_ptr pcMat(new aiMaterial()); // read the skin, this works exactly as for MDL7 ParseSkinLump_3DGS_MDL7(szCursor, &szCursor, - pcMat, iType, iWidth, iHeight); + pcMat.get(), iType, iWidth, iHeight); // now we need to skip any other skins ... for (unsigned int i = 1; i < iNumSkins; ++i) { @@ -468,7 +464,7 @@ void HMPImporter::ReadFirstSkin(unsigned int iNumSkins, const unsigned char *szC // setup the material ... pScene->mNumMaterials = 1; pScene->mMaterials = new aiMaterial *[1]; - pScene->mMaterials[0] = pcMat; + pScene->mMaterials[0] = pcMat.release(); *szCursorOut = szCursor; } diff --git a/code/AssetLib/MD5/MD5Parser.cpp b/code/AssetLib/MD5/MD5Parser.cpp index dce4c5732..97dedab71 100644 --- a/code/AssetLib/MD5/MD5Parser.cpp +++ b/code/AssetLib/MD5/MD5Parser.cpp @@ -228,15 +228,20 @@ bool MD5Parser::ParseSection(Section &out) { out.data[out.length] = '\0'; // parse a string, enclosed in quotation marks -#define AI_MD5_PARSE_STRING_IN_QUOTATION(out) \ - while ('\"' != *sz) \ - ++sz; \ - const char *szStart = ++sz; \ - while ('\"' != *sz) \ - ++sz; \ - const char *szEnd = (sz++); \ - out.length = (ai_uint32)(szEnd - szStart); \ - ::memcpy(out.data, szStart, out.length); \ +#define AI_MD5_PARSE_STRING_IN_QUOTATION(out) \ + out.length = 0; \ + while ('\"' != *sz && '\0' != *sz) \ + ++sz; \ + if ('\0' != *sz) { \ + const char *szStart = ++sz; \ + while ('\"' != *sz && '\0' != *sz) \ + ++sz; \ + if ('\0' != *sz) { \ + const char *szEnd = (sz++); \ + out.length = (ai_uint32)(szEnd - szStart); \ + ::memcpy(out.data, szStart, out.length); \ + } \ + } \ out.data[out.length] = '\0'; // ------------------------------------------------------------------------------------------------ // .MD5MESH parsing function diff --git a/code/AssetLib/MDL/MDLLoader.cpp b/code/AssetLib/MDL/MDLLoader.cpp index 76b61cda5..098b53e76 100644 --- a/code/AssetLib/MDL/MDLLoader.cpp +++ b/code/AssetLib/MDL/MDLLoader.cpp @@ -274,7 +274,7 @@ void MDLImporter::InternReadFile(const std::string &pFile, // ------------------------------------------------------------------------------------------------ // Check whether we're still inside the valid file range void MDLImporter::SizeCheck(const void *szPos) { - if (!szPos || (const unsigned char *)szPos > this->mBuffer + this->iFileSize) { + if (!szPos || (const unsigned char *)szPos > this->mBuffer + this->iFileSize || szPos < this->mBuffer) { throw DeadlyImportError("Invalid MDL file. The file is too small " "or contains invalid data."); } diff --git a/code/AssetLib/MDL/MDLMaterialLoader.cpp b/code/AssetLib/MDL/MDLMaterialLoader.cpp index 799368264..fbda40151 100644 --- a/code/AssetLib/MDL/MDLMaterialLoader.cpp +++ b/code/AssetLib/MDL/MDLMaterialLoader.cpp @@ -703,7 +703,14 @@ void MDLImporter::SkipSkinLump_3DGS_MDL7( tex.pcData = bad_texel; tex.mHeight = iHeight; tex.mWidth = iWidth; - ParseTextureColorData(szCurrent, iMasked, &iSkip, &tex); + + try { + ParseTextureColorData(szCurrent, iMasked, &iSkip, &tex); + } catch (...) { + // FIX: Important, otherwise the destructor will crash + tex.pcData = nullptr; + throw; + } // FIX: Important, otherwise the destructor will crash tex.pcData = nullptr; diff --git a/code/AssetLib/OFF/OFFLoader.cpp b/code/AssetLib/OFF/OFFLoader.cpp index cb265029a..f50afb57b 100644 --- a/code/AssetLib/OFF/OFFLoader.cpp +++ b/code/AssetLib/OFF/OFFLoader.cpp @@ -284,7 +284,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS for (unsigned int i = 0; i < numFaces; ) { if(!GetNextLine(buffer,line)) { ASSIMP_LOG_ERROR("OFF: The number of faces in the header is incorrect"); - break; + throw DeadlyImportError("OFF: The number of faces in the header is incorrect"); } unsigned int idx; sz = line; SkipSpaces(&sz); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index f4d0d6632..942c63c85 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -100,8 +100,6 @@ glTF2Importer::glTF2Importer() : // empty } -glTF2Importer::~glTF2Importer() = default; - const aiImporterDesc *glTF2Importer::GetInfo() const { return &desc; } @@ -443,10 +441,10 @@ static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsign #endif // ASSIMP_BUILD_DEBUG template -aiColor4D *GetVertexColorsForType(Ref input) { +aiColor4D *GetVertexColorsForType(Ref input, std::vector *vertexRemappingTable) { constexpr float max = std::numeric_limits::max(); aiColor4t *colors; - input->ExtractData(colors); + input->ExtractData(colors, vertexRemappingTable); auto output = new aiColor4D[input->count]; for (size_t i = 0; i < input->count; i++) { output[i] = aiColor4D( @@ -461,20 +459,26 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { ASSIMP_LOG_DEBUG("Importing ", r.meshes.Size(), " meshes"); std::vector> meshes; - unsigned int k = 0; meshOffsets.clear(); + meshOffsets.reserve(r.meshes.Size() + 1); + mVertexRemappingTables.clear(); + // Count the number of aiMeshes + unsigned int num_aiMeshes = 0; + for (unsigned int m = 0; m < r.meshes.Size(); ++m) { + meshOffsets.push_back(num_aiMeshes); + num_aiMeshes += unsigned(r.meshes[m].primitives.size()); + } + meshOffsets.push_back(num_aiMeshes); // add a last element so we can always do meshOffsets[n+1] - meshOffsets[n] - std::vector usedVertexIndices; std::vector reverseMappingIndices; std::vector indexBuffer; + meshes.reserve(num_aiMeshes); + mVertexRemappingTables.resize(num_aiMeshes); for (unsigned int m = 0; m < r.meshes.Size(); ++m) { Mesh &mesh = r.meshes[m]; - meshOffsets.push_back(k); - k += unsigned(mesh.primitives.size()); - for (unsigned int p = 0; p < mesh.primitives.size(); ++p) { Mesh::Primitive &prim = mesh.primitives[p]; @@ -488,14 +492,14 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { // Extract used vertices: bool useIndexBuffer = prim.indices; - std::vector* vertexRemappingTable = nullptr; + std::vector *vertexRemappingTable = nullptr; + if (useIndexBuffer) { size_t count = prim.indices->count; indexBuffer.resize(count); - usedVertexIndices.clear(); reverseMappingIndices.clear(); - usedVertexIndices.reserve(count / 3); // this is a very rough heuristic to reduce re-allocations - vertexRemappingTable = &usedVertexIndices; + vertexRemappingTable = &mVertexRemappingTables[meshes.size()]; + vertexRemappingTable->reserve(count / 3); // this is a very rough heuristic to reduce re-allocations Accessor::Indexer data = prim.indices->GetIndexer(); if (!data.IsValid()) { throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name)); @@ -515,8 +519,8 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { reverseMappingIndices.resize(index + 1, unusedIndex); } if (reverseMappingIndices[index] == unusedIndex) { - reverseMappingIndices[index] = static_cast(usedVertexIndices.size()); - usedVertexIndices.push_back(index); + reverseMappingIndices[index] = static_cast(vertexRemappingTable->size()); + vertexRemappingTable->push_back(index); } indexBuffer[i] = reverseMappingIndices[index]; } @@ -597,9 +601,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { attr.color[c]->ExtractData(aim->mColors[c], vertexRemappingTable); } else { if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) { - aim->mColors[c] = GetVertexColorsForType(attr.color[c]); + aim->mColors[c] = GetVertexColorsForType(attr.color[c], vertexRemappingTable); } else if (componentType == glTF2::ComponentType_UNSIGNED_SHORT) { - aim->mColors[c] = GetVertexColorsForType(attr.color[c]); + aim->mColors[c] = GetVertexColorsForType(attr.color[c], vertexRemappingTable); } } } @@ -875,8 +879,6 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { } } - meshOffsets.push_back(k); - CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes); } @@ -1009,7 +1011,8 @@ static void GetNodeTransform(aiMatrix4x4 &matrix, const glTF2::Node &node) { } } -static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector> &map) { +static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector> &map, std::vector* vertexRemappingTablePtr) { + Mesh::Primitive::Attributes &attr = primitive.attributes; if (attr.weight.empty() || attr.joint.empty()) { return; @@ -1018,14 +1021,14 @@ static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vectorcount; + size_t num_vertices = 0; struct Weights { float values[4]; }; Weights **weights = new Weights*[attr.weight.size()]; for (size_t w = 0; w < attr.weight.size(); ++w) { - attr.weight[w]->ExtractData(weights[w]); + num_vertices = attr.weight[w]->ExtractData(weights[w], vertexRemappingTablePtr); } struct Indices8 { @@ -1039,12 +1042,12 @@ static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vectorGetElementSize() == 4) { indices8 = new Indices8*[attr.joint.size()]; for (size_t j = 0; j < attr.joint.size(); ++j) { - attr.joint[j]->ExtractData(indices8[j]); + attr.joint[j]->ExtractData(indices8[j], vertexRemappingTablePtr); } } else { indices16 = new Indices16 *[attr.joint.size()]; for (size_t j = 0; j < attr.joint.size(); ++j) { - attr.joint[j]->ExtractData(indices16[j]); + attr.joint[j]->ExtractData(indices16[j], vertexRemappingTablePtr); } } // @@ -1109,7 +1112,7 @@ void ParseExtras(aiMetadata* metadata, const Extras& extras) { } } -aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector &meshOffsets, glTF2::Ref &ptr) { +aiNode *glTF2Importer::ImportNode(glTF2::Asset &r, glTF2::Ref &ptr) { Node &node = *ptr; aiNode *ainode = new aiNode(GetNodeName(node)); @@ -1121,7 +1124,7 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & std::fill(ainode->mChildren, ainode->mChildren + ainode->mNumChildren, nullptr); for (unsigned int i = 0; i < ainode->mNumChildren; ++i) { - aiNode *child = ImportNode(pScene, r, meshOffsets, node.children[i]); + aiNode *child = ImportNode(r, node.children[i]); child->mParent = ainode; ainode->mChildren[i] = child; } @@ -1154,11 +1157,13 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & if (node.skin) { for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) { - aiMesh *mesh = pScene->mMeshes[meshOffsets[mesh_idx] + primitiveNo]; + unsigned int aiMeshIdx = meshOffsets[mesh_idx] + primitiveNo; + aiMesh *mesh = mScene->mMeshes[aiMeshIdx]; unsigned int numBones = static_cast(node.skin->jointNames.size()); + std::vector *vertexRemappingTablePtr = mVertexRemappingTables[aiMeshIdx].empty() ? nullptr : &mVertexRemappingTables[aiMeshIdx]; std::vector> weighting(numBones); - BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting); + BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting, vertexRemappingTablePtr); mesh->mNumBones = static_cast(numBones); mesh->mBones = new aiBone *[mesh->mNumBones]; @@ -1175,7 +1180,7 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & // mapping which makes things doubly-slow. mat4 *pbindMatrices = nullptr; - node.skin->inverseBindMatrices->ExtractData(pbindMatrices); + node.skin->inverseBindMatrices->ExtractData(pbindMatrices, nullptr); for (uint32_t i = 0; i < numBones; ++i) { const std::vector &weights = weighting[i]; @@ -1221,11 +1226,11 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & } if (node.camera) { - pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName; + mScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName; } if (node.light) { - pScene->mLights[node.light.GetIndex()]->mName = ainode->mName; + mScene->mLights[node.light.GetIndex()]->mName = ainode->mName; // range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual // it is added to meta data of parent node, because there is no other place to put it @@ -1257,7 +1262,7 @@ void glTF2Importer::ImportNodes(glTF2::Asset &r) { // The root nodes unsigned int numRootNodes = unsigned(rootNodes.size()); if (numRootNodes == 1) { // a single root node: use it - mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]); + mScene->mRootNode = ImportNode(r, rootNodes[0]); } else if (numRootNodes > 1) { // more than one root node: create a fake root aiNode *root = mScene->mRootNode = new aiNode("ROOT"); @@ -1265,7 +1270,7 @@ void glTF2Importer::ImportNodes(glTF2::Asset &r) { std::fill(root->mChildren, root->mChildren + numRootNodes, nullptr); for (unsigned int i = 0; i < numRootNodes; ++i) { - aiNode *node = ImportNode(mScene, r, meshOffsets, rootNodes[i]); + aiNode *node = ImportNode(r, rootNodes[i]); node->mParent = root; root->mChildren[root->mNumChildren++] = node; } @@ -1666,6 +1671,7 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO // clean all member arrays meshOffsets.clear(); + mVertexRemappingTables.clear(); mEmbeddedTexIdxs.clear(); this->mScene = pScene; diff --git a/code/AssetLib/glTF2/glTF2Importer.h b/code/AssetLib/glTF2/glTF2Importer.h index 80cf689dc..2be42126c 100644 --- a/code/AssetLib/glTF2/glTF2Importer.h +++ b/code/AssetLib/glTF2/glTF2Importer.h @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_GLTF2IMPORTER_H_INC #include +#include struct aiNode; @@ -59,7 +60,7 @@ namespace Assimp { class glTF2Importer : public BaseImporter { public: glTF2Importer(); - ~glTF2Importer() override; + ~glTF2Importer() override = default; bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override; protected: @@ -76,10 +77,12 @@ private: void ImportNodes(glTF2::Asset &a); void ImportAnimations(glTF2::Asset &a); void ImportCommonMetadata(glTF2::Asset &a); + aiNode *ImportNode(glTF2::Asset &r, glTF2::Ref &ptr); private: std::vector meshOffsets; std::vector mEmbeddedTexIdxs; + std::vector> mVertexRemappingTables; // for each converted aiMesh in the scene, it stores a list of vertices that are actually used aiScene *mScene; /// An instance of rapidjson::IRemoteSchemaDocumentProvider diff --git a/code/PostProcessing/LimitBoneWeightsProcess.cpp b/code/PostProcessing/LimitBoneWeightsProcess.cpp index 7047ec0f1..16b32143e 100644 --- a/code/PostProcessing/LimitBoneWeightsProcess.cpp +++ b/code/PostProcessing/LimitBoneWeightsProcess.cpp @@ -81,6 +81,7 @@ void LimitBoneWeightsProcess::Execute( aiScene* pScene) { // Executes the post processing step on the given imported data. void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) { this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS); + this->mRemoveEmptyBones = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, 1) != 0; } // ------------------------------------------------------------------------------------------------ @@ -172,9 +173,9 @@ void LimitBoneWeightsProcess::ProcessMesh(aiMesh* pMesh) { } // remove empty bones -#ifdef AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES - pMesh->mNumBones = removeEmptyBones(pMesh); -#endif // AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES + if (mRemoveEmptyBones) { + pMesh->mNumBones = removeEmptyBones(pMesh); + } if (!DefaultLogger::isNullLogger()) { ASSIMP_LOG_INFO("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones); diff --git a/code/PostProcessing/LimitBoneWeightsProcess.h b/code/PostProcessing/LimitBoneWeightsProcess.h index b19d536cf..8e5ebd80d 100644 --- a/code/PostProcessing/LimitBoneWeightsProcess.h +++ b/code/PostProcessing/LimitBoneWeightsProcess.h @@ -133,6 +133,7 @@ public: /** Maximum number of bones influencing any single vertex. */ unsigned int mMaxWeights; + bool mRemoveEmptyBones; }; } // end of namespace Assimp