Skip to content

Fix crash importing skinned meshes without animations (e.g. bandit.fbx) - #237

Merged
fernandotonon merged 3 commits into
masterfrom
fix/import-skinned-mesh-without-animations
Apr 1, 2026
Merged

Fix crash importing skinned meshes without animations (e.g. bandit.fbx)#237
fernandotonon merged 3 commits into
masterfrom
fix/import-skinned-mesh-without-animations

Conversation

@fernandotonon

@fernandotonon fernandotonon commented Mar 31, 2026

Copy link
Copy Markdown
Owner

Summary

  • Bug: Hard crash (SIGSEGV) when importing any FBX/DAE/etc. that has a skinned mesh (bones + vertex weights) but no animation tracks.
  • Root cause: Importer.cpp only created the Ogre skeleton when scene->HasAnimations(), so a bind-pose-only rigged model left skeleton = nullptr. MeshProcessor::processMesh then called skeleton->getBone() on that null pointer.
  • Fix: Create the skeleton whenever any mesh has bones (mNumBones > 0), not just when animations are present. Also added a hasBone() guard in MeshProcessor as a safety net.

Test plan

  • Import bandit.fbx (116-bone skinned character, no animations) — should succeed
  • Import animated FBX (e.g. Rumba Dancing) — should still work
  • Import static mesh (no bones) — should still work

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Skeletons and binding poses are now created for skinned meshes with bones even when no animation data exists.
    • Additional validation prevents errors when processing bone weights with missing or mismatched bone references.
  • Chores

    • Project version bumped to 2.18.1.

The skeleton was only created when scene->HasAnimations(), but a mesh can
be fully skinned (bones + weights) without any animation tracks. When
MeshProcessor tried to call skeleton->getBone() for a null skeleton,
the result was a SIGSEGV.

Fix: create the skeleton whenever any mesh in the scene has bones, not
just when animations are present. Also add a hasBone() guard in
MeshProcessor so a missing bone entry never hard-crashes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5a8b3ed4-1ba6-431d-a3b7-b6297141ea73

📥 Commits

Reviewing files that changed from the base of the PR and between e3d7fd2 and c941868.

📒 Files selected for processing (1)
  • CMakeLists.txt
✅ Files skipped from review due to trivial changes (1)
  • CMakeLists.txt

📝 Walkthrough

Walkthrough

Skeleton and binding-pose creation was decoupled from animation presence: skeletons are now created when a scene has animations or skinned meshes with bones. Animation track processing remains executed only when scene->HasAnimations() is true. Bone-weight assignment now validates bone existence on the skeleton before using it.

Changes

Cohort / File(s) Summary
Importer — Skeleton & Animation gating
src/Assimp/Importer.cpp
Expanded skeleton creation condition to run when the scene has animations OR contains skinned meshes with bones (mNumBones > 0). Separated animation processing so AnimationProcessor::processAnimations(scene) runs only if scene->HasAnimations() is true.
MeshProcessor — Bone-weight validation
src/Assimp/MeshProcessor.cpp
Added checks to skip bone-weight processing when skeleton is null or does not contain the referenced bone name, preventing invalid bone lookups.
Build metadata
CMakeLists.txt
Bumped project version from 2.18.0 to 2.18.1.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped through code with nimble paws,

Found bones that waited without applause.
Now skeletons rise though no dances play,
Binding poses set, in clay they stay.
A little rabbit cheers—hip, hop, hooray! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly summarizes the main fix: addressing a crash when importing skinned meshes without animations, with a specific example (bandit.fbx).
Description check ✅ Passed The PR description provides a clear summary of the bug, root cause, and fix, along with a concrete test plan. It follows the template structure with proper sections.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/import-skinned-mesh-without-animations

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Assimp/Importer.cpp`:
- Around line 78-85: The skeleton member/variable must be cleared before the
conditional creation to avoid carrying a previous skeleton into MeshProcessor;
update Importer.cpp to reset skeleton (e.g., set to nullptr/clear) immediately
before the if(hasBones || scene->HasAnimations()) block so that when the block
is skipped MeshProcessor meshProcessor(skeleton) sees an empty/null skeleton and
no stale skeleton is bound; ensure references to
Ogre::SkeletonManager::getSingleton().create(modelName+".skeleton", ...) remain
unchanged and only the pre-check reset of skeleton is added.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 56c21850-e1e8-42cc-a801-3e47b650fbef

📥 Commits

Reviewing files that changed from the base of the PR and between df4cdac and e3d7fd2.

📒 Files selected for processing (2)
  • src/Assimp/Importer.cpp
  • src/Assimp/MeshProcessor.cpp

Comment thread src/Assimp/Importer.cpp
Comment on lines +78 to 85
// Process the skeleton whenever the scene has bones (skinned mesh) or animations.
// A mesh can be skinned without having any animations (e.g. a rigged bind-pose).
bool hasBones = false;
for(unsigned i = 0; i < scene->mNumMeshes && !hasBones; ++i)
hasBones = scene->mMeshes[i]->mNumBones > 0;

if(hasBones || scene->HasAnimations()) {
skeleton = Ogre::SkeletonManager::getSingleton().create(modelName+".skeleton", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Reset skeleton before conditional creation to avoid stale state across imports.

When this block is skipped (no bones and no animations), skeleton can keep a previous value and still flow into Line 103 (MeshProcessor meshProcessor(skeleton)), then be rebound at mesh creation time. That can incorrectly link a static mesh to an old skeleton.

Proposed fix
 Ogre::MeshPtr AssimpToOgreImporter::loadModel(const std::string& path, bool convertToLeftHanded, unsigned int additionalFlags) {
+    // Clear importer state from previous loads.
+    skeleton = Ogre::SkeletonPtr();
+
     importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false);
@@
-    if(hasBones || scene->HasAnimations()) {
+    if(hasBones || scene->HasAnimations()) {
         skeleton = Ogre::SkeletonManager::getSingleton().create(modelName+".skeleton", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Assimp/Importer.cpp` around lines 78 - 85, The skeleton member/variable
must be cleared before the conditional creation to avoid carrying a previous
skeleton into MeshProcessor; update Importer.cpp to reset skeleton (e.g., set to
nullptr/clear) immediately before the if(hasBones || scene->HasAnimations())
block so that when the block is skipped MeshProcessor meshProcessor(skeleton)
sees an empty/null skeleton and no stale skeleton is bound; ensure references to
Ogre::SkeletonManager::getSingleton().create(modelName+".skeleton", ...) remain
unchanged and only the pre-check reset of skeleton is added.

@fernandotonon
fernandotonon merged commit 746c0fc into master Apr 1, 2026
4 checks passed
@fernandotonon
fernandotonon deleted the fix/import-skinned-mesh-without-animations branch April 1, 2026 02:33
@sonarqubecloud

sonarqubecloud Bot commented Apr 1, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant