diff options
-rw-r--r-- | indra/llprimitive/llmodel.cpp | 87 | ||||
-rw-r--r-- | indra/llprimitive/llmodel.h | 1 | ||||
-rw-r--r-- | indra/newview/gltf/llgltfloader.cpp | 79 | ||||
-rw-r--r-- | indra/newview/gltf/llgltfloader.h | 3 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_model_preview.xml | 2 |
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" |