Bugfix/sparky kitty studios (#6623)

* Fixed regression that scrambled FBX blendshape order.

* Merge master to this branch

---------

Co-authored-by: Lux <lxw404@gmail.com>
Co-authored-by: Lux <52231149+lxw404@users.noreply.github.com>
Co-authored-by: Kim Kulling <kim.kulling@draeger.com>
This commit is contained in:
Kim Kulling
2026-04-30 11:15:09 +02:00
committed by GitHub
parent 11a5d1b8ef
commit 392a658f9c
5 changed files with 68 additions and 60 deletions

View File

@@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include <algorithm>
#include "FBXParser.h" #include "FBXParser.h"
#include "FBXDocument.h" #include "FBXDocument.h"
#include "FBXMeshGeometry.h" #include "FBXMeshGeometry.h"
@@ -144,8 +146,10 @@ BlendShape::BlendShape(uint64_t id, const Element& element, const Document& doc,
for (const Connection* con : conns) { for (const Connection* con : conns) {
const BlendShapeChannel* const bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element); const BlendShapeChannel* const bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element);
if (bspc) { if (bspc) {
auto pr = blendShapeChannels.insert(bspc); // Only add a channel if it doesn't exist already
if (!pr.second) { 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()); 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) { for (const Connection* con : conns) {
const ShapeGeometry* const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element); const ShapeGeometry* const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element);
if (sg) { if (sg) {
auto pr = shapeGeometries.insert(sg); // Only add a geometry if it doesn't exist already
if (!pr.second) { 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()); FBXImporter::LogWarn("there is the same shapeGeometrie id ", sg->ID());
} }
} }

View File

@@ -865,14 +865,14 @@ public:
return fullWeights; return fullWeights;
} }
const std::unordered_set<const ShapeGeometry*>& GetShapeGeometries() const { const std::vector<const ShapeGeometry*>& GetShapeGeometries() const {
return shapeGeometries; return shapeGeometries;
} }
private: private:
float percent; float percent;
WeightArray fullWeights; WeightArray fullWeights;
std::unordered_set<const ShapeGeometry*> shapeGeometries; std::vector<const ShapeGeometry*> shapeGeometries;
}; };
/** DOM class for BlendShape deformers */ /** DOM class for BlendShape deformers */
@@ -882,12 +882,12 @@ public:
virtual ~BlendShape() = default; virtual ~BlendShape() = default;
const std::unordered_set<const BlendShapeChannel*>& BlendShapeChannels() const { const std::vector<const BlendShapeChannel*>& BlendShapeChannels() const {
return blendShapeChannels; return blendShapeChannels;
} }
private: private:
std::unordered_set<const BlendShapeChannel*> blendShapeChannels; std::vector<const BlendShapeChannel*> blendShapeChannels;
}; };
/** DOM class for skin deformer clusters (aka sub-deformers) */ /** DOM class for skin deformer clusters (aka sub-deformers) */

View File

@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include <algorithm>
#include <functional> #include <functional>
#include "FBXMeshGeometry.h" #include "FBXMeshGeometry.h"
@@ -69,8 +70,10 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name,
} }
const BlendShape* const bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", element); const BlendShape* const bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", element);
if (bsp) { if (bsp) {
auto pr = blendShapes.insert(bsp); // Only add a blendshape if it doesn't exist already
if (!pr.second) { 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()); 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<const BlendShape*>& Geometry::GetBlendShapes() const { const std::vector<const BlendShape*>& Geometry::GetBlendShapes() const {
return blendShapes; return blendShapes;
} }

View File

@@ -72,11 +72,11 @@ public:
/// @brief Get the BlendShape attached to this geometry or nullptr /// @brief Get the BlendShape attached to this geometry or nullptr
/// @return The blendshape arrays. /// @return The blendshape arrays.
const std::unordered_set<const BlendShape*>& GetBlendShapes() const; const std::vector<const BlendShape*>& GetBlendShapes() const;
private: private:
const Skin* skin; const Skin* skin;
std::unordered_set<const BlendShape*> blendShapes; std::vector<const BlendShape*> blendShapes;
}; };

View File

@@ -58,11 +58,55 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stack> #include <stack>
namespace Assimp { 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<ai_real>(0.f);
}
return fACMR;
}
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() : ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() : mConfigCacheDepth(PP_ICL_PTCACHE_SIZE) {
mConfigCacheDepth(PP_ICL_PTCACHE_SIZE) {
// empty // 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<ai_real>(0.f);
}
return fACMR;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Improves the cache coherency of a specific mesh // Improves the cache coherency of a specific mesh
ai_real ImproveCacheLocalityProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshNum) { ai_real ImproveCacheLocalityProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshNum) {