diff options
| author | Gaia <gaia.clary@machinimatrix.org> | 2026-05-10 00:57:41 +0200 |
|---|---|---|
| committer | Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> | 2026-05-18 23:37:50 +0300 |
| commit | 447f30781ef7a42a5f72971d713ea716befedf95 (patch) | |
| tree | 99d38ee3d31807b00b84cb38c0df3d035f037d8c /indra/newview/gltf/llgltfloader.cpp | |
| parent | 5f046e1233e25f711f66a0bd6265313721f0ec7d (diff) | |
Fix GLTF bind matrix remapping
Diffstat (limited to 'indra/newview/gltf/llgltfloader.cpp')
| -rw-r--r-- | indra/newview/gltf/llgltfloader.cpp | 93 |
1 files changed, 86 insertions, 7 deletions
diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp index 4e52c82e86..129c3a3b7a 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -1398,6 +1398,24 @@ void LLGLTFLoader::populateJointsFromSkin(S32 skin_idx) buildOverrideMatrix(viewer_data, joints_data, names_to_nodes, ident, ident); } + // Precompute the source bind pose in viewer coordinate space so each + // joint can later be remapped against its own GLTF rest matrix. This + // keeps bind-pose deltas local to the joint hierarchy. Without this, + // a source skeleton whose parent joints carry bind-pose rotations that + // the viewer skeleton does not have can leak those rotations into + // child joints with different local bases. + joint_node_mat4_map_t rotated_bind_matrices; + joint_node_mat4_map_t converted_bind_matrices; + if (inverse_count > 0) + { + for (S32 i = 0; i < joint_count && i < inverse_count; i++) + { + S32 joint = skin.mJoints[i]; + glm::mat4 original_bind_matrix = glm::inverse(skin.mInverseBindMatricesData[i]); + rotated_bind_matrices[joint] = rotateGltfMatrixToViewerSpace(original_bind_matrix); + } + } + for (S32 i = 0; i < joint_count; i++) { S32 joint = skin.mJoints[i]; @@ -1430,12 +1448,19 @@ void LLGLTFLoader::populateJointsFromSkin(S32 skin_idx) } else if (inverse_count > i) { - // Transalte existing bind matrix to viewer's overriden skeleton - glm::mat4 original_bind_matrix = glm::inverse(skin.mInverseBindMatricesData[i]); - glm::mat4 rotated_original = coord_system_rotation * original_bind_matrix; - glm::mat4 skeleton_transform = computeGltfToViewerSkeletonTransform(joints_data, joint, legal_name); - glm::mat4 tranlated_original = skeleton_transform * rotated_original; - glm::mat4 final_inverse_bind_matrix = glm::inverse(tranlated_original); + // Translate existing bind matrices to the viewer skeleton by + // preserving each joint's bind-pose delta from its own GLTF rest + // matrix. The previous world-space remap applied one skeleton + // transform directly to each bind matrix; when a source parent has + // bind-pose rotation that the viewer parent does not, that rotation + // could get baked into child inverse binds. Keeping the delta local + // fixes the resulting child joint twists. + glm::mat4 translated_original = computeViewerBindMatrix( + joints_data, + rotated_bind_matrices, + joint, + converted_bind_matrices); + glm::mat4 final_inverse_bind_matrix = glm::inverse(translated_original); LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(final_inverse_bind_matrix)); LL_DEBUGS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " Translated val: " << gltf_transform << LL_ENDL; @@ -1658,6 +1683,61 @@ glm::mat4 LLGLTFLoader::buildGltfRestMatrix(S32 joint_node_index, const joints_d return data.mGltfMatrix; } +// Convert a GLTF-space transform into the viewer coordinate space used by +// skeleton override and bind-pose calculations. Keeping this conversion in +// one helper ensures bind and rest matrices use the same rotation path, +// including the optional XY rotation applied for compatible uploads. +glm::mat4 LLGLTFLoader::rotateGltfMatrixToViewerSpace(const glm::mat4& gltf_matrix) const +{ + glm::mat4 rotated = coord_system_rotation * gltf_matrix; + if (mApplyXYRotation) + { + rotated = coord_system_rotationxy * rotated; + } + return rotated; +} + +// Convert one GLTF bind matrix to the matching viewer bind matrix. The +// function derives the joint-local bind delta from the GLTF bind and rest +// matrices, then applies that same delta to the viewer override rest matrix. +// Results are cached because child calculations can request the same joint +// more than once while preserving local hierarchy behavior. +glm::mat4 LLGLTFLoader::computeViewerBindMatrix( + const joints_data_map_t& joints_data_map, + const joint_node_mat4_map_t& rotated_bind_matrices, + S32 gltf_node_index, + joint_node_mat4_map_t& converted_bind_matrices) const +{ + auto cached = converted_bind_matrices.find(gltf_node_index); + if (cached != converted_bind_matrices.end()) + { + return cached->second; + } + + auto node_iter = joints_data_map.find(gltf_node_index); + if (node_iter == joints_data_map.end()) + { + return glm::mat4(1.f); + } + + const JointNodeData& node_data = node_iter->second; + auto bind_iter = rotated_bind_matrices.find(gltf_node_index); + if (bind_iter == rotated_bind_matrices.end() || !node_data.mIsOverrideValid) + { + converted_bind_matrices[gltf_node_index] = node_data.mOverrideRestMatrix; + return node_data.mOverrideRestMatrix; + } + + glm::mat4 gltf_joint_node = rotateGltfMatrixToViewerSpace(node_data.mGltfRestMatrix); + glm::mat4 gltf_joint_bind = bind_iter->second; + glm::mat4 viewer_joint_node = node_data.mOverrideRestMatrix; + glm::mat4 bind_delta = gltf_joint_bind * glm::inverse(gltf_joint_node); + glm::mat4 viewer_joint_bind = bind_delta * viewer_joint_node; + + converted_bind_matrices[gltf_node_index] = viewer_joint_bind; + return viewer_joint_bind; +} + // This function computes the transformation matrix needed to convert from GLTF skeleton space // to viewer skeleton space for a specific joint @@ -1910,4 +1990,3 @@ std::string LLGLTFLoader::getLodlessLabel(const LL::GLTF::Node& node) } return node.mName; } - |
