From c00bfab1a5f4d3f91efd771820553d876ef9bdfc Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 07:26:26 -0700 Subject: [PATCH] fix(wom): scrub NaN bone pivots and clamp parent indices at save time Symmetric with the existing load-side guards. A bone with a NaN pivot poisons its child bones' world matrices; an out-of-range parent index would walk past the bones array during evaluation. --- src/pipeline/wowee_model.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/pipeline/wowee_model.cpp b/src/pipeline/wowee_model.cpp index 3e45569f..54ed330e 100644 --- a/src/pipeline/wowee_model.cpp +++ b/src/pipeline/wowee_model.cpp @@ -307,9 +307,19 @@ bool WoweeModelLoader::save(const WoweeModel& model, const std::string& basePath uint32_t boneCount = static_cast(model.bones.size()); f.write(reinterpret_cast(&boneCount), 4); for (const auto& bone : model.bones) { + // Symmetric scrub with load — pivot NaN propagates through + // skeleton matrices to every child bone; parent indices outside + // bone array would walk off the end during matrix evaluation. + glm::vec3 pivot = bone.pivot; + if (!std::isfinite(pivot.x)) pivot.x = 0.0f; + if (!std::isfinite(pivot.y)) pivot.y = 0.0f; + if (!std::isfinite(pivot.z)) pivot.z = 0.0f; + int16_t parent = bone.parentBone; + if (parent >= 0 && static_cast(parent) >= boneCount) + parent = -1; f.write(reinterpret_cast(&bone.keyBoneId), 4); - f.write(reinterpret_cast(&bone.parentBone), 2); - f.write(reinterpret_cast(&bone.pivot), 12); + f.write(reinterpret_cast(&parent), 2); + f.write(reinterpret_cast(&pivot), 12); f.write(reinterpret_cast(&bone.flags), 4); }