summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llprimitive/llmodel.cpp87
-rw-r--r--indra/llprimitive/llmodel.h1
-rw-r--r--indra/newview/gltf/llgltfloader.cpp79
-rw-r--r--indra/newview/gltf/llgltfloader.h3
-rw-r--r--indra/newview/skins/default/xui/en/floater_model_preview.xml2
5 files changed, 78 insertions, 94 deletions
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index e47c0d8d07..3d31cfbb7f 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -112,93 +112,6 @@ void LLModel::remapVolumeFaces()
}
}
-void LLModel::remapSkinWeightsAndJoints()
-{
- if (mSkinWeights.empty())
- {
- return;
- }
-
- mPosition.clear();
-
- // Make a list of positions
- std::set<LLVector3> positions;
- for (S32 i = 0; i < getNumVolumeFaces(); ++i)
- {
- for (S32 j = 0; j < mVolumeFaces[i].mNumVertices; ++j)
- {
- positions.emplace(mVolumeFaces[i].mPositions[j].getF32ptr());
- }
- }
-
- // Build new list of weights and record used joints
- weight_map replacement_weights;
- std::vector<S32> joint_index_use_count;
- size_t joint_count = mSkinInfo.mJointNames.size();
- joint_index_use_count.resize(joint_count, 0);
- for (const LLVector3& pos : positions)
- {
- mPosition.push_back(pos);
- auto found = mSkinWeights.find(pos);
- if (found != mSkinWeights.end())
- {
- replacement_weights[pos] = found->second;
-
- for (auto& weight : found->second)
- {
- if (joint_count > weight.mJointIdx)
- {
- joint_index_use_count[weight.mJointIdx]++;
- }
- }
- }
- }
-
- // go over joint data and remap joints
- // prepare joint map
- std::vector<std::string> replacement_joint_names;
- std::vector<S32> replacement_joint_nums;
- LLMeshSkinInfo::matrix_list_t replacement_inv_bind;
- LLMeshSkinInfo::matrix_list_t replacement_alt_bind;
- std::vector<S32> index_map;
- index_map.resize(joint_count);
- S32 replacement_index = 0;
-
- for (S32 i = 0; i < joint_count; i++)
- {
- if (joint_index_use_count[i] > 0)
- {
- replacement_joint_names.push_back(mSkinInfo.mJointNames[i]);
- replacement_joint_nums.push_back(mSkinInfo.mJointNums[i]);
- replacement_inv_bind.push_back(mSkinInfo.mInvBindMatrix[i]);
- replacement_alt_bind.push_back(mSkinInfo.mAlternateBindMatrix[i]);
- index_map[i] = replacement_index++;
- }
- }
-
- // Apply new data
- mSkinInfo.mJointNames.clear();
- mSkinInfo.mJointNames = replacement_joint_names;
- mSkinInfo.mJointNums.clear();
- mSkinInfo.mJointNums = replacement_joint_nums;
- mSkinInfo.mInvBindMatrix.clear();
- mSkinInfo.mInvBindMatrix = replacement_inv_bind;
- mSkinInfo.mAlternateBindMatrix.clear();
- mSkinInfo.mAlternateBindMatrix = replacement_alt_bind;
-
- // remap weights
- for (auto& weights : replacement_weights)
- {
- for (auto& weight : weights.second)
- {
- weight.mJointIdx = index_map[weight.mJointIdx];
- }
- }
-
- mSkinWeights.clear();
- mSkinWeights = replacement_weights;
-}
-
void LLModel::optimizeVolumeFaces()
{
for (S32 i = 0; i < getNumVolumeFaces(); ++i)
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index 7fa3a00ee2..5c6d0a55d2 100644
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -205,7 +205,6 @@ public:
void normalizeVolumeFacesAndWeights();
void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL);
void remapVolumeFaces();
- void remapSkinWeightsAndJoints();
void optimizeVolumeFaces();
void offsetMesh( const LLVector3& pivotPoint );
void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out) const;
diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp
index 50ce075d65..e495abfe75 100644
--- a/indra/newview/gltf/llgltfloader.cpp
+++ b/indra/newview/gltf/llgltfloader.cpp
@@ -225,8 +225,6 @@ void LLGLTFLoader::addModelToScene(
{
// remove unused/redundant vertices
model->remapVolumeFaces();
- // remove unused/redundant weights and joints
- model->remapSkinWeightsAndJoints();
mModelList.push_back(model);
@@ -821,21 +819,25 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
&& vertices[i].weights.x > 0.f)
{
weight_list.push_back(LLModel::JointWeight(vertices[i].joints.x, vertices[i].weights.x));
+ gltf_joint_index_use[vertices[i].joints.x]++;
}
if (gltf_joint_index_use[vertices[i].joints.y] >= 0
&& vertices[i].weights.y > 0.f)
{
weight_list.push_back(LLModel::JointWeight(vertices[i].joints.y, vertices[i].weights.y));
+ gltf_joint_index_use[vertices[i].joints.y]++;
}
if (gltf_joint_index_use[vertices[i].joints.z] >= 0
&& vertices[i].weights.z > 0.f)
{
weight_list.push_back(LLModel::JointWeight(vertices[i].joints.z, vertices[i].weights.z));
+ gltf_joint_index_use[vertices[i].joints.z]++;
}
if (gltf_joint_index_use[vertices[i].joints.w] >= 0
&& vertices[i].weights.w > 0.f)
{
weight_list.push_back(LLModel::JointWeight(vertices[i].joints.w, vertices[i].weights.w));
+ gltf_joint_index_use[vertices[i].joints.w]++;
}
std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater());
@@ -1022,14 +1024,23 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
{
LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skinIdx];
LLMeshSkinInfo& skin_info = pModel->mSkinInfo;
+ S32 valid_joints_count = mValidJointsCount[skinIdx];
+ std::vector<S32> gltfindex_to_joitindex_map;
size_t jointCnt = gltf_skin.mJoints.size();
+ gltfindex_to_joitindex_map.resize(jointCnt);
S32 replacement_index = 0;
+ S32 stripped_valid_joints = 0;
for (size_t i = 0; i < jointCnt; ++i)
{
// Process joint name and idnex
S32 joint = gltf_skin.mJoints[i];
+ if (gltf_joint_index_use[i] < 0)
+ {
+ // unsupported (-1) joint, drop it
+ continue;
+ }
LL::GLTF::Node& jointNode = mGLTFAsset.mNodes[joint];
std::string legal_name(jointNode.mName);
@@ -1037,10 +1048,28 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
{
legal_name = mJointMap[legal_name];
}
- // else thanks to gltf_joint_index_usage any illegal
- // joint should have zero uses.
- // Add them anyway to preserve order, remapSkinWeightsAndJoints
- // will sort them out later
+ else
+ {
+ llassert(false); // should have been stopped by gltf_joint_index_use[i] == -1
+ continue;
+ }
+
+ if (valid_joints_count > LL_MAX_JOINTS_PER_MESH_OBJECT
+ && gltf_joint_index_use[i] == 0
+ && legal_name != "mPelvis")
+ {
+ // Unused (0) joint
+ // It's perfectly valid to have more joints than is in use
+ // Ex: sandals that make your legs digitigrade despite not skining to
+ // knees or the like.
+ // But if model is over limid, drop extras sans pelvis.
+ // Keeping 'pelvis' is a workaround to keep preview whole.
+ // Todo: consider improving this, either take as much as possible or
+ // ensure common parents/roots are included
+ continue;
+ }
+
+ gltfindex_to_joitindex_map[i] = replacement_index++;
skin_info.mJointNames.push_back(legal_name);
skin_info.mJointNums.push_back(-1);
@@ -1050,6 +1079,28 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh&
skin_info.mAlternateBindMatrix.push_back(mAlternateBindMatrices[skinIdx][i]);
}
+
+ if (skin_info.mInvBindMatrix.size() > LL_MAX_JOINTS_PER_MESH_OBJECT)
+ {
+ LL_WARNS("GLTF_IMPORT") << "Too many jonts in " << pModel->mLabel
+ << " Count: " << (S32)skin_info.mInvBindMatrix.size()
+ << " Limit:" << (S32)LL_MAX_JOINTS_PER_MESH_OBJECT << LL_ENDL;
+ LLSD args;
+ args["Message"] = "ModelTooManyJoint";
+ args["MODEL_NAME"] = pModel->mLabel;
+ args["JOINT_COUNT"] = (S32)skin_info.mInvBindMatrix.size();
+ args["MAX"] = (S32)LL_MAX_JOINTS_PER_MESH_OBJECT;
+ mWarningsArray.append(args);
+ }
+
+ // Remap indices for pModel->mSkinWeights
+ for (auto& weights : pModel->mSkinWeights)
+ {
+ for (auto& weight : weights.second)
+ {
+ weight.mJointIdx = gltfindex_to_joitindex_map[weight.mJointIdx];
+ }
+ }
}
return true;
@@ -1075,6 +1126,7 @@ void LLGLTFLoader::populateJointFromSkin(S32 skin_idx)
{
mInverseBindMatrices.resize(skin_idx + 1);
mAlternateBindMatrices.resize(skin_idx + 1);
+ mValidJointsCount.resize(skin_idx + 1, 0);
}
// fill up joints related data
@@ -1095,6 +1147,7 @@ void LLGLTFLoader::populateJointFromSkin(S32 skin_idx)
{
data.mName = mJointMap[jointNode.mName];
data.mIsValidViewerJoint = true;
+ mValidJointsCount[skin_idx]++;
}
else
{
@@ -1111,6 +1164,20 @@ void LLGLTFLoader::populateJointFromSkin(S32 skin_idx)
}
}
+ if (mValidJointsCount[skin_idx] > LL_MAX_JOINTS_PER_MESH_OBJECT)
+ {
+ LL_WARNS("GLTF_IMPORT") << "Too many jonts, will strip unused joints"
+ << " Count: " << mValidJointsCount[skin_idx]
+ << " Limit:" << (S32)LL_MAX_JOINTS_PER_MESH_OBJECT << LL_ENDL;
+
+ LLSD args;
+ args["Message"] = "SkinJointsOverLimit";
+ args["SKIN_INDEX"] = (S32)skin_idx;
+ args["JOINT_COUNT"] = mValidJointsCount[skin_idx];
+ args["MAX"] = (S32)LL_MAX_JOINTS_PER_MESH_OBJECT;
+ mWarningsArray.append(args);
+ }
+
// Go over viewer joints and build overrides
glm::mat4 ident(1.0);
for (auto &viewer_data : mViewerJointData)
diff --git a/indra/newview/gltf/llgltfloader.h b/indra/newview/gltf/llgltfloader.h
index aa77c39030..393df8c4ee 100644
--- a/indra/newview/gltf/llgltfloader.h
+++ b/indra/newview/gltf/llgltfloader.h
@@ -207,6 +207,9 @@ protected:
bind_matrices_t mInverseBindMatrices;
bind_matrices_t mAlternateBindMatrices;
+ // per skin joint count, needs to be tracked for the sake of limits check.
+ std::vector<S32> mValidJointsCount;
+
private:
bool parseMeshes();
void uploadMeshes();
diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml
index 6c177e5008..4c60320a94 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -72,6 +72,8 @@
<string name="UnsupportedExtension">Unable to load model, unsupported extension: [EXT]</string>
<string name="TooManyMeshParts">Model contains [PART_COUNT] mesh parts. Maximum allowed: [LIMIT]</string>
<string name="FailedToCreateTempFile">Failed to create temporary file for embedded texture [TEXTURE_INDEX]: [TEMP_FILE]</string>
+ <string name="SkinJointsOverLimit">Skin [SKIN_INDEX] defines [JOINT_COUNT] compatible joints, maximum is: [MAX]. Unused joints will be stripped on per model basis.</string>
+ <string name="ModelTooManyJoint">Model [MODEL_NAME] uses [JOINT_COUNT], maximum: [MAX], upload might fail</string>
<panel
follows="top|left"