From 392a658f9c271be965271f45e7521a1b80ea4392 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 30 Apr 2026 11:15:09 +0200 Subject: [PATCH] Bugfix/sparky kitty studios (#6623) * Fixed regression that scrambled FBX blendshape order. * Merge master to this branch --------- Co-authored-by: Lux Co-authored-by: Lux <52231149+lxw404@users.noreply.github.com> Co-authored-by: Kim Kulling --- code/AssetLib/FBX/FBXDeformer.cpp | 14 ++- code/AssetLib/FBX/FBXDocument.h | 8 +- code/AssetLib/FBX/FBXMeshGeometry.cpp | 9 +- code/AssetLib/FBX/FBXMeshGeometry.h | 4 +- code/PostProcessing/ImproveCacheLocality.cpp | 93 ++++++++++---------- 5 files changed, 68 insertions(+), 60 deletions(-) diff --git a/code/AssetLib/FBX/FBXDeformer.cpp b/code/AssetLib/FBX/FBXDeformer.cpp index ee3751350..c2c201793 100644 --- a/code/AssetLib/FBX/FBXDeformer.cpp +++ b/code/AssetLib/FBX/FBXDeformer.cpp @@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER +#include + #include "FBXParser.h" #include "FBXDocument.h" #include "FBXMeshGeometry.h" @@ -144,8 +146,10 @@ BlendShape::BlendShape(uint64_t id, const Element& element, const Document& doc, for (const Connection* con : conns) { const BlendShapeChannel* const bspc = ProcessSimpleConnection(*con, false, "BlendShapeChannel -> BlendShape", element); if (bspc) { - auto pr = blendShapeChannels.insert(bspc); - if (!pr.second) { + // Only add a channel if it doesn't exist already + if (std::find(blendShapeChannels.begin(), blendShapeChannels.end(), bspc) == blendShapeChannels.end()) { + blendShapeChannels.push_back(bspc); + } else { FBXImporter::LogWarn("there is the same blendShapeChannel id ", bspc->ID()); } } @@ -170,8 +174,10 @@ BlendShapeChannel::BlendShapeChannel(uint64_t id, const Element& element, const for (const Connection* con : conns) { const ShapeGeometry* const sg = ProcessSimpleConnection(*con, false, "Shape -> BlendShapeChannel", element); if (sg) { - auto pr = shapeGeometries.insert(sg); - if (!pr.second) { + // Only add a geometry if it doesn't exist already + if (std::find(shapeGeometries.begin(), shapeGeometries.end(), sg) == shapeGeometries.end()) { + shapeGeometries.push_back(sg); + } else { FBXImporter::LogWarn("there is the same shapeGeometrie id ", sg->ID()); } } diff --git a/code/AssetLib/FBX/FBXDocument.h b/code/AssetLib/FBX/FBXDocument.h index b0f7da248..a92dd2049 100644 --- a/code/AssetLib/FBX/FBXDocument.h +++ b/code/AssetLib/FBX/FBXDocument.h @@ -865,14 +865,14 @@ public: return fullWeights; } - const std::unordered_set& GetShapeGeometries() const { + const std::vector& GetShapeGeometries() const { return shapeGeometries; } private: float percent; WeightArray fullWeights; - std::unordered_set shapeGeometries; + std::vector shapeGeometries; }; /** DOM class for BlendShape deformers */ @@ -882,12 +882,12 @@ public: virtual ~BlendShape() = default; - const std::unordered_set& BlendShapeChannels() const { + const std::vector& BlendShapeChannels() const { return blendShapeChannels; } private: - std::unordered_set blendShapeChannels; + std::vector blendShapeChannels; }; /** DOM class for skin deformer clusters (aka sub-deformers) */ diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index dc8303db7..14bcd71d3 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER +#include #include #include "FBXMeshGeometry.h" @@ -69,8 +70,10 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, } const BlendShape* const bsp = ProcessSimpleConnection(*con, false, "BlendShape -> Geometry", element); if (bsp) { - auto pr = blendShapes.insert(bsp); - if (!pr.second) { + // Only add a blendshape if it doesn't exist already + if (std::find(blendShapes.begin(), blendShapes.end(), bsp) == blendShapes.end()) { + blendShapes.push_back(bsp); + } else { FBXImporter::LogWarn("there is the same blendShape id ", bsp->ID()); } } @@ -78,7 +81,7 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, } // ------------------------------------------------------------------------------------------------ -const std::unordered_set& Geometry::GetBlendShapes() const { +const std::vector& Geometry::GetBlendShapes() const { return blendShapes; } diff --git a/code/AssetLib/FBX/FBXMeshGeometry.h b/code/AssetLib/FBX/FBXMeshGeometry.h index c7817b8d6..6b55307de 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.h +++ b/code/AssetLib/FBX/FBXMeshGeometry.h @@ -72,11 +72,11 @@ public: /// @brief Get the BlendShape attached to this geometry or nullptr /// @return The blendshape arrays. - const std::unordered_set& GetBlendShapes() const; + const std::vector& GetBlendShapes() const; private: const Skin* skin; - std::unordered_set blendShapes; + std::vector blendShapes; }; diff --git a/code/PostProcessing/ImproveCacheLocality.cpp b/code/PostProcessing/ImproveCacheLocality.cpp index 34c51c707..be594ed34 100644 --- a/code/PostProcessing/ImproveCacheLocality.cpp +++ b/code/PostProcessing/ImproveCacheLocality.cpp @@ -58,11 +58,55 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include namespace Assimp { +namespace { + ai_real calculateInputACMR(aiMesh *pMesh, const aiFace *const pcEnd, + unsigned int configCacheDepth, unsigned int meshNum) { + ai_real fACMR = 0.0f; + unsigned int *piFIFOStack = new unsigned int[configCacheDepth]; + memset(piFIFOStack, 0xff, configCacheDepth * sizeof(unsigned int)); + unsigned int *piCur = piFIFOStack; + const unsigned int *const piCurEnd = piFIFOStack + configCacheDepth; + + // count the number of cache misses + unsigned int iCacheMisses = 0; + for (const aiFace *pcFace = pMesh->mFaces; pcFace != pcEnd; ++pcFace) { + for (unsigned int qq = 0; qq < 3; ++qq) { + bool bInCache = false; + for (unsigned int *pp = piFIFOStack; pp < piCurEnd; ++pp) { + if (*pp == pcFace->mIndices[qq]) { + // the vertex is in cache + bInCache = true; + break; + } + } + if (!bInCache) { + ++iCacheMisses; + if (piCurEnd == piCur) { + piCur = piFIFOStack; + } + *piCur++ = pcFace->mIndices[qq]; + } + } + } + delete[] piFIFOStack; + fACMR = (ai_real)iCacheMisses / pMesh->mNumFaces; + if (3.0 == fACMR) { + char szBuff[128]; // should be sufficiently large in every case + + // the JoinIdenticalVertices process has not been executed on this + // mesh, otherwise this value would normally be at least minimally + // smaller than 3.0 ... + ai_snprintf(szBuff, 128, "Mesh %u: Not suitable for vcache optimization", meshNum); + ASSIMP_LOG_WARN(szBuff); + return static_cast(0.f); + } + return fACMR; + } +} // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer -ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() : - mConfigCacheDepth(PP_ICL_PTCACHE_SIZE) { +ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() : mConfigCacheDepth(PP_ICL_PTCACHE_SIZE) { // empty } @@ -107,51 +151,6 @@ void ImproveCacheLocalityProcess::Execute(aiScene *pScene) { } } -// ------------------------------------------------------------------------------------------------ -static ai_real calculateInputACMR(aiMesh *pMesh, const aiFace *const pcEnd, - unsigned int configCacheDepth, unsigned int meshNum) { - ai_real fACMR = 0.0f; - unsigned int *piFIFOStack = new unsigned int[configCacheDepth]; - memset(piFIFOStack, 0xff, configCacheDepth * sizeof(unsigned int)); - unsigned int *piCur = piFIFOStack; - const unsigned int *const piCurEnd = piFIFOStack + configCacheDepth; - - // count the number of cache misses - unsigned int iCacheMisses = 0; - for (const aiFace *pcFace = pMesh->mFaces; pcFace != pcEnd; ++pcFace) { - for (unsigned int qq = 0; qq < 3; ++qq) { - bool bInCache = false; - for (unsigned int *pp = piFIFOStack; pp < piCurEnd; ++pp) { - if (*pp == pcFace->mIndices[qq]) { - // the vertex is in cache - bInCache = true; - break; - } - } - if (!bInCache) { - ++iCacheMisses; - if (piCurEnd == piCur) { - piCur = piFIFOStack; - } - *piCur++ = pcFace->mIndices[qq]; - } - } - } - delete[] piFIFOStack; - fACMR = (ai_real)iCacheMisses / pMesh->mNumFaces; - if (3.0 == fACMR) { - char szBuff[128]; // should be sufficiently large in every case - - // the JoinIdenticalVertices process has not been executed on this - // mesh, otherwise this value would normally be at least minimally - // smaller than 3.0 ... - ai_snprintf(szBuff, 128, "Mesh %u: Not suitable for vcache optimization", meshNum); - ASSIMP_LOG_WARN(szBuff); - return static_cast(0.f); - } - return fACMR; -} - // ------------------------------------------------------------------------------------------------ // Improves the cache coherency of a specific mesh ai_real ImproveCacheLocalityProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshNum) {