fix: handle non-contiguous xmodel root bones (#1009)

* fix: handle non-contiguous xmodel root bones

* chore: add comment about invalid parent index fix

---------

Co-authored-by: Jan Laupetin <[email protected]>
This commit is contained in:
moandJan Laupetin authored and GitHub committed 2026-09-21 21:23:25 +02:00
1 parent fc1a0bbc38
commit 209d0105c3
3 files changed
+117 -10

No files matched your search

+7 -9
View File
@@ -189,8 +189,8 @@ namespace
for (auto meshIndex = 0u; meshIndex < meshCount; meshIndex++)
rootNode.children->emplace_back(m_first_mesh_node + meshIndex);
for (auto rootBoneIndex = 0u; rootBoneIndex < m_root_bone_count; rootBoneIndex++)
rootNode.children->emplace_back(m_first_bone_node + rootBoneIndex);
for (const auto rootBoneNode : m_root_bone_nodes)
rootNode.children->emplace_back(rootBoneNode);
m_root_node = static_cast<unsigned>(gltf.nodes->size());
gltf.nodes->emplace_back(std::move(rootNode));
@@ -303,7 +303,7 @@ namespace
const auto boneCount = common.m_bones.size();
m_first_bone_node = static_cast<unsigned>(gltf.nodes->size());
m_root_bone_count = 0;
m_root_bone_nodes.clear();
for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++)
{
JsonNode boneNode;
@@ -337,10 +337,7 @@ namespace
rotation = inverseParentRotation * rotation;
}
else
{
assert(m_root_bone_count == boneIndex);
m_root_bone_count++;
}
m_root_bone_nodes.emplace_back(m_first_bone_node + boneIndex);
rotation.normalize();
boneNode.name = bone.name;
@@ -380,7 +377,8 @@ namespace
if (!xmodel.m_bone_weight_data.weights.empty())
skin.inverseBindMatrices = m_inverse_bind_matrices_accessor;
skin.skeleton = m_first_bone_node;
if (m_root_bone_nodes.size() == 1u)
skin.skeleton = m_root_bone_nodes[0];
gltf.skins->emplace_back(std::move(skin));
}
@@ -751,7 +749,7 @@ namespace
unsigned m_first_mesh_node = 0u;
unsigned m_root_node = 0u;
unsigned m_first_bone_node = 0u;
unsigned m_root_bone_count = 0u;
std::vector<unsigned> m_root_bone_nodes;
unsigned m_position_accessor = 0u;
unsigned m_normal_accessor = 0u;
unsigned m_color_accessor = 0u;
@@ -37,6 +37,7 @@
#include <cassert>
#include <format>
#include <string_view>
#include <utility>
using namespace GAME;
@@ -258,7 +259,19 @@ namespace
bone.name = "INVALID_BONE_NAME";
if (boneNum >= model.numRootBones)
bone.parentIndex = static_cast<int>(boneNum - static_cast<unsigned int>(model.parentList[boneNum - model.numRootBones]));
{
const auto parentIndex = static_cast<int>(boneNum) - static_cast<int>(model.parentList[boneNum - model.numRootBones]);
if (parentIndex >= 0 && std::cmp_less(parentIndex, model.numBones))
{
bone.parentIndex = static_cast<unsigned>(parentIndex);
}
else
{
// This means that the parent index is invalid. Which should never happen... but it does.
// For some custom maps... We will pretend it is a root bone in that case.
bone.parentIndex = std::nullopt;
}
}
else
bone.parentIndex = std::nullopt;