Assimp: SplitByBoneCountProcess looses morph targets along the way

Created on 6 Nov 2020  路  5Comments  路  Source: assimp/assimp

Describe the bug
when aiProcess_SplitByBoneCount is on and bone count is high enough so that SplitByBoneCountProcess runs all blendshapes are lost

To Reproduce
Have aiProcess_SplitByBoneCount set for import and import glb with large bone count (Im still checking if I can attach my .glb to the ticket since it could have IP problems)

Expected behavior
meshes are split but they still have blendshapes intact

Desktop (please complete the following information):
All platforms

Bug Postprocessing

Most helpful comment

I can propose a PR with smth like code below added to end of SplitByBoneCountProcess::SplitMesh

        // ... and copy all the morph targets for all the vertices which made it into the new submesh
        if (pMesh->mNumAnimMeshes > 0) {
            newMesh->mNumAnimMeshes = pMesh->mNumAnimMeshes;
            newMesh->mAnimMeshes = new aiAnimMesh*[newMesh->mNumAnimMeshes];

            for (unsigned int morphIdx = 0; morphIdx < newMesh->mNumAnimMeshes; ++morphIdx) {
                aiAnimMesh* origTarget = pMesh->mAnimMeshes[morphIdx];
                aiAnimMesh* newTarget = new aiAnimMesh;
                newTarget->mName = origTarget->mName;
                newTarget->mWeight = origTarget->mWeight;
                newTarget->mNumVertices = numSubMeshVertices;
                newTarget->mVertices = new aiVector3D[numSubMeshVertices];
                newMesh->mAnimMeshes[morphIdx] = newTarget;

                if (origTarget->HasNormals()) {
                    newTarget->mNormals = new aiVector3D[numSubMeshVertices];
                }

                if (origTarget->HasTangentsAndBitangents()) {
                    newTarget->mTangents = new aiVector3D[numSubMeshVertices];
                    newTarget->mBitangents = new aiVector3D[numSubMeshVertices];
                }

                for( unsigned int vi = 0; vi < numSubMeshVertices; ++vi) {
                    // find the source vertex for it in the source mesh
                    unsigned int previousIndex = previousVertexIndices[vi];
                    newTarget->mVertices[vi] = origTarget->mVertices[previousIndex];

                    if (newTarget->HasNormals()) {
                        newTarget->mNormals[vi] = origTarget->mNormals[previousIndex];
                    }
                    if (newTarget->HasTangentsAndBitangents()) {
                        newTarget->mTangents[vi] = origTarget->mTangents[previousIndex];
                        newTarget->mBitangents[vi] = origTarget->mBitangents[previousIndex];
                    }
                }
            }
        }

This seem to work for my basic tests at least, however I'm not sure if SplitByBoneCountProcess introduces new vertices, in which case morph targets would need to be somehow interpolated for new vertices as well

All 5 comments

I can propose a PR with smth like code below added to end of SplitByBoneCountProcess::SplitMesh

        // ... and copy all the morph targets for all the vertices which made it into the new submesh
        if (pMesh->mNumAnimMeshes > 0) {
            newMesh->mNumAnimMeshes = pMesh->mNumAnimMeshes;
            newMesh->mAnimMeshes = new aiAnimMesh*[newMesh->mNumAnimMeshes];

            for (unsigned int morphIdx = 0; morphIdx < newMesh->mNumAnimMeshes; ++morphIdx) {
                aiAnimMesh* origTarget = pMesh->mAnimMeshes[morphIdx];
                aiAnimMesh* newTarget = new aiAnimMesh;
                newTarget->mName = origTarget->mName;
                newTarget->mWeight = origTarget->mWeight;
                newTarget->mNumVertices = numSubMeshVertices;
                newTarget->mVertices = new aiVector3D[numSubMeshVertices];
                newMesh->mAnimMeshes[morphIdx] = newTarget;

                if (origTarget->HasNormals()) {
                    newTarget->mNormals = new aiVector3D[numSubMeshVertices];
                }

                if (origTarget->HasTangentsAndBitangents()) {
                    newTarget->mTangents = new aiVector3D[numSubMeshVertices];
                    newTarget->mBitangents = new aiVector3D[numSubMeshVertices];
                }

                for( unsigned int vi = 0; vi < numSubMeshVertices; ++vi) {
                    // find the source vertex for it in the source mesh
                    unsigned int previousIndex = previousVertexIndices[vi];
                    newTarget->mVertices[vi] = origTarget->mVertices[previousIndex];

                    if (newTarget->HasNormals()) {
                        newTarget->mNormals[vi] = origTarget->mNormals[previousIndex];
                    }
                    if (newTarget->HasTangentsAndBitangents()) {
                        newTarget->mTangents[vi] = origTarget->mTangents[previousIndex];
                        newTarget->mBitangents[vi] = origTarget->mBitangents[previousIndex];
                    }
                }
            }
        }

This seem to work for my basic tests at least, however I'm not sure if SplitByBoneCountProcess introduces new vertices, in which case morph targets would need to be somehow interpolated for new vertices as well

Would be great if you could prepare a PR for that fix.

Could you please provide some information how much bones are necessary? Maybe I can provide an example model.

Created PR 3512, I believe default AI_SBBC_DEFAULT_MAX_BONES is 60 so anything above it would go through splitting
Note, per GLTF spec all primitives must have same set of morph targets, but since not all vertices are shared by every primitive after split, some morph target would end up being empty. This might seem awkward but works correctly if user is handling all morph targets

Thanks a lot for the great job!

Closed by https://github.com/assimp/assimp/pull/3512

Was this page helpful?
0 / 5 - 0 ratings