#4148 Skeleton Translation

master
Andrey Kleshchev 2025-06-04 16:39:51 +03:00 committed by Andrey Kleshchev
parent 6924862d2e
commit b20d10c0cc
5 changed files with 139 additions and 15 deletions

View File

@ -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;
@ -105,11 +106,14 @@ public:
S32 getNumBones() const { return mNumBones; }
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

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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)