summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Kleshchev <andreykproductengine@lindenlab.com>2025-06-04 16:39:51 +0300
committerAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2025-06-09 20:06:01 +0300
commitb20d10c0cc96cfcd93468b8e31e47ab1977a9555 (patch)
treec7327409c1d4321b41623b35137037998f3dd1f7
parent6924862d2ef1ef6af6a04c013aebeecbb5717bde (diff)
#4148 Skeleton Translation
-rw-r--r--indra/llappearance/llavatarappearance.cpp47
-rw-r--r--indra/llappearance/llavatarappearance.h4
-rw-r--r--indra/newview/gltf/llgltfloader.cpp87
-rw-r--r--indra/newview/gltf/llgltfloader.h11
-rw-r--r--indra/newview/llmodelpreview.cpp5
5 files changed, 139 insertions, 15 deletions
diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp
index 3d66809ed6..34d6b4db83 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -34,11 +34,11 @@
#include "llpolymorph.h"
#include "llpolymesh.h"
#include "llpolyskeletaldistortion.h"
-#include "llstl.h"
#include "lltexglobalcolor.h"
#include "llwearabledata.h"
#include "boost/bind.hpp"
#include "boost/tokenizer.hpp"
+#include "v4math.h"
using namespace LLAvatarAppearanceDefines;
@@ -71,6 +71,7 @@ public:
mChildren.clear();
}
bool parseXml(LLXmlTreeNode* node);
+ glm::mat4 getJointMatrix();
private:
std::string mName;
@@ -106,10 +107,13 @@ public:
S32 getNumCollisionVolumes() const { return mNumCollisionVolumes; }
private:
+ typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
+ static void getJointRestMatrices(const bone_info_list_t& bone_list, LLAvatarAppearance::joint_rest_map_t& result, const glm::mat4 &parent_mat);
+
+private:
S32 mNumBones;
S32 mNumCollisionVolumes;
LLAvatarAppearance::joint_alias_map_t mJointAliasMap;
- typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
bone_info_list_t mBoneInfoList;
};
@@ -1623,6 +1627,21 @@ bool LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
return true;
}
+
+glm::mat4 LLAvatarBoneInfo::getJointMatrix()
+{
+ glm::mat4 mat(1.0f);
+ // 1. Scaling
+ mat = glm::scale(mat, glm::vec3(mScale[0], mScale[1], mScale[2]));
+ // 2. Rotation (Euler angles rad)
+ mat = glm::rotate(mat, mRot[0], glm::vec3(1, 0, 0));
+ mat = glm::rotate(mat, mRot[1], glm::vec3(0, 1, 0));
+ mat = glm::rotate(mat, mRot[2], glm::vec3(0, 0, 1));
+ // 3. Position
+ mat = glm::translate(mat, glm::vec3(mPos[0], mPos[1], mPos[2]));
+ return mat;
+}
+
//-----------------------------------------------------------------------------
// LLAvatarSkeletonInfo::parseXml()
//-----------------------------------------------------------------------------
@@ -1653,6 +1672,22 @@ bool LLAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
return true;
}
+void LLAvatarSkeletonInfo::getJointRestMatrices(
+ const bone_info_list_t& bone_list,
+ LLAvatarAppearance::joint_rest_map_t& result,
+ const glm::mat4& parent_mat)
+{
+ for (LLAvatarBoneInfo* bone_info : bone_list)
+ {
+ if (bone_info->mIsJoint)
+ {
+ glm::mat4 rest_mat = parent_mat * bone_info->getJointMatrix();
+ result[bone_info->mName] = rest_mat;
+ getJointRestMatrices(bone_info->mChildren, result, rest_mat);
+ }
+ }
+}
+
//Make aliases for joint and push to map.
void LLAvatarAppearance::makeJointAliases(LLAvatarBoneInfo *bone_info)
{
@@ -1714,6 +1749,14 @@ const LLAvatarAppearance::joint_alias_map_t& LLAvatarAppearance::getJointAliases
return mJointAliasMap;
}
+LLAvatarAppearance::joint_rest_map_t LLAvatarAppearance:: getJointRestMatrices() const
+{
+ LLAvatarAppearance::joint_rest_map_t result;
+ glm::mat4 identity(1.f);
+ LLAvatarSkeletonInfo::getJointRestMatrices(sAvatarSkeletonInfo->mBoneInfoList, result, identity);
+ return result;
+}
+
//-----------------------------------------------------------------------------
// parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree
diff --git a/indra/llappearance/llavatarappearance.h b/indra/llappearance/llavatarappearance.h
index 13e504e639..cea2a837cd 100644
--- a/indra/llappearance/llavatarappearance.h
+++ b/indra/llappearance/llavatarappearance.h
@@ -34,6 +34,7 @@
#include "lltexlayer.h"
#include "llviewervisualparam.h"
#include "llxmltree.h"
+#include "v4math.h"
class LLTexLayerSet;
class LLTexGlobalColor;
@@ -153,7 +154,8 @@ public:
const avatar_joint_list_t& getSkeleton() { return mSkeleton; }
typedef std::map<std::string, std::string> joint_alias_map_t;
const joint_alias_map_t& getJointAliases();
-
+ typedef std::map<std::string, glm::mat4> joint_rest_map_t;
+ joint_rest_map_t getJointRestMatrices() const;
protected:
static bool parseSkeletonFile(const std::string& filename, LLXmlTree& skeleton_xml_tree);
diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp
index 17de6eb829..7cf26941ad 100644
--- a/indra/newview/gltf/llgltfloader.cpp
+++ b/indra/newview/gltf/llgltfloader.cpp
@@ -87,7 +87,8 @@ LLGLTFLoader::LLGLTFLoader(std::string filename,
JointNameSet & jointsFromNodes,
std::map<std::string, std::string> &jointAliasMap,
U32 maxJointsPerMesh,
- U32 modelLimit) //,
+ U32 modelLimit,
+ joint_rest_map_t jointRestMatrices) //,
//bool preprocess)
: LLModelLoader( filename,
lod,
@@ -103,7 +104,8 @@ LLGLTFLoader::LLGLTFLoader(std::string filename,
//mPreprocessGLTF(preprocess),
mMeshesLoaded(false),
mMaterialsLoaded(false),
- mGeneratedModelLimit(modelLimit)
+ mGeneratedModelLimit(modelLimit),
+ mJointRestMatrices(jointRestMatrices)
{
}
@@ -917,20 +919,24 @@ void LLGLTFLoader::populateJointFromSkin(S32 skin_idx)
}
else if (inverse_count > i)
{
- LL::GLTF::mat4 gltf_mat = skin.mInverseBindMatricesData[i];
-
- // Todo: there should be a simpler way
- glm::mat4 original_bind_matrix = glm::inverse(gltf_mat);
+ glm::mat4 original_bind_matrix = glm::inverse(skin.mInverseBindMatricesData[i]);
glm::mat4 rotated_original = coord_system_rotation * original_bind_matrix;
- glm::mat4 rotated_inverse_bind_matrix = glm::inverse(rotated_original);
- LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(rotated_inverse_bind_matrix));
+ glm::mat4 skeleton_transform = computeGltfToViewerSkeletonTransform(skin, i, legal_name);
+ glm::mat4 tranlated_original = skeleton_transform * rotated_original;
+ glm::mat4 final_inverse_bind_matrix = glm::inverse(tranlated_original);
+
+ LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(final_inverse_bind_matrix));
LL_INFOS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " val: " << gltf_transform << LL_ENDL;
mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform));
}
else
{
- LLMatrix4 gltf_transform;
+ glm::mat4 inv_bind(1.0f);
+ glm::mat4 skeleton_transform = computeGltfToViewerSkeletonTransform(skin, i, legal_name);
+ inv_bind = glm::inverse(skeleton_transform * inv_bind);
+
+ LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(inv_bind));
gltf_transform.setIdentity();
LL_INFOS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " val: " << gltf_transform << LL_ENDL;
mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform));
@@ -1074,6 +1080,69 @@ S32 LLGLTFLoader::findParentNode(S32 node) const
return -1;
}
+glm::mat4 LLGLTFLoader::buildGltfRestMatrix(S32 joint_node_index, const LL::GLTF::Skin& gltf_skin) const
+{
+ // This is inefficient since we are recalculating some joints multiple times over
+ // Todo: cache it?
+
+ if (joint_node_index < 0 || joint_node_index >= static_cast<S32>(mGLTFAsset.mNodes.size()))
+ {
+ return glm::mat4(1.0f);
+ }
+
+ const auto& node = mGLTFAsset.mNodes[joint_node_index];
+
+ // Find and apply parent transform if it exists
+ for (size_t i = 0; i < mGLTFAsset.mNodes.size(); ++i)
+ {
+ const auto& potential_parent = mGLTFAsset.mNodes[i];
+ auto it = std::find(potential_parent.mChildren.begin(), potential_parent.mChildren.end(), joint_node_index);
+
+ if (it != potential_parent.mChildren.end())
+ {
+ // Found parent
+ if (std::find(gltf_skin.mJoints.begin(), gltf_skin.mJoints.end(), joint_node_index) != gltf_skin.mJoints.end())
+ {
+ // parent is a joint - recursively combine transform
+ // assumes that matrix is already valid
+ return buildGltfRestMatrix(static_cast<S32>(i), gltf_skin) * node.mMatrix;
+ }
+ }
+ }
+ // Should we return armature or stop earlier?
+ return node.mMatrix;
+}
+
+// This function computes the transformation matrix needed to convert from GLTF skeleton space
+// to viewer skeleton space for a specific joint
+glm::mat4 LLGLTFLoader::computeGltfToViewerSkeletonTransform(const LL::GLTF::Skin& gltf_skin, S32 gltf_joint_index, const std::string& joint_name) const
+{
+ joint_rest_map_t::const_iterator found = mJointRestMatrices.find(joint_name);
+ if (found == mJointRestMatrices.end())
+ {
+ // For now assume they are identical and return an identity (for ease of debuging)
+ // But there should be no joints viewer isn't aware about
+ // Warn or assert about missing joints
+ return glm::mat4(1.0f);
+ }
+ glm::mat4 viewer_joint_rest_pose = found->second;
+
+ // Get the GLTF joint's rest pose (in GLTF coordinate system)
+ S32 joint_node_index = gltf_skin.mJoints[gltf_joint_index];
+ glm::mat4 gltf_joint_rest_pose = buildGltfRestMatrix(joint_node_index, gltf_skin);
+ gltf_joint_rest_pose = coord_system_rotation * gltf_joint_rest_pose;
+
+ LL_INFOS("GLTF_DEBUG") << "rest matrix for joint " << joint_name << ": ";
+
+ LLMatrix4 transform(glm::value_ptr(gltf_joint_rest_pose));
+
+ LL_CONT << transform << LL_ENDL;
+
+ // Compute transformation from GLTF space to viewer space
+ // This assumes both skeletons are in rest pose initially
+ return viewer_joint_rest_pose * glm::inverse(gltf_joint_rest_pose);
+}
+
bool LLGLTFLoader::parseMaterials()
{
if (!mGltfLoaded) return false;
diff --git a/indra/newview/gltf/llgltfloader.h b/indra/newview/gltf/llgltfloader.h
index 19337c24aa..41e6a2dd6d 100644
--- a/indra/newview/gltf/llgltfloader.h
+++ b/indra/newview/gltf/llgltfloader.h
@@ -121,7 +121,7 @@ class LLGLTFLoader : public LLModelLoader
{
public:
typedef std::map<std::string, LLImportMaterial> material_map;
- typedef std::map<std::string, LLVector3> joint_pos_map_t;
+ typedef std::map<std::string, glm::mat4> joint_rest_map_t;
LLGLTFLoader(std::string filename,
S32 lod,
@@ -134,7 +134,8 @@ class LLGLTFLoader : public LLModelLoader
JointNameSet & jointsFromNodes,
std::map<std::string, std::string> &jointAliasMap,
U32 maxJointsPerMesh,
- U32 modelLimit); //,
+ U32 modelLimit,
+ joint_rest_map_t jointRestMatrices); //,
//bool preprocess );
virtual ~LLGLTFLoader();
@@ -164,6 +165,10 @@ protected:
std::vector<gltf_image> mImages;
std::vector<gltf_sampler> mSamplers;
+ // GLTF isn't aware of viewer's skeleton and uses it's own,
+ // so need to take viewer's joints and use them to
+ // recalculate iverse bind matrices
+ joint_rest_map_t mJointRestMatrices;
// vector of vectors because of a posibility of having more than one skin
typedef std::vector<LLMeshSkinInfo::matrix_list_t> bind_matrices_t;
@@ -183,6 +188,8 @@ private:
S32 findValidRootJointNode(S32 source_joint_node, const LL::GLTF::Skin& gltf_skin) const;
S32 findGLTFRootJointNode(const LL::GLTF::Skin& gltf_skin) const; // if there are multiple roots, gltf stores them under one commor joint
S32 findParentNode(S32 node) const;
+ glm::mat4 buildGltfRestMatrix(S32 joint_node_index, const LL::GLTF::Skin& gltf_skin) const;
+ glm::mat4 computeGltfToViewerSkeletonTransform(const LL::GLTF::Skin& gltf_skin, S32 joint_index, const std::string& joint_name) const;
LLUUID imageBufferToTextureUUID(const gltf_texture& tex);
void notifyUnsupportedExtension(bool unsupported);
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 2d568ecb8b..40b6d186b8 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -810,6 +810,8 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
}
else
{
+ LLVOAvatar* av = getPreviewAvatar();
+ LLAvatarAppearance::joint_rest_map_t rest_pose = av->getJointRestMatrices();
mModelLoader = new LLGLTFLoader(
filename,
lod,
@@ -822,7 +824,8 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
mJointsFromNode,
joint_alias_map,
LLSkinningUtil::getMaxJointCount(),
- gSavedSettings.getU32("ImporterModelLimit"));
+ gSavedSettings.getU32("ImporterModelLimit"),
+ rest_pose);
}
if (force_disable_slm)