From dfd70b5c10cd45ef9d80c96ee08fe4e7393520a9 Mon Sep 17 00:00:00 2001 From: Florian Born Date: Thu, 9 Mar 2023 19:06:58 +0100 Subject: [PATCH 01/10] GLTF Importer: Build a list of the actual vertices so it works well with shared attribute lists --- code/AssetLib/glTF2/glTF2Asset.h | 2 +- code/AssetLib/glTF2/glTF2Asset.inl | 19 ++--- code/AssetLib/glTF2/glTF2Importer.cpp | 100 +++++++++++++++++--------- 3 files changed, 78 insertions(+), 43 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 1d7cff325..cf9c8f11c 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -565,7 +565,7 @@ struct Accessor : public Object { inline size_t GetMaxByteSize(); template - void ExtractData(T *&outData); + size_t ExtractData(T *&outData, const std::vector *remappingIndices = nullptr); void WriteData(size_t count, const void *src_buffer, size_t src_stride); void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride); diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 0e2998357..4e4497e1f 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -962,14 +962,15 @@ inline size_t Accessor::GetMaxByteSize() { } template -void Accessor::ExtractData(T *&outData) { +size_t Accessor::ExtractData(T *&outData, const std::vector *remappingIndices) { uint8_t *data = GetPointer(); if (!data) { throw DeadlyImportError("GLTF2: data is null when extracting data from ", getContextForErrorMessages(id, name)); } + const size_t usedCount = (remappingIndices != nullptr) ? remappingIndices->size() : count; const size_t elemSize = GetElementSize(); - const size_t totalSize = elemSize * count; + const size_t totalSize = elemSize * usedCount; const size_t stride = GetStride(); @@ -980,18 +981,20 @@ void Accessor::ExtractData(T *&outData) { } const size_t maxSize = GetMaxByteSize(); - if (count * stride > maxSize) { - throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name)); + if (usedCount * stride > maxSize) { + throw DeadlyImportError("GLTF: count*stride ", (usedCount * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name)); } - outData = new T[count]; - if (stride == elemSize && targetElemSize == elemSize) { + outData = new T[usedCount]; + if (remappingIndices == nullptr && stride == elemSize && targetElemSize == elemSize) { memcpy(outData, data, totalSize); } else { - for (size_t i = 0; i < count; ++i) { - memcpy(outData + i, data + i * stride, elemSize); + for (size_t i = 0; i < usedCount; ++i) { + size_t srcIdx = remappingIndices != nullptr ? static_cast((*remappingIndices)[i]) : i; + memcpy(outData + i, data + srcIdx * stride, elemSize); } } + return usedCount; } inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t src_stride) { diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index d30556806..f0dc284c3 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -453,6 +453,12 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { unsigned int k = 0; meshOffsets.clear(); + + const unsigned int unusedIndex = ~0; + std::vector usedVertexIndices; + std::vector reverseMappingIndices; + std::vector indexBuffer; + for (unsigned int m = 0; m < r.meshes.Size(); ++m) { Mesh &mesh = r.meshes[m]; @@ -462,6 +468,36 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { for (unsigned int p = 0; p < mesh.primitives.size(); ++p) { Mesh::Primitive &prim = mesh.primitives[p]; + // extract used vertices: + bool useIndexBuffer = prim.indices; + std::vector* usedVertexIndicesPtr = 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 + usedVertexIndicesPtr = &usedVertexIndices; + Accessor::Indexer data = prim.indices->GetIndexer(); + if (!data.IsValid()) { + throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name)); + } + + // Build the vertex remapping table and the modified index buffer (used later instead of the original one) + // In case no index buffer is used, the original vertex arrays are being used so no remapping is required in the first place. + for (unsigned int i = 0; i < count; ++i) { + unsigned int index = data.GetUInt(i); + if (index >= reverseMappingIndices.size()) { + reverseMappingIndices.resize(index + 1, unusedIndex); + } + if (reverseMappingIndices[index] == unusedIndex) { + reverseMappingIndices[index] = static_cast(usedVertexIndices.size()); + usedVertexIndices.push_back(index); + } + indexBuffer[i] = reverseMappingIndices[index]; + } + } + aiMesh *aim = new aiMesh(); meshes.push_back(std::unique_ptr(aim)); @@ -493,26 +529,27 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { Mesh::Primitive::Attributes &attr = prim.attributes; + size_t numAllVertices = 0; if (!attr.position.empty() && attr.position[0]) { - aim->mNumVertices = static_cast(attr.position[0]->count); - attr.position[0]->ExtractData(aim->mVertices); + numAllVertices = attr.position[0]->count; + aim->mNumVertices = static_cast(attr.position[0]->ExtractData(aim->mVertices, usedVertexIndicesPtr)); } if (!attr.normal.empty() && attr.normal[0]) { - if (attr.normal[0]->count != aim->mNumVertices) { + if (attr.normal[0]->count != numAllVertices) { DefaultLogger::get()->warn("Normal count in mesh \"", mesh.name, "\" does not match the vertex count, normals ignored."); } else { - attr.normal[0]->ExtractData(aim->mNormals); + attr.normal[0]->ExtractData(aim->mNormals, usedVertexIndicesPtr); // only extract tangents if normals are present if (!attr.tangent.empty() && attr.tangent[0]) { - if (attr.tangent[0]->count != aim->mNumVertices) { + if (attr.tangent[0]->count != numAllVertices) { DefaultLogger::get()->warn("Tangent count in mesh \"", mesh.name, "\" does not match the vertex count, tangents ignored."); } else { // generate bitangents from normals and tangents according to spec Tangent *tangents = nullptr; - attr.tangent[0]->ExtractData(tangents); + attr.tangent[0]->ExtractData(tangents, usedVertexIndicesPtr); aim->mTangents = new aiVector3D[aim->mNumVertices]; aim->mBitangents = new aiVector3D[aim->mNumVertices]; @@ -529,7 +566,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { } for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) { - if (attr.color[c]->count != aim->mNumVertices) { + if (attr.color[c]->count != numAllVertices) { DefaultLogger::get()->warn("Color stream size in mesh \"", mesh.name, "\" does not match the vertex count"); continue; @@ -537,7 +574,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { auto componentType = attr.color[c]->componentType; if (componentType == glTF2::ComponentType_FLOAT) { - attr.color[c]->ExtractData(aim->mColors[c]); + attr.color[c]->ExtractData(aim->mColors[c], usedVertexIndicesPtr); } else { if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) { aim->mColors[c] = GetVertexColorsForType(attr.color[c]); @@ -552,13 +589,13 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { continue; } - if (attr.texcoord[tc]->count != aim->mNumVertices) { + if (attr.texcoord[tc]->count != numAllVertices) { DefaultLogger::get()->warn("Texcoord stream size in mesh \"", mesh.name, "\" does not match the vertex count"); continue; } - attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]); + attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc], usedVertexIndicesPtr); aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents(); aiVector3D *values = aim->mTextureCoords[tc]; @@ -583,11 +620,11 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { Mesh::Primitive::Target &target = targets[i]; if (needPositions) { - if (target.position[0]->count != aim->mNumVertices) { + if (target.position[0]->count != numAllVertices) { ASSIMP_LOG_WARN("Positions of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); } else { aiVector3D *positionDiff = nullptr; - target.position[0]->ExtractData(positionDiff); + target.position[0]->ExtractData(positionDiff, usedVertexIndicesPtr); for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) { aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId]; } @@ -595,11 +632,11 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { } } if (needNormals) { - if (target.normal[0]->count != aim->mNumVertices) { + if (target.normal[0]->count != numAllVertices) { ASSIMP_LOG_WARN("Normals of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); } else { aiVector3D *normalDiff = nullptr; - target.normal[0]->ExtractData(normalDiff); + target.normal[0]->ExtractData(normalDiff, usedVertexIndicesPtr); for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) { aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId]; } @@ -610,14 +647,14 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { if (!aiAnimMesh.HasNormals()) { // prevent nullptr access to aiAnimMesh.mNormals below when no normals are available ASSIMP_LOG_WARN("Bitangents of target ", i, " in mesh \"", mesh.name, "\" can't be computed, because mesh has no normals."); - } else if (target.tangent[0]->count != aim->mNumVertices) { + } else if (target.tangent[0]->count != numAllVertices) { ASSIMP_LOG_WARN("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); } else { Tangent *tangent = nullptr; - attr.tangent[0]->ExtractData(tangent); + attr.tangent[0]->ExtractData(tangent, usedVertexIndicesPtr); aiVector3D *tangentDiff = nullptr; - target.tangent[0]->ExtractData(tangentDiff); + target.tangent[0]->ExtractData(tangentDiff, usedVertexIndicesPtr); for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) { tangent[vertexId].xyz += tangentDiff[vertexId]; @@ -641,20 +678,15 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { aiFace *facePtr = nullptr; size_t nFaces = 0; - if (prim.indices) { - size_t count = prim.indices->count; - - Accessor::Indexer data = prim.indices->GetIndexer(); - if (!data.IsValid()) { - throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name)); - } + if (useIndexBuffer) { + size_t count = indexBuffer.size(); switch (prim.mode) { case PrimitiveMode_POINTS: { nFaces = count; facePtr = faces = new aiFace[nFaces]; for (unsigned int i = 0; i < count; ++i) { - SetFaceAndAdvance1(facePtr, aim->mNumVertices, data.GetUInt(i)); + SetFaceAndAdvance1(facePtr, aim->mNumVertices, indexBuffer[i]); } break; } @@ -667,7 +699,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { } facePtr = faces = new aiFace[nFaces]; for (unsigned int i = 0; i < count; i += 2) { - SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1)); + SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1]); } break; } @@ -676,12 +708,12 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { case PrimitiveMode_LINE_STRIP: { nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0); facePtr = faces = new aiFace[nFaces]; - SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1)); + SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[0], indexBuffer[1]); for (unsigned int i = 2; i < count; ++i) { - SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i - 1), data.GetUInt(i)); + SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[i - 1], indexBuffer[i]); } if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop - SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(static_cast(count) - 1), faces[0].mIndices[0]); + SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[static_cast(count) - 1], faces[0].mIndices[0]); } break; } @@ -694,7 +726,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { } facePtr = faces = new aiFace[nFaces]; for (unsigned int i = 0; i < count; i += 3) { - SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2)); + SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1], indexBuffer[i + 2]); } break; } @@ -705,10 +737,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { // The ordering is to ensure that the triangles are all drawn with the same orientation if ((i + 1) % 2 == 0) { // For even n, vertices n + 1, n, and n + 2 define triangle n - SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2)); + SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i + 1], indexBuffer[i], indexBuffer[i + 2]); } else { // For odd n, vertices n, n+1, and n+2 define triangle n - SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2)); + SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1], indexBuffer[i + 2]); } } break; @@ -716,9 +748,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { case PrimitiveMode_TRIANGLE_FAN: nFaces = count - 2; facePtr = faces = new aiFace[nFaces]; - SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1), data.GetUInt(2)); + SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[0], indexBuffer[1], indexBuffer[2]); for (unsigned int i = 1; i < nFaces; ++i) { - SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(i + 1), data.GetUInt(i + 2)); + SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[0], indexBuffer[i + 1], indexBuffer[i + 2]); } break; } From 60cefdd54927e79515563907887a1d9813d0b9b3 Mon Sep 17 00:00:00 2001 From: Florian Born Date: Fri, 10 Mar 2023 12:10:38 +0100 Subject: [PATCH 02/10] Jan's fedback --- code/AssetLib/glTF2/glTF2Asset.inl | 25 ++++++++++++++++++------- code/AssetLib/glTF2/glTF2Importer.cpp | 24 ++++++++++++------------ 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 4e4497e1f..b4ea77c79 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -981,18 +981,29 @@ size_t Accessor::ExtractData(T *&outData, const std::vector *remap } const size_t maxSize = GetMaxByteSize(); - if (usedCount * stride > maxSize) { - throw DeadlyImportError("GLTF: count*stride ", (usedCount * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name)); - } outData = new T[usedCount]; - if (remappingIndices == nullptr && stride == elemSize && targetElemSize == elemSize) { - memcpy(outData, data, totalSize); - } else { + + if (remappingIndices != nullptr) { + const unsigned int maxIndex = static_cast(maxSize / stride - 1); for (size_t i = 0; i < usedCount; ++i) { - size_t srcIdx = remappingIndices != nullptr ? static_cast((*remappingIndices)[i]) : i; + size_t srcIdx = (*remappingIndices)[i]; + if (srcIdx > maxIndex) { + throw DeadlyImportError("GLTF: index*stride ", (srcIdx * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name)); + } memcpy(outData + i, data + srcIdx * stride, elemSize); } + } else { // non-indexed cases + if (usedCount * stride > maxSize) { + throw DeadlyImportError("GLTF: count*stride ", (usedCount * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name)); + } + if (stride == elemSize && targetElemSize == elemSize) { + memcpy(outData, data, totalSize); + } else { + for (size_t i = 0; i < usedCount; ++i) { + memcpy(outData + i, data + i * stride, elemSize); + } + } } return usedCount; } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index f0dc284c3..ed9b1ca85 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -454,7 +454,6 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { meshOffsets.clear(); - const unsigned int unusedIndex = ~0; std::vector usedVertexIndices; std::vector reverseMappingIndices; std::vector indexBuffer; @@ -470,14 +469,14 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { // extract used vertices: bool useIndexBuffer = prim.indices; - std::vector* usedVertexIndicesPtr = 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 - usedVertexIndicesPtr = &usedVertexIndices; + vertexRemappingTable = &usedVertexIndices; Accessor::Indexer data = prim.indices->GetIndexer(); if (!data.IsValid()) { throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name)); @@ -485,6 +484,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { // Build the vertex remapping table and the modified index buffer (used later instead of the original one) // In case no index buffer is used, the original vertex arrays are being used so no remapping is required in the first place. + const unsigned int unusedIndex = ~0u; for (unsigned int i = 0; i < count; ++i) { unsigned int index = data.GetUInt(i); if (index >= reverseMappingIndices.size()) { @@ -532,14 +532,14 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { size_t numAllVertices = 0; if (!attr.position.empty() && attr.position[0]) { numAllVertices = attr.position[0]->count; - aim->mNumVertices = static_cast(attr.position[0]->ExtractData(aim->mVertices, usedVertexIndicesPtr)); + aim->mNumVertices = static_cast(attr.position[0]->ExtractData(aim->mVertices, vertexRemappingTable)); } if (!attr.normal.empty() && attr.normal[0]) { if (attr.normal[0]->count != numAllVertices) { DefaultLogger::get()->warn("Normal count in mesh \"", mesh.name, "\" does not match the vertex count, normals ignored."); } else { - attr.normal[0]->ExtractData(aim->mNormals, usedVertexIndicesPtr); + attr.normal[0]->ExtractData(aim->mNormals, vertexRemappingTable); // only extract tangents if normals are present if (!attr.tangent.empty() && attr.tangent[0]) { @@ -549,7 +549,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { // generate bitangents from normals and tangents according to spec Tangent *tangents = nullptr; - attr.tangent[0]->ExtractData(tangents, usedVertexIndicesPtr); + attr.tangent[0]->ExtractData(tangents, vertexRemappingTable); aim->mTangents = new aiVector3D[aim->mNumVertices]; aim->mBitangents = new aiVector3D[aim->mNumVertices]; @@ -574,7 +574,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { auto componentType = attr.color[c]->componentType; if (componentType == glTF2::ComponentType_FLOAT) { - attr.color[c]->ExtractData(aim->mColors[c], usedVertexIndicesPtr); + attr.color[c]->ExtractData(aim->mColors[c], vertexRemappingTable); } else { if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) { aim->mColors[c] = GetVertexColorsForType(attr.color[c]); @@ -595,7 +595,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { continue; } - attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc], usedVertexIndicesPtr); + attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc], vertexRemappingTable); aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents(); aiVector3D *values = aim->mTextureCoords[tc]; @@ -624,7 +624,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { ASSIMP_LOG_WARN("Positions of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); } else { aiVector3D *positionDiff = nullptr; - target.position[0]->ExtractData(positionDiff, usedVertexIndicesPtr); + target.position[0]->ExtractData(positionDiff, vertexRemappingTable); for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) { aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId]; } @@ -636,7 +636,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { ASSIMP_LOG_WARN("Normals of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); } else { aiVector3D *normalDiff = nullptr; - target.normal[0]->ExtractData(normalDiff, usedVertexIndicesPtr); + target.normal[0]->ExtractData(normalDiff, vertexRemappingTable); for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) { aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId]; } @@ -651,10 +651,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { ASSIMP_LOG_WARN("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); } else { Tangent *tangent = nullptr; - attr.tangent[0]->ExtractData(tangent, usedVertexIndicesPtr); + attr.tangent[0]->ExtractData(tangent, vertexRemappingTable); aiVector3D *tangentDiff = nullptr; - target.tangent[0]->ExtractData(tangentDiff, usedVertexIndicesPtr); + target.tangent[0]->ExtractData(tangentDiff, vertexRemappingTable); for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) { tangent[vertexId].xyz += tangentDiff[vertexId]; From 8176c6a0e47c18317267b00e579cb0217fb2ccc3 Mon Sep 17 00:00:00 2001 From: Florian Born Date: Fri, 10 Mar 2023 18:36:43 +0100 Subject: [PATCH 03/10] Adjusting the unit tests to pass new gltf --- code/AssetLib/glTF2/glTF2Importer.cpp | 21 +++++++++++++++------ test/unit/utglTF2ImportExport.cpp | 14 +++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index ed9b1ca85..428763798 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -467,7 +467,15 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { for (unsigned int p = 0; p < mesh.primitives.size(); ++p) { Mesh::Primitive &prim = mesh.primitives[p]; - // extract used vertices: + Mesh::Primitive::Attributes &attr = prim.attributes; + + // Find out the maximum number of vertices: + size_t numAllVertices = 0; + if (!attr.position.empty() && attr.position[0]) { + numAllVertices = attr.position[0]->count; + } + + // Extract used vertices: bool useIndexBuffer = prim.indices; std::vector* vertexRemappingTable = nullptr; if (useIndexBuffer) { @@ -487,6 +495,11 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { const unsigned int unusedIndex = ~0u; for (unsigned int i = 0; i < count; ++i) { unsigned int index = data.GetUInt(i); + if (index >= numAllVertices) { + // Out-of-range indices will be filtered out when adding the faces and then lead to a warning. At this stage, we just keep them. + indexBuffer[i] = index; + continue; + } if (index >= reverseMappingIndices.size()) { reverseMappingIndices.resize(index + 1, unusedIndex); } @@ -527,12 +540,8 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { break; } - Mesh::Primitive::Attributes &attr = prim.attributes; - - size_t numAllVertices = 0; if (!attr.position.empty() && attr.position[0]) { - numAllVertices = attr.position[0]->count; - aim->mNumVertices = static_cast(attr.position[0]->ExtractData(aim->mVertices, vertexRemappingTable)); + aim->mNumVertices = static_cast(attr.position[0]->ExtractData(aim->mVertices, vertexRemappingTable)); } if (!attr.normal.empty() && attr.normal[0]) { diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index ef3fc4137..957a94b15 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -380,7 +380,7 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLines) { const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.gltf", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u); - std::array l1 = { { 0u, 3u, 2u, 1u, 0u } }; + std::array l1 = { { 0u, 1u, 2u, 3u, 0u } }; EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u); for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) { EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], l1[i]); @@ -394,7 +394,7 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLineLoop) { const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.gltf", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u); - std::array l1 = { { 0, 3u, 2u, 1u, 0u } }; + std::array l1 = { { 0, 1u, 2u, 3u, 0u } }; EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u); for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) { EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], l1[i]); @@ -408,7 +408,7 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLineStrip) { const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.gltf", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u); - std::array l1 = { { 0u, 3u, 2u, 1u, 0u } }; + std::array l1 = { { 0u, 1u, 2u, 3u, 0u } }; EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u); for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) { EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], l1[i]); @@ -423,13 +423,13 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeTrianglesStrip) { EXPECT_NE(nullptr, scene); EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2u); EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u); - std::array f1 = { { 0u, 3u, 1u } }; + std::array f1 = { { 0u, 1u, 2u } }; EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3u); for (size_t i = 0; i < 3; ++i) { EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mIndices[i], f1[i]); } - std::array f2 = { { 1u, 3u, 2u } }; + std::array f2 = { { 2u, 1u, 3u } }; EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3u); for (size_t i = 0; i < 3; ++i) { EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mIndices[i], f2[i]); @@ -443,13 +443,13 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeTrianglesFan) { EXPECT_NE(nullptr, scene); EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u); EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2u); - std::array f1 = { { 0u, 3u, 2u } }; + std::array f1 = { { 0u, 1u, 2u } }; EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3u); for (size_t i = 0; i < 3; ++i) { EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mIndices[i], f1[i]); } - std::array f2 = { { 0u, 2u, 1u } }; + std::array f2 = { { 0u, 2u, 3u } }; EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3u); for (size_t i = 0; i < 3; ++i) { EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mIndices[i], f2[i]); From cdfdd75a66e2323bd834f923798d3043ad3deaf8 Mon Sep 17 00:00:00 2001 From: Inho Lee Date: Tue, 21 Mar 2023 10:35:24 +0100 Subject: [PATCH 04/10] FBXConverter : import FbxCamera correctly FbxCamera's default value is correct but its transformed values are described in each NodeProperties. --- code/AssetLib/FBX/FBXConverter.cpp | 30 +++++++++++++++++++++--------- code/AssetLib/FBX/FBXConverter.h | 9 ++++++--- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index 37654746e..416a73d9b 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -152,7 +152,7 @@ void FBXConverter::ConvertRootNode() { mSceneOut->mRootNode->mName.Set(unique_name); // root has ID 0 - ConvertNodes(0L, mSceneOut->mRootNode, mSceneOut->mRootNode); + ConvertNodes(0L, mSceneOut->mRootNode, mSceneOut->mRootNode, aiMatrix4x4()); } static std::string getAncestorBaseName(const aiNode *node) { @@ -196,7 +196,7 @@ struct FBXConverter::PotentialNode { /// todo: get bone from stack /// todo: make map of aiBone* to aiNode* /// then update convert clusters to the new format -void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) { +void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &globalTransform) { const std::vector &conns = doc.GetConnectionsByDestinationSequenced(id, "Model"); std::vector nodes; @@ -290,14 +290,15 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) } // recursion call - child nodes - ConvertNodes(model->ID(), last_parent, root_node); + aiMatrix4x4 newGlobalMatrix = globalTransform * nodes_chain.front().mNode->mTransformation; + ConvertNodes(model->ID(), last_parent, root_node, newGlobalMatrix); if (doc.Settings().readLights) { ConvertLights(*model, node_name); } if (doc.Settings().readCameras) { - ConvertCameras(*model, node_name); + ConvertCameras(*model, node_name, newGlobalMatrix); } nodes.push_back(std::move(nodes_chain.front())); @@ -327,12 +328,14 @@ void FBXConverter::ConvertLights(const Model &model, const std::string &orig_nam } } -void FBXConverter::ConvertCameras(const Model &model, const std::string &orig_name) { +void FBXConverter::ConvertCameras(const Model &model, + const std::string &orig_name, + const aiMatrix4x4 &transform) { const std::vector &node_attrs = model.GetAttributes(); for (const NodeAttribute *attr : node_attrs) { const Camera *const cam = dynamic_cast(attr); if (cam) { - ConvertCamera(*cam, orig_name); + ConvertCamera(*cam, orig_name, transform); } } } @@ -413,7 +416,9 @@ void FBXConverter::ConvertLight(const Light &light, const std::string &orig_name } } -void FBXConverter::ConvertCamera(const Camera &cam, const std::string &orig_name) { +void FBXConverter::ConvertCamera(const Camera &cam, + const std::string &orig_name, + aiMatrix4x4 transform) { cameras.push_back(new aiCamera()); aiCamera *const out_camera = cameras.back(); @@ -421,9 +426,16 @@ void FBXConverter::ConvertCamera(const Camera &cam, const std::string &orig_name out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight(); + aiVector3D pos = cam.Position(); + out_camera->mLookAt = cam.InterestPosition(); + out_camera->mUp = pos + cam.UpVector(); + transform.Inverse(); + pos *= transform; + out_camera->mLookAt *= transform; + out_camera->mUp *= transform; + out_camera->mLookAt -= pos; + out_camera->mUp -= pos; out_camera->mPosition = aiVector3D(0.0f); - out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f); - out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f); out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView()); diff --git a/code/AssetLib/FBX/FBXConverter.h b/code/AssetLib/FBX/FBXConverter.h index 41acb6ffe..b9b6c46b0 100644 --- a/code/AssetLib/FBX/FBXConverter.h +++ b/code/AssetLib/FBX/FBXConverter.h @@ -134,19 +134,22 @@ private: // ------------------------------------------------------------------------------------------------ // collect and assign child nodes - void ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node); + void ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &globalTransform); // ------------------------------------------------------------------------------------------------ void ConvertLights(const Model& model, const std::string &orig_name ); // ------------------------------------------------------------------------------------------------ - void ConvertCameras(const Model& model, const std::string &orig_name ); + void ConvertCameras(const Model& model, const std::string &orig_name, + const aiMatrix4x4 &transform); // ------------------------------------------------------------------------------------------------ void ConvertLight( const Light& light, const std::string &orig_name ); // ------------------------------------------------------------------------------------------------ - void ConvertCamera( const Camera& cam, const std::string &orig_name ); + void ConvertCamera(const Camera& cam, const std::string &orig_name, + aiMatrix4x4 transform); // ------------------------------------------------------------------------------------------------ void GetUniqueName( const std::string &name, std::string& uniqueName ); From 4438b3ecc9aae5156f888ecd5a9604d8e1ce638e Mon Sep 17 00:00:00 2001 From: aniongithub Date: Wed, 29 Mar 2023 18:36:18 +0000 Subject: [PATCH 05/10] Fix Issue #4486 using the fix described by @jianliang79 --- code/AssetLib/FBX/FBXConverter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index f1f57c10b..257845384 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -640,7 +640,7 @@ void FBXConverter::GetRotationMatrix(Model::RotOrder mode, const aiVector3D &rot bool FBXConverter::NeedsComplexTransformationChain(const Model &model) { const PropertyTable &props = model.Props(); - const auto zero_epsilon = ai_epsilon; + const auto zero_epsilon = Math::getEpsilon(); const aiVector3D all_ones(1.0f, 1.0f, 1.0f); for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) { const TransformationComp comp = static_cast(i); From 5cf9d3abf7d7f6debbab1e491313249b805bd2f3 Mon Sep 17 00:00:00 2001 From: Sven Liedtke Date: Sat, 1 Apr 2023 01:02:21 +0200 Subject: [PATCH 06/10] std::getenv is not supported using uwp --- code/Common/ImporterRegistry.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index 78c02d96d..ac4f5e544 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -214,7 +214,12 @@ void GetImporterInstanceList(std::vector &out) { // Some importers may be unimplemented or otherwise unsuitable for general use // in their current state. Devs can set ASSIMP_ENABLE_DEV_IMPORTERS in their // local environment to enable them, otherwise they're left out of the registry. +#if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP + // not supported under uwp const char *envStr = std::getenv("ASSIMP_ENABLE_DEV_IMPORTERS"); +#else + const char *envStr = { "0" }; +#endif bool devImportersEnabled = envStr && strcmp(envStr, "0"); // Ensure no unused var warnings if all uses are #ifndef'd away below: @@ -378,7 +383,7 @@ void GetImporterInstanceList(std::vector &out) { out.push_back(new IQMImporter()); #endif //#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER - // out.push_back(new StepFile::StepFileImporter()); + // out.push_back(new StepFile::StepFileImporter()); //#endif } From 6b547613106cce7513806426c05a75f7bba92cfc Mon Sep 17 00:00:00 2001 From: Jackie9527 <80555200+Jackie9527@users.noreply.github.com> Date: Sat, 1 Apr 2023 10:30:10 +0800 Subject: [PATCH 07/10] Fix warning related to nested-anon-types. Signed-off-by: Jackie9527 <80555200+Jackie9527@users.noreply.github.com> --- code/AssetLib/glTF/glTFAsset.h | 27 ++++++++++++++------------- code/CMakeLists.txt | 1 - 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/code/AssetLib/glTF/glTFAsset.h b/code/AssetLib/glTF/glTFAsset.h index d2ae84b49..b5e6375ad 100644 --- a/code/AssetLib/glTF/glTFAsset.h +++ b/code/AssetLib/glTF/glTFAsset.h @@ -513,21 +513,22 @@ struct Camera : public Object { }; Type type; + struct Perspective { + float aspectRatio; //! Date: Sat, 1 Apr 2023 12:19:20 +0800 Subject: [PATCH 08/10] Fix warning related to format-non-iso. Signed-off-by: Jackie9527 <80555200+Jackie9527@users.noreply.github.com> --- code/CMakeLists.txt | 1 - include/assimp/StringUtils.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index fd3b1cb88..3f5b8e936 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -1246,7 +1246,6 @@ IF (ASSIMP_WARNINGS_AS_ERRORS) -Wno-deprecated-copy-with-dtor -Wno-deprecated -Wno-format-nonliteral - -Wno-format-non-iso -Wno-comma -Wno-unreachable-code-break -Wno-unreachable-code-return diff --git a/include/assimp/StringUtils.h b/include/assimp/StringUtils.h index 59c6e9ead..cd8726785 100644 --- a/include/assimp/StringUtils.h +++ b/include/assimp/StringUtils.h @@ -56,7 +56,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) #define AI_SIZEFMT "%Iu" #else #define AI_SIZEFMT "%zu" From dacaebc0788848157ad6c54e1f10aa0e117fcefb Mon Sep 17 00:00:00 2001 From: Jackie9527 <80555200+Jackie9527@users.noreply.github.com> Date: Sat, 1 Apr 2023 23:49:22 +0800 Subject: [PATCH 09/10] Remove unused -Wno-shadow-field-in-constructor. Signed-off-by: Jackie9527 <80555200+Jackie9527@users.noreply.github.com> --- code/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index fd3b1cb88..e05b4dd7b 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -1229,7 +1229,6 @@ IF (ASSIMP_WARNINGS_AS_ERRORS) -Wno-double-promotion -Wno-unused-macros -Wno-disabled-macro-expansion - -Wno-shadow-field-in-constructor -Wno-shadow-field -Wno-shadow -Wno-language-extension-token From e6f26fc52e93979a999ebf3b1ed083cc9006e294 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 4 Apr 2023 12:54:26 +0200 Subject: [PATCH 10/10] Remove dead code. --- code/Common/ImporterRegistry.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index ac4f5e544..c67fee936 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -382,9 +382,6 @@ void GetImporterInstanceList(std::vector &out) { #ifndef ASSIMP_BUILD_NO_IQM_IMPORTER out.push_back(new IQMImporter()); #endif - //#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER - // out.push_back(new StepFile::StepFileImporter()); - //#endif } /** will delete all registered importers. */