#4080 Rigged mesh support #7

master
Andrey Kleshchev 2025-06-03 20:27:25 +03:00 committed by Andrey Kleshchev
parent b322b3f6f1
commit acd4de6658
2 changed files with 152 additions and 74 deletions

View File

@ -153,9 +153,9 @@ bool LLGLTFLoader::parseMeshes()
// Populate the joints from skins first.
// There's not many skins - and you can pretty easily iterate through the nodes from that.
for (auto& skin : mGLTFAsset.mSkins)
for (S32 i = 0; i < mGLTFAsset.mSkins.size(); i++)
{
populateJointFromSkin(skin);
populateJointFromSkin(i);
}
// Track how many times each mesh name has been used
@ -293,6 +293,8 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
base_name = "mesh_" + std::to_string(mesh_index);
}
LL_INFOS("GLTF_DEBUG") << "Processing model " << base_name << LL_ENDL;
if (instance_count > 0)
{
pModel->mLabel = base_name + "_copy_" + std::to_string(instance_count);
@ -672,16 +674,8 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skinIdx];
LLMeshSkinInfo& skin_info = pModel->mSkinInfo;
size_t jointCnt = gltf_skin.mJoints.size();
if (gltf_skin.mInverseBindMatrices >= 0 && jointCnt != gltf_skin.mInverseBindMatricesData.size())
{
LL_INFOS("GLTF_IMPORT") << "Bind matrices count mismatch joints count" << LL_ENDL;
LLSD args;
args["Message"] = "InvBindCountMismatch";
mWarningsArray.append(args);
}
std::vector<S32> gltfindex_to_joitindex_map;
size_t jointCnt = gltf_skin.mJoints.size();
gltfindex_to_joitindex_map.resize(jointCnt);
S32 replacement_index = 0;
@ -711,44 +705,24 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
skin_info.mJointNames.push_back(legal_name);
skin_info.mJointNums.push_back(-1);
if (i < gltf_skin.mInverseBindMatricesData.size())
// In scope of same skin multiple meshes reuse same bind matrices
skin_info.mInvBindMatrix.push_back(mInverseBindMatrices[skinIdx][i]);
// For alternate bind matrix, use the ORIGINAL joint transform (before rotation)
// Get the original joint node and use its matrix directly
// Todo: this seems blatantly wrong, it should have been rotated
glm::mat4 joint_mat = jointNode.mMatrix;
S32 root_joint = findValidRootJointNode(joint, gltf_skin); // skeleton can have multiple real roots
if (root_joint == joint)
{
// Use pre-computed coord_system_rotation instead of recreating it
LL::GLTF::mat4 gltf_mat = gltf_skin.mInverseBindMatricesData[i];
glm::mat4 original_bind_matrix = glm::inverse(gltf_mat);
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));
skin_info.mInvBindMatrix.push_back(LLMatrix4a(gltf_transform));
LL_INFOS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " val: " << gltf_transform << LL_ENDL;
// For alternate bind matrix, use the ORIGINAL joint transform (before rotation)
// Get the original joint node and use its matrix directly
S32 joint = gltf_skin.mJoints[i];
LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint];
glm::mat4 joint_mat = jointNode.mMatrix;
S32 root_joint = findValidRootJoint(joint, gltf_skin); // skeleton can have multiple real roots
if (root_joint == joint)
{
// This is very likely incomplete in some way.
// Root shouldn't be the only one to need full coordinate fix
joint_mat = coord_system_rotation * joint_mat;
}
LLMatrix4 original_joint_transform(glm::value_ptr(joint_mat));
LL_INFOS("GLTF_DEBUG") << "mAlternateBindMatrix name: " << legal_name << " val: " << original_joint_transform << LL_ENDL;
skin_info.mAlternateBindMatrix.push_back(LLMatrix4a(original_joint_transform));
}
else
{
// For gltf mInverseBindMatrices are optional, but not for viewer
// todo: get a model that triggers this
skin_info.mInvBindMatrix.push_back(LLMatrix4a(mJointList[legal_name])); // might need to be an 'identity'
skin_info.mAlternateBindMatrix.push_back(LLMatrix4a(mJointList[legal_name]));
// This is very likely incomplete in some way.
// Root shouldn't be the only one to need full coordinate fix
joint_mat = coord_system_rotation * joint_mat;
}
LLMatrix4 original_joint_transform(glm::value_ptr(joint_mat));
LL_INFOS("GLTF_DEBUG") << "mAlternateBindMatrix name: " << legal_name << " val: " << original_joint_transform << LL_ENDL;
skin_info.mAlternateBindMatrix.push_back(LLMatrix4a(original_joint_transform));
}
// Remap indices for pModel->mSkinWeights
@ -764,23 +738,71 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
return true;
}
void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin)
void LLGLTFLoader::populateJointFromSkin(S32 skin_idx)
{
LL_INFOS("GLTF_DEBUG") << "populateJointFromSkin: Processing " << skin.mJoints.size() << " joints" << LL_ENDL;
const LL::GLTF::Skin& skin = mGLTFAsset.mSkins[skin_idx];
for (auto joint : skin.mJoints)
LL_INFOS("GLTF_DEBUG") << "populateJointFromSkin: Processing skin " << skin_idx << " with " << skin.mJoints.size() << " joints" << LL_ENDL;
if (skin.mInverseBindMatrices > 0 && skin.mJoints.size() != skin.mInverseBindMatricesData.size())
{
auto jointNode = mGLTFAsset.mNodes[joint];
LL_INFOS("GLTF_IMPORT") << "Bind matrices count mismatch joints count" << LL_ENDL;
LLSD args;
args["Message"] = "InvBindCountMismatch";
mWarningsArray.append(args);
}
S32 joint_count = (S32)skin.mJoints.size();
S32 inverse_count = (S32)skin.mInverseBindMatricesData.size();
if (mInverseBindMatrices.size() <= skin_idx)
{
mInverseBindMatrices.resize(skin_idx + 1);
}
for (S32 i = 0; i < joint_count; i++)
{
S32 joint = skin.mJoints[i];
LL::GLTF::Node jointNode = mGLTFAsset.mNodes[joint];
std::string legal_name(jointNode.mName);
bool legal_joint = false;
if (mJointMap.find(legal_name) != mJointMap.end())
{
legal_name = mJointMap[legal_name];
legal_joint = true;
}
if (!legal_joint)
{
// add placeholder to not break index
LLMatrix4 gltf_transform;
gltf_transform.setIdentity();
mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform));
}
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 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));
LL_INFOS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " val: " << gltf_transform << LL_ENDL;
mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform));
}
else
{
// ignore unrecognized joint
LL_DEBUGS("GLTF") << "Ignoring joint: " << legal_name << LL_ENDL;
LLMatrix4 gltf_transform;
gltf_transform.setIdentity();
LL_INFOS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " val: " << gltf_transform << LL_ENDL;
mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform));
}
// todo: prepare mAlternateBindMatrix here
if (!legal_joint)
{
LL_DEBUGS("GLTF") << "Ignoring unrecognized joint: " << legal_name << LL_ENDL;
continue;
}
@ -812,14 +834,45 @@ void LLGLTFLoader::populateJointFromSkin(const LL::GLTF::Skin& skin)
}
}
S32 LLGLTFLoader::findValidRootJoint(S32 source_joint, const LL::GLTF::Skin& gltf_skin) const
S32 LLGLTFLoader::findClosestValidJoint(S32 source_joint, const LL::GLTF::Skin& gltf_skin) const
{
S32 root_joint = 0;
S32 found_joint = source_joint;
S32 source_joint_node = gltf_skin.mJoints[source_joint];
S32 root_node = source_joint_node;
S32 found_node = source_joint_node;
S32 size = (S32)gltf_skin.mJoints.size();
do
{
root_joint = found_joint;
root_node = found_node;
for (S32 i = 0; i < size; i++)
{
S32 joint = gltf_skin.mJoints[i];
const LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint];
std::vector<S32>::const_iterator it = std::find(jointNode.mChildren.begin(), jointNode.mChildren.end(), root_node);
if (it != jointNode.mChildren.end())
{
// Found node's parent
found_node = joint;
if (mJointMap.find(jointNode.mName) != mJointMap.end())
{
return i;
}
break;
}
}
} while (root_node != found_node);
return -1;
}
S32 LLGLTFLoader::findValidRootJointNode(S32 source_joint_node, const LL::GLTF::Skin& gltf_skin) const
{
S32 root_node = 0;
S32 found_node = source_joint_node;
S32 size = (S32)gltf_skin.mJoints.size();
do
{
root_node = found_node;
for (S32 i = 0; i < size; i++)
{
S32 joint = gltf_skin.mJoints[i];
@ -827,44 +880,61 @@ S32 LLGLTFLoader::findValidRootJoint(S32 source_joint, const LL::GLTF::Skin& glt
if (mJointMap.find(jointNode.mName) != mJointMap.end())
{
std::vector<S32>::const_iterator it = std::find(jointNode.mChildren.begin(), jointNode.mChildren.end(), root_joint);
std::vector<S32>::const_iterator it = std::find(jointNode.mChildren.begin(), jointNode.mChildren.end(), root_node);
if (it != jointNode.mChildren.end())
{
found_joint = joint;
// Found node's parent
found_node = joint;
break;
}
}
}
} while (root_joint != found_joint);
} while (root_node != found_node);
return root_joint;
return root_node;
}
S32 LLGLTFLoader::findGLTFRootJoint(const LL::GLTF::Skin& gltf_skin) const
S32 LLGLTFLoader::findGLTFRootJointNode(const LL::GLTF::Skin& gltf_skin) const
{
S32 root_joint = 0;
S32 found_joint = 0;
S32 root_node = 0;
S32 found_node = 0;
S32 size = (S32)gltf_skin.mJoints.size();
do
{
root_joint = found_joint;
root_node = found_node;
for (S32 i = 0; i < size; i++)
{
S32 joint = gltf_skin.mJoints[i];
const LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint];
std::vector<S32>::const_iterator it = std::find(jointNode.mChildren.begin(), jointNode.mChildren.end(), root_joint);
std::vector<S32>::const_iterator it = std::find(jointNode.mChildren.begin(), jointNode.mChildren.end(), root_node);
if (it != jointNode.mChildren.end())
{
found_joint = joint;
// Found node's parent
found_node = joint;
break;
}
}
} while (root_joint != found_joint);
} while (root_node != found_node);
LL_INFOS("GLTF_DEBUG") << "mJointList name: ";
const LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[root_joint];
LL_CONT << jointNode.mName << " index: " << root_joint << LL_ENDL;
return root_joint;
const LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[root_node];
LL_CONT << jointNode.mName << " index: " << root_node << LL_ENDL;
return root_node;
}
S32 LLGLTFLoader::findParentNode(S32 node) const
{
S32 size = (S32)mGLTFAsset.mNodes.size();
for (S32 i = 0; i < size; i++)
{
const LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[i];
std::vector<S32>::const_iterator it = std::find(jointNode.mChildren.begin(), jointNode.mChildren.end(), node);
if (it != jointNode.mChildren.end())
{
return i;
}
}
return -1;
}
bool LLGLTFLoader::parseMaterials()

View File

@ -121,6 +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;
LLGLTFLoader(std::string filename,
S32 lod,
@ -162,6 +163,11 @@ protected:
std::vector<gltf_image> mImages;
std::vector<gltf_sampler> mSamplers;
// vector of vectors because of a posibility of having more than one skin
typedef std::vector<LLMeshSkinInfo::matrix_list_t> bind_matrices_t;
bind_matrices_t mInverseBindMatrices;
private:
bool parseMeshes();
void uploadMeshes();
@ -169,9 +175,11 @@ private:
void uploadMaterials();
void computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) const;
bool populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh &mesh, const LL::GLTF::Node &node, material_map& mats, S32 instance_count);
void populateJointFromSkin(const LL::GLTF::Skin& skin);
S32 findValidRootJoint(S32 source_joint, const LL::GLTF::Skin& gltf_skin) const;
S32 findGLTFRootJoint(const LL::GLTF::Skin& gltf_skin) const; // if there are multiple roots, gltf stores them under one commor joint
void populateJointFromSkin(S32 skin_idx);
S32 findClosestValidJoint(S32 source_joint, const LL::GLTF::Skin& gltf_skin) const;
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;
LLUUID imageBufferToTextureUUID(const gltf_texture& tex);
void notifyUnsupportedExtension(bool unsupported);