diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index c67dac27334..bc9f62b6c0f 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -112,6 +112,93 @@ void LLModel::remapVolumeFaces() } } +void LLModel::remapSkinWeightsAndJoints() +{ + if (mSkinWeights.empty()) + { + return; + } + + mPosition.clear(); + + // Make a list of positions + std::set 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 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 replacement_joint_names; + std::vector replacement_joint_nums; + LLMeshSkinInfo::matrix_list_t replacement_inv_bind; + LLMeshSkinInfo::matrix_list_t replacement_alt_bind; + std::vector 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 5c6d0a55d22..7fa3a00ee27 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -205,6 +205,7 @@ class LLModel : public LLVolume 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 c99d7c2993c..60c6832058e 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/newview/gltf/llgltfloader.cpp @@ -102,7 +102,8 @@ LLGLTFLoader::LLGLTFLoader(std::string filename, maxJointsPerMesh ), //mPreprocessGLTF(preprocess), mMeshesLoaded(false), - mMaterialsLoaded(false) + mMaterialsLoaded(false), + mGeneratedModelLimit(modelLimit) { } @@ -135,6 +136,97 @@ bool LLGLTFLoader::OpenFile(const std::string &filename) return (mMeshesLoaded); } +void LLGLTFLoader::addModelToScene( + LLModel* pModel, + U32 submodel_limit, + const LLMatrix4& transformation, + const LLVolumeParams& volume_params) +{ + U32 volume_faces = pModel->getNumVolumeFaces(); + + // Side-steps all manner of issues when splitting models + // and matching lower LOD materials to base models + // + pModel->sortVolumeFacesByMaterialName(); + + int submodelID = 0; + + // remove all faces that definitely won't fit into one model and submodel limit + U32 face_limit = (submodel_limit + 1) * LL_SCULPT_MESH_MAX_FACES; + if (face_limit < volume_faces) + { + pModel->setNumVolumeFaces(face_limit); + } + + LLVolume::face_list_t remainder; + std::vector ready_models; + LLModel* current_model = pModel; + do + { + current_model->trimVolumeFacesToSize(LL_SCULPT_MESH_MAX_FACES, &remainder); + + volume_faces = static_cast(remainder.size()); + + // Don't add to scene yet because weights and materials aren't ready. + // Just save it + ready_models.push_back(current_model); + + // If we have left-over volume faces, create another model + // to absorb them. + if (volume_faces) + { + LLModel* next = new LLModel(volume_params, 0.f); + next->ClearFacesAndMaterials(); + next->mSubmodelID = ++submodelID; + next->mLabel = pModel->mLabel + (char)((int)'a' + next->mSubmodelID) + lod_suffix[mLod]; + next->getVolumeFaces() = remainder; + next->mNormalizedScale = current_model->mNormalizedScale; + next->mNormalizedTranslation = current_model->mNormalizedTranslation; + next->mSkinWeights = current_model->mSkinWeights; + next->mPosition = current_model->mPosition; + + const LLMeshSkinInfo& current_skin_info = current_model->mSkinInfo; + LLMeshSkinInfo& next_skin_info = next->mSkinInfo; + next_skin_info.mJointNames = current_skin_info.mJointNames; + next_skin_info.mJointNums = current_skin_info.mJointNums; + next_skin_info.mBindShapeMatrix = current_skin_info.mBindShapeMatrix; + next_skin_info.mInvBindMatrix = current_skin_info.mInvBindMatrix; + next_skin_info.mAlternateBindMatrix = current_skin_info.mAlternateBindMatrix; + next_skin_info.mPelvisOffset = current_skin_info.mPelvisOffset; + + + if (current_model->mMaterialList.size() > LL_SCULPT_MESH_MAX_FACES) + { + next->mMaterialList.assign(current_model->mMaterialList.begin() + LL_SCULPT_MESH_MAX_FACES, current_model->mMaterialList.end()); + current_model->mMaterialList.resize(LL_SCULPT_MESH_MAX_FACES); + } + + current_model = next; + } + + remainder.clear(); + + } while (volume_faces); + + for (auto model : ready_models) + { + // remove unused/redundant vertices + current_model->remapVolumeFaces(); + // remove unused/redundant weights and joints + current_model->remapSkinWeightsAndJoints(); + + mModelList.push_back(model); + + std::map materials; + for (U32 i = 0; i < (U32)model->mMaterialList.size(); ++i) + { + materials[model->mMaterialList[i]] = LLImportMaterial(); + } + mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials)); + stretch_extents(model, transformation); + } +} + bool LLGLTFLoader::parseMeshes() { if (!mGltfLoaded) return false; @@ -160,6 +252,7 @@ bool LLGLTFLoader::parseMeshes() // Track how many times each mesh name has been used std::map mesh_name_counts; + U32 submodel_limit = mGLTFAsset.mNodes.size() > 0 ? mGeneratedModelLimit / (U32)mGLTFAsset.mNodes.size() : 0; // Process each node for (auto& node : mGLTFAsset.mNodes) @@ -188,8 +281,6 @@ bool LLGLTFLoader::parseMeshes() (LLModel::NO_ERRORS == pModel->getStatus()) && validate_model(pModel)) { - mModelList.push_back(pModel); - mTransform.setIdentity(); transformation = mTransform; @@ -234,8 +325,7 @@ bool LLGLTFLoader::parseMeshes() mWarningsArray.append(args); } - mScene[transformation].push_back(LLModelInstance(pModel, pModel->mLabel, transformation, mats)); - stretch_extents(pModel, transformation); + addModelToScene(pModel, submodel_limit, transformation, volume_params); } else { @@ -347,8 +437,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& } } - auto prims = mesh.mPrimitives; - for (auto prim : prims) + for (const LL::GLTF::Primitive& prim : mesh.mPrimitives) { // Unfortunately, SLM does not support 32 bit indices. Filter out anything that goes beyond 16 bit. if (prim.getVertexCount() < USHRT_MAX) @@ -471,7 +560,7 @@ bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const LL::GLTF::Mesh& { vert.weights = glm::vec4(prim.mWeights[i]); - auto accessorIdx = prim.mAttributes["JOINTS_0"]; + auto accessorIdx = prim.mAttributes.at("JOINTS_0"); LL::GLTF::Accessor::ComponentType componentType = LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE; if (accessorIdx >= 0) { diff --git a/indra/newview/gltf/llgltfloader.h b/indra/newview/gltf/llgltfloader.h index a9572a5bfc5..a3ee8d91df4 100644 --- a/indra/newview/gltf/llgltfloader.h +++ b/indra/newview/gltf/llgltfloader.h @@ -155,6 +155,7 @@ class LLGLTFLoader : public LLModelLoader bool mGltfLoaded; bool mMeshesLoaded; bool mMaterialsLoaded; + U32 mGeneratedModelLimit; std::vector mMeshes; std::vector mMaterials; @@ -176,6 +177,11 @@ class LLGLTFLoader : public LLModelLoader 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(S32 skin_idx); + void addModelToScene( + LLModel* pModel, + U32 submodel_limit, + const LLMatrix4& transformation, + const LLVolumeParams& volume_params); 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