From 2d2ed85c56c0aa47e6909becaf60b71f1daaf46f Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 28 Jul 2021 21:30:49 +0300 Subject: DRTVWR-542 Fix malfunctioning warning --- indra/llprimitive/lldaeloader.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'indra/llprimitive') diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index dfa29fb539..d3acd44f06 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -787,7 +787,12 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector& fac for (U32 i = 0; i < verts.size(); ++i) { indices[i] = vert_idx[verts[i]]; - llassert(!i || (indices[i-1] != indices[i])); + if (i % 3 != 0) // assumes GL_TRIANGLES, compare 0-1, 1-2, 3-4, 4-5 but not 2-3 or 5-6 + { + // A faulty degenerate triangle detection (triangle with 0 area), + // probably should be a warning and not an assert + llassert(!i || (indices[i-1] != indices[i])); + } } // DEBUG just build an expanded triangle list -- cgit v1.3 From 7235d333ea24388fc13a6d01dbafc707b658a0d4 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 4 Aug 2021 00:15:49 +0300 Subject: DRTVWR-542 Fix incorect dropbox state --- indra/llprimitive/lldaeloader.cpp | 1 + indra/llprimitive/llmodel.h | 2 ++ indra/newview/llfloatermodelpreview.cpp | 2 +- indra/newview/llfloatermodelpreview.h | 4 +--- indra/newview/llmodelpreview.cpp | 8 +++++--- 5 files changed, 10 insertions(+), 7 deletions(-) (limited to 'indra/llprimitive') diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index d3acd44f06..17e9e05edd 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -289,6 +289,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector& fa material = std::string(tri->getMaterial()); } + // Todo: mark model in some way as having generated(split) faces materials.push_back(material); face_list.push_back(face); face_list.rbegin()->fillFromLegacyData(verts, indices); diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index 51fa2f8079..96368d64e5 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -281,6 +281,8 @@ public: EModelStatus mStatus ; + // A model/object can only have 8 faces, spillover faces will + // be moved to new model/object and assigned a submodel id. int mSubmodelID; }; diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 3f2fcb148c..e5d451e909 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -136,7 +136,7 @@ mAvatarTabIndex(0) mStatusLock = new LLMutex(); mModelPreview = NULL; - mLODMode[LLModel::LOD_HIGH] = 0; + mLODMode[LLModel::LOD_HIGH] = LLModelPreview::LOD_FROM_FILE; for (U32 i = 0; i < LLModel::LOD_HIGH; i++) { mLODMode[i] = LLModelPreview::MESH_OPTIMIZER; diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h index 8a01b0c307..51f9b3a0e2 100644 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -196,9 +196,7 @@ protected: std::map mViewOptionDisabled; //store which lod mode each LOD is using - // 0 - load from file - // 1 - auto generate - // 2 - use LoD above + // See eLoDMode S32 mLODMode[4]; LLMutex* mStatusLock; diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 96eb9340f5..11a536473c 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1842,6 +1842,8 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d S32 model_meshopt_mode = meshopt_mode; + // Ideally this should run not per model, + // but combine all submodels with origin model as well if (model_meshopt_mode == MESH_OPTIMIZER_COMBINE) { // Figure out buffer size @@ -2758,7 +2760,7 @@ void LLModelPreview::updateLodControls(S32 lod) S32 lod_mode = lod_combo->getCurrentIndex(); if (lod_mode == LOD_FROM_FILE) // LoD from file { - fmp->mLODMode[lod] = 0; + fmp->mLODMode[lod] = LOD_FROM_FILE; for (U32 i = 0; i < num_file_controls; ++i) { mFMP->childSetVisible(file_controls[i] + lod_name[lod], true); @@ -2771,7 +2773,7 @@ void LLModelPreview::updateLodControls(S32 lod) } else if (lod_mode == USE_LOD_ABOVE) // use LoD above { - fmp->mLODMode[lod] = 2; + fmp->mLODMode[lod] = USE_LOD_ABOVE; for (U32 i = 0; i < num_file_controls; ++i) { mFMP->childSetVisible(file_controls[i] + lod_name[lod], false); @@ -2797,7 +2799,7 @@ void LLModelPreview::updateLodControls(S32 lod) } else // auto generate, the default case for all LoDs except High { - fmp->mLODMode[lod] = 1; + fmp->mLODMode[lod] = MESH_OPTIMIZER; //don't actually regenerate lod when refreshing UI mLODFrozen = true; -- cgit v1.3 From 1a1793244002effe46cedf63180de60f4bc69a9a Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 4 Aug 2021 21:14:00 +0300 Subject: DRTVWR-542 Automated method selection Normally simplification methods apply for whole upload, but this one selects methods per model or per face. --- indra/llmeshoptimizer/llmeshoptimizer.cpp | 97 ++-- indra/llmeshoptimizer/llmeshoptimizer.h | 21 +- indra/llprimitive/lldaeloader.cpp | 32 +- indra/llprimitive/llmodel.cpp | 3 +- indra/llprimitive/llmodel.h | 5 + indra/newview/llfloatermodelpreview.cpp | 6 +- indra/newview/llmodelpreview.cpp | 631 +++++++++++---------- indra/newview/llmodelpreview.h | 7 +- .../skins/default/xui/en/floater_model_preview.xml | 48 +- 9 files changed, 481 insertions(+), 369 deletions(-) (limited to 'indra/llprimitive') diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp index 8097a05511..a879389c5a 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.cpp +++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp @@ -67,19 +67,36 @@ U64 LLMeshOptimizer::simplifyU32(U32 *destination, U64 vertex_positions_stride, U64 target_index_count, F32 target_error, + bool sloppy, F32* result_error ) { - return meshopt_simplify(destination, - indices, - index_count, - (const float*)vertex_positions, - vertex_count, - vertex_positions_stride, - target_index_count, - target_error, - result_error - ); + if (sloppy) + { + return meshopt_simplifySloppy(destination, + indices, + index_count, + (const float*)vertex_positions, + vertex_count, + vertex_positions_stride, + target_index_count, + target_error, + result_error + ); + } + else + { + return meshopt_simplify(destination, + indices, + index_count, + (const float*)vertex_positions, + vertex_count, + vertex_positions_stride, + target_index_count, + target_error, + result_error + ); + } } //static @@ -91,41 +108,35 @@ U64 LLMeshOptimizer::simplify(U16 *destination, U64 vertex_positions_stride, U64 target_index_count, F32 target_error, + bool sloppy, F32* result_error ) { - return meshopt_simplify(destination, - indices, - index_count, - (const float*)vertex_positions, - vertex_count, - vertex_positions_stride, - target_index_count, - target_error, - result_error - ); + if (sloppy) + { + return meshopt_simplifySloppy(destination, + indices, + index_count, + (const float*)vertex_positions, + vertex_count, + vertex_positions_stride, + target_index_count, + target_error, + result_error + ); + } + else + { + return meshopt_simplify(destination, + indices, + index_count, + (const float*)vertex_positions, + vertex_count, + vertex_positions_stride, + target_index_count, + target_error, + result_error + ); + } } -//static -U64 LLMeshOptimizer::simplifySloppy(U16 *destination, - const U16 *indices, - U64 index_count, - const LLVector4a *vertex_positions, - U64 vertex_count, - U64 vertex_positions_stride, - U64 target_index_count, - F32 target_error, - F32* result_error - ) -{ - return meshopt_simplifySloppy(destination, - indices, - index_count, - (const float*)vertex_positions, - vertex_count, - vertex_positions_stride, - target_index_count, - target_error, - result_error - ); -} diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h index e881ec26c5..e8dd16dae9 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.h +++ b/indra/llmeshoptimizer/llmeshoptimizer.h @@ -45,6 +45,8 @@ public: U64 vertex_positions_stride); // returns amount of indices in destiantion + // sloppy engages a variant of a mechanizm that does not respect topology as much + // but is much more efective for simpler models // result_error returns how far from original the model is in % if not NULL // Works with U32 indices (LLFace uses U16 indices) static U64 simplifyU32( @@ -56,10 +58,13 @@ public: U64 vertex_positions_stride, U64 target_index_count, F32 target_error, + bool sloppy, F32* result_error); // Returns amount of indices in destiantion - // Result_error returns how far from original the model is in % if not NULL + // sloppy engages a variant of a mechanizm that does not respect topology as much + // but is much better for simpler models + // result_error returns how far from original the model is in % if not NULL // Meant for U16 indices (LLFace uses U16 indices) static U64 simplify( U16 *destination, @@ -70,19 +75,7 @@ public: U64 vertex_positions_stride, U64 target_index_count, F32 target_error, - F32* result_error); - - // returns amount of indices in destiantion - // result_error returns how far from original the model is in % if not NULL - static U64 simplifySloppy( - U16 *destination, - const U16 *indices, - U64 index_count, - const LLVector4a *vertex_positions, - U64 vertex_count, - U64 vertex_positions_stride, - U64 target_index_count, - F32 target_error, + bool sloppy, F32* result_error); private: }; diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index 17e9e05edd..dcf3b5fa0e 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -149,7 +149,11 @@ bool get_dom_sources(const domInputLocalOffset_Array& inputs, S32& pos_offset, S return true; } -LLModel::EModelStatus load_face_from_dom_triangles(std::vector& face_list, std::vector& materials, domTrianglesRef& tri) +LLModel::EModelStatus load_face_from_dom_triangles( + std::vector& face_list, + std::vector& materials, + domTrianglesRef& tri, + bool &generated_additional_faces) { LLVolumeFace face; std::vector verts; @@ -282,6 +286,8 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector& fa if (indices.size()%3 == 0 && verts.size() >= 65532) { + generated_additional_faces = true; + std::string material; if (tri->getMaterial()) @@ -289,7 +295,6 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector& fa material = std::string(tri->getMaterial()); } - // Todo: mark model in some way as having generated(split) faces materials.push_back(material); face_list.push_back(face); face_list.rbegin()->fillFromLegacyData(verts, indices); @@ -344,7 +349,12 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector& fa return LLModel::NO_ERRORS ; } -LLModel::EModelStatus load_face_from_dom_polylist(std::vector& face_list, std::vector& materials, domPolylistRef& poly, LLSD& log_msg) +LLModel::EModelStatus load_face_from_dom_polylist( + std::vector& face_list, + std::vector& materials, + domPolylistRef& poly, + bool& generated_additional_faces, + LLSD& log_msg) { domPRef p = poly->getP(); domListOfUInts& idx = p->getValue(); @@ -546,6 +556,8 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector& fac if (indices.size()%3 == 0 && indices.size() >= 65532) { + generated_additional_faces = true; + std::string material; if (poly->getMaterial()) @@ -771,6 +783,9 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector& fac } } + // Viewer can only fit U16 vertices, shouldn't we do some checks here and return overflow if result has more? + llassert(vert_idx.size() < U16_MAX); + //build vertex array from map std::vector new_verts; new_verts.resize(vert_idx.size()); @@ -2390,11 +2405,13 @@ bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh, LLSD& LLModel::EModelStatus status = LLModel::NO_ERRORS; domTriangles_Array& tris = mesh->getTriangles_array(); + pModel->mHasGeneratedFaces = false; + for (U32 i = 0; i < tris.getCount(); ++i) { domTrianglesRef& tri = tris.get(i); - status = load_face_from_dom_triangles(pModel->getVolumeFaces(), pModel->getMaterialList(), tri); + status = load_face_from_dom_triangles(pModel->getVolumeFaces(), pModel->getMaterialList(), tri, pModel->mHasGeneratedFaces); pModel->mStatus = status; if(status != LLModel::NO_ERRORS) { @@ -2407,7 +2424,7 @@ bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh, LLSD& for (U32 i = 0; i < polys.getCount(); ++i) { domPolylistRef& poly = polys.get(i); - status = load_face_from_dom_polylist(pModel->getVolumeFaces(), pModel->getMaterialList(), poly, log_msg); + status = load_face_from_dom_polylist(pModel->getVolumeFaces(), pModel->getMaterialList(), poly, pModel->mHasGeneratedFaces, log_msg); if(status != LLModel::NO_ERRORS) { @@ -2421,6 +2438,10 @@ bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh, LLSD& for (U32 i = 0; i < polygons.getCount(); ++i) { domPolygonsRef& poly = polygons.get(i); + + // Due to how poligons work, assume that face was 'generated' by default + pModel->mHasGeneratedFaces = true; + status = load_face_from_dom_polygons(pModel->getVolumeFaces(), pModel->getMaterialList(), poly); if(status != LLModel::NO_ERRORS) @@ -2520,6 +2541,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector& mo { LLModel* next = new LLModel(volume_params, 0.f); next->mSubmodelID = ++submodelID; + next->mHasGeneratedFaces = ret->mHasGeneratedFaces; next->mLabel = model_name + (char)((int)'a' + next->mSubmodelID) + lod_suffix[mLod]; next->getVolumeFaces() = remainder; next->mNormalizedScale = ret->mNormalizedScale; diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 702a1b5238..8b8fde0ea0 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -55,7 +55,8 @@ LLModel::LLModel(LLVolumeParams& params, F32 detail) mNormalizedTranslation(0,0,0), mPelvisOffset( 0.0f ), mStatus(NO_ERRORS), - mSubmodelID(0) + mSubmodelID(0), + mHasGeneratedFaces(false) { mDecompID = -1; mLocalID = -1; diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index 96368d64e5..87a47dcb36 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -284,6 +284,11 @@ public: // A model/object can only have 8 faces, spillover faces will // be moved to new model/object and assigned a submodel id. int mSubmodelID; + // A .dae face can have more than 65K vertices, but viewer + // is limited to U16 for indices, in such case spilower will + // be moved into new face and this will be set to true. + // Also true in case faces were generated from polygons + bool mHasGeneratedFaces; }; typedef std::vector > model_list; diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index e5d451e909..6795ea8f53 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -139,7 +139,7 @@ mAvatarTabIndex(0) mLODMode[LLModel::LOD_HIGH] = LLModelPreview::LOD_FROM_FILE; for (U32 i = 0; i < LLModel::LOD_HIGH; i++) { - mLODMode[i] = LLModelPreview::MESH_OPTIMIZER; + mLODMode[i] = LLModelPreview::MESH_OPTIMIZER_AUTO; } } @@ -729,6 +729,7 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) case LLModelPreview::GENERATE: mModelPreview->onLODGenerateParamCommit(lod, enforce_tri_limit); break; + case LLModelPreview::MESH_OPTIMIZER_AUTO: case LLModelPreview::MESH_OPTIMIZER: case LLModelPreview::MESH_OPTIMIZER_SLOPPY: case LLModelPreview::MESH_OPTIMIZER_COMBINE: @@ -1738,6 +1739,7 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod) LLComboBox* lod_source_combo = getChild("lod_source_" + lod_name[lod]); S32 index = lod_source_combo->getCurrentIndex(); if (index == LLModelPreview::GENERATE + || index == LLModelPreview::MESH_OPTIMIZER_AUTO || index == LLModelPreview::MESH_OPTIMIZER || index == LLModelPreview::MESH_OPTIMIZER_SLOPPY || index == LLModelPreview::MESH_OPTIMIZER_COMBINE) @@ -1771,7 +1773,7 @@ void LLFloaterModelPreview::resetUploadOptions() getChild("lod_source_" + lod_name[NUM_LOD - 1])->setCurrentByIndex(LLModelPreview::LOD_FROM_FILE); for (S32 lod = 0; lod < NUM_LOD - 1; ++lod) { - getChild("lod_source_" + lod_name[lod])->setCurrentByIndex(LLModelPreview::MESH_OPTIMIZER); + getChild("lod_source_" + lod_name[lod])->setCurrentByIndex(LLModelPreview::MESH_OPTIMIZER_AUTO); childSetValue("lod_file_" + lod_name[lod], ""); } diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 11a536473c..3a8676d7b9 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1706,6 +1706,307 @@ void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri } } +// Runs per object, but likely it is a better way to run per model+submodels +// returns a ratio of base model indices to resulting indices +F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_decimator, F32 error_threshold, bool sloppy) +{ + // Figure out buffer size + S32 size_indices = 0; + S32 size_vertices = 0; + + for (U32 face_idx = 0; face_idx < base_model->getNumVolumeFaces(); ++face_idx) + { + const LLVolumeFace &face = base_model->getVolumeFace(face_idx); + size_indices += face.mNumIndices; + size_vertices += face.mNumVertices; + } + + // Allocate buffers, note that we are using U32 buffer instead of U16 + U32* combined_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32)); + U32* output_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32)); + + // extra space for normals and text coords + S32 tc_bytes_size = ((size_vertices * sizeof(LLVector2)) + 0xF) & ~0xF; + LLVector4a* combined_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_vertices + tc_bytes_size); + LLVector4a* combined_normals = combined_positions + size_vertices; + LLVector2* combined_tex_coords = (LLVector2*)(combined_normals + size_vertices); + + // copy indices and vertices into new buffers + S32 combined_positions_shift = 0; + S32 indices_idx_shift = 0; + S32 combined_indices_shift = 0; + for (U32 face_idx = 0; face_idx < base_model->getNumVolumeFaces(); ++face_idx) + { + const LLVolumeFace &face = base_model->getVolumeFace(face_idx); + + // vertices + S32 copy_bytes = face.mNumVertices * sizeof(LLVector4a); + LLVector4a::memcpyNonAliased16((F32*)(combined_positions + combined_positions_shift), (F32*)face.mPositions, copy_bytes); + + // normals + LLVector4a::memcpyNonAliased16((F32*)(combined_normals + combined_positions_shift), (F32*)face.mNormals, copy_bytes); + + // tex coords + copy_bytes = (face.mNumVertices * sizeof(LLVector2) + 0xF) & ~0xF; + LLVector4a::memcpyNonAliased16((F32*)(combined_tex_coords + combined_positions_shift), (F32*)face.mTexCoords, copy_bytes); + + combined_positions_shift += face.mNumVertices; + + // indices, sadly can't do dumb memcpy for indices, need to adjust each value + for (S32 i = 0; i < face.mNumIndices; ++i) + { + U16 idx = face.mIndices[i]; + + combined_indices[combined_indices_shift] = idx + indices_idx_shift; + combined_indices_shift++; + } + indices_idx_shift += face.mNumVertices; + } + + // Now that we have buffers, optimize + S32 target_indices = 0; + F32 result_code = 0; // how far from original the model is, 1 == 100% + S32 new_indices = 0; + + target_indices = llmax(3, llfloor(size_indices / indices_decimator)); // leave at least one triangle + new_indices = LLMeshOptimizer::simplifyU32( + output_indices, + combined_indices, + size_indices, + combined_positions, + size_vertices, + LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], + target_indices, + error_threshold, + sloppy, + &result_code); + + + if (result_code < 0) + { + LL_WARNS() << "Negative result code from meshoptimizer for model " << target_model->mLabel + << " target Indices: " << target_indices + << " new Indices: " << new_indices + << " original count: " << size_indices << LL_ENDL; + } + + // repack back into individual faces + + LLVector4a* buffer_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_vertices + tc_bytes_size); + LLVector4a* buffer_normals = buffer_positions + size_vertices; + LLVector2* buffer_tex_coords = (LLVector2*)(buffer_normals + size_vertices); + U16* buffer_indices = (U16*)ll_aligned_malloc_16(U16_MAX * sizeof(U16)); + S32* old_to_new_positions_map = new S32[size_vertices]; + + S32 buf_positions_copied = 0; + S32 buf_indices_copied = 0; + indices_idx_shift = 0; + + // Crude method to copy indices back into face + for (U32 face_idx = 0; face_idx < base_model->getNumVolumeFaces(); ++face_idx) + { + const LLVolumeFace &face = base_model->getVolumeFace(face_idx); + + // reset data for new run + buf_positions_copied = 0; + buf_indices_copied = 0; + bool copy_triangle = false; + S32 range = indices_idx_shift + face.mNumVertices; + + for (S32 i = 0; i < size_vertices; i++) + { + old_to_new_positions_map[i] = -1; + } + + // Copy relevant indices and vertices + for (S32 i = 0; i < new_indices; ++i) + { + U32 idx = output_indices[i]; + + if ((i % 3) == 0) + { + copy_triangle = idx >= indices_idx_shift && idx < range; + } + + if (copy_triangle) + { + if (old_to_new_positions_map[idx] == -1) + { + // New position, need to copy it + // Validate size + if (buf_positions_copied >= U16_MAX) + { + // Normally this shouldn't happen since the whole point is to reduce amount of vertices + // but it might happen if user tries to run optimization with too large triangle or error value + // so fallback to 'per face' mode or verify requested limits and copy base model as is. + LL_WARNS() << "Over triangle limit. Failed to optimize in 'per object' mode, falling back to per face variant for" + << " model " << target_model->mLabel + << " target Indices: " << target_indices + << " new Indices: " << new_indices + << " original count: " << size_indices + << " error treshold: " << error_threshold + << LL_ENDL; + return -1; + } + + // Copy vertice, normals, tcs + buffer_positions[buf_positions_copied] = combined_positions[idx]; + buffer_normals[buf_positions_copied] = combined_normals[idx]; + buffer_tex_coords[buf_positions_copied] = combined_tex_coords[idx]; + + old_to_new_positions_map[idx] = buf_positions_copied; + + buffer_indices[buf_indices_copied] = (U16)buf_positions_copied; + buf_positions_copied++; + } + else + { + // existing position + buffer_indices[buf_indices_copied] = (U16)old_to_new_positions_map[idx]; + } + buf_indices_copied++; + } + } + + if (buf_positions_copied >= U16_MAX) + { + break; + } + + LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); + //new_face = face; //temp + + if (buf_indices_copied < 3) + { + // face was optimized away + new_face.resizeIndices(3); + new_face.resizeVertices(1); + memset(new_face.mIndices, 0, sizeof(U16) * 3); + new_face.mPositions[0].clear(); // set first vertice to 0 + new_face.mNormals[0].clear(); + new_face.mTexCoords[0].setZero(); + } + else + { + new_face.resizeIndices(buf_indices_copied); + new_face.resizeVertices(buf_positions_copied); + + S32 idx_size = (buf_indices_copied * sizeof(U16) + 0xF) & ~0xF; + LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)buffer_indices, idx_size); + + LLVector4a::memcpyNonAliased16((F32*)new_face.mPositions, (F32*)buffer_positions, buf_positions_copied * sizeof(LLVector4a)); + LLVector4a::memcpyNonAliased16((F32*)new_face.mNormals, (F32*)buffer_normals, buf_positions_copied * sizeof(LLVector4a)); + + U32 tex_size = (buf_positions_copied * sizeof(LLVector2) + 0xF)&~0xF; + LLVector4a::memcpyNonAliased16((F32*)new_face.mTexCoords, (F32*)buffer_tex_coords, tex_size); + } + + indices_idx_shift += face.mNumVertices; + } + + delete[]old_to_new_positions_map; + ll_aligned_free<64>(combined_positions); + ll_aligned_free<64>(buffer_positions); + ll_aligned_free_32(output_indices); + ll_aligned_free_32(buffer_indices); + ll_aligned_free_32(combined_indices); + + if (new_indices <= 0) + { + return -1; + } + + return (F32)size_indices / (F32)new_indices; +} + +F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_decimator, F32 error_threshold, bool sloppy) +{ + const LLVolumeFace &face = base_model->getVolumeFace(face_idx); + S32 size_indices = face.mNumIndices; + // todo: do not allocate per each face, add one large buffer somewhere + // faces have limited amount of indices + S32 size = (size_indices * sizeof(U16) + 0xF) & ~0xF; + U16* output = (U16*)ll_aligned_malloc_16(size); + + S32 target_indices = 0; + F32 result_code = 0; // how far from original the model is, 1 == 100% + S32 new_indices = 0; + + target_indices = llmax(3, llfloor(size_indices / indices_decimator)); // leave at least one triangle + new_indices = LLMeshOptimizer::simplify( + output, + face.mIndices, + size_indices, + face.mPositions, + face.mNumVertices, + LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], + target_indices, + error_threshold, + sloppy, + &result_code); + + + if (result_code < 0) + { + LL_WARNS() << "Negative result code from meshoptimizer for face " << face_idx + << " of model " << target_model->mLabel + << " target Indices: " << target_indices + << " new Indices: " << new_indices + << " original count: " << size_indices + << " error treshold: " << error_threshold + << LL_ENDL; + } + + LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); + + // Copy old values + new_face = face; + + + if (new_indices == 0) + { + if (!sloppy) + { + // meshopt_optimizeSloppy() can optimize triangles away even if target_indices is > 2, + // but optimize() isn't supposed to + LL_INFOS() << "No indices generated by meshoptimizer for face " << face_idx + << " of model " << target_model->mLabel + << " target Indices: " << target_indices + << " original count: " << size_indices + << " error treshold: " << error_threshold + << LL_ENDL; + } + + // Face got optimized away + // Generate empty triangle + new_face.resizeIndices(3); + new_face.resizeVertices(1); + memset(new_face.mIndices, 0, sizeof(U16) * 3); + new_face.mPositions[0].clear(); // set first vertice to 0 + new_face.mNormals[0].clear(); + new_face.mTexCoords[0].setZero(); + } + else + { + // Assign new values + new_face.resizeIndices(new_indices); // will wipe out mIndices, so new_face can't substitute output + S32 idx_size = (new_indices * sizeof(U16) + 0xF) & ~0xF; + LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)output, idx_size); + + // clear unused values + new_face.optimize(); + } + + ll_aligned_free_16(output); + + if (new_indices <= 0) + { + return -1; + } + + return (F32)size_indices / (F32)new_indices; +} + void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 decimation, bool enforce_tri_limit) { LL_INFOS() << "Generating lod " << which_lod << " using meshoptimizer" << LL_ENDL; @@ -1736,7 +2037,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // TODO: add interface to mFMP to get error treshold or let mFMP write one into LLModelPreview // We should not be accesing views from other class! U32 lod_mode = LIMIT_TRIANGLES; - F32 indices_ratio = 0; + F32 indices_decimator = 0; F32 triangle_limit = 0; F32 lod_error_threshold = 1; //100% @@ -1767,7 +2068,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d } // meshoptimizer doesn't use triangle limit, it uses indices limit, so convert it to aproximate ratio - indices_ratio = triangle_limit / (F32)base_triangle_count; + indices_decimator = (F32)base_triangle_count / triangle_limit; } else { @@ -1776,8 +2077,8 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d } else { - // we are genrating all lods and each lod will get own indices_ratio - indices_ratio = 1; + // we are genrating all lods and each lod will get own indices_decimator + indices_decimator = 1; triangle_limit = base_triangle_count; } @@ -1810,7 +2111,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // we are genrating all lods and each lod gets own indices_ratio if (lod < start) { - indices_ratio /= decimation; + indices_decimator *= decimation; triangle_limit /= decimation; } } @@ -1846,308 +2147,64 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // but combine all submodels with origin model as well if (model_meshopt_mode == MESH_OPTIMIZER_COMBINE) { - // Figure out buffer size - S32 size_indices = 0; - S32 size_vertices = 0; + // Run meshoptimizer for each model/object, up to 8 faces in one model + // Ideally this should run not per model, + // but combine all submodels with origin model as well + genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); + } + + if (model_meshopt_mode == MESH_OPTIMIZER) + { + // Run meshoptimizer for each face for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - const LLVolumeFace &face = base->getVolumeFace(face_idx); - size_indices += face.mNumIndices; - size_vertices += face.mNumVertices; + genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); } + } - // Allocate buffers, note that we are using U32 buffer instead of U16 - U32* combined_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32)); - U32* output_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32)); - - // extra space for normals and text coords - S32 tc_bytes_size = ((size_vertices * sizeof(LLVector2)) + 0xF) & ~0xF; - LLVector4a* combined_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_vertices + tc_bytes_size); - LLVector4a* combined_normals = combined_positions + size_vertices; - LLVector2* combined_tex_coords = (LLVector2*)(combined_normals + size_vertices); - - // copy indices and vertices into new buffers - S32 combined_positions_shift = 0; - S32 indices_idx_shift = 0; - S32 combined_indices_shift = 0; + if (model_meshopt_mode == MESH_OPTIMIZER_SLOPPY) + { + // Run meshoptimizer for each face for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - const LLVolumeFace &face = base->getVolumeFace(face_idx); - - // vertices - S32 copy_bytes = face.mNumVertices * sizeof(LLVector4a); - LLVector4a::memcpyNonAliased16((F32*)(combined_positions + combined_positions_shift), (F32*)face.mPositions, copy_bytes); - - // normals - LLVector4a::memcpyNonAliased16((F32*)(combined_normals + combined_positions_shift), (F32*)face.mNormals, copy_bytes); - - // tex coords - copy_bytes = (face.mNumVertices * sizeof(LLVector2) + 0xF) & ~0xF; - LLVector4a::memcpyNonAliased16((F32*)(combined_tex_coords + combined_positions_shift), (F32*)face.mTexCoords, copy_bytes); - - combined_positions_shift += face.mNumVertices; - - // indices, sadly can't do dumb memcpy for indices, need to adjust each value - for (S32 i = 0; i < face.mNumIndices; ++i) - { - U16 idx = face.mIndices[i]; - - combined_indices[combined_indices_shift] = idx + indices_idx_shift; - combined_indices_shift++; - } - indices_idx_shift += face.mNumVertices; - } - - // Now that we have buffers, optimize - S32 target_indices = 0; - F32 result_code = 0; // how far from original the model is, 1 == 100% - S32 new_indices = 0; - - target_indices = llmax(3, llfloor(size_indices * indices_ratio)); // leave at least one triangle - new_indices = LLMeshOptimizer::simplifyU32( - output_indices, - combined_indices, - size_indices, - combined_positions, - size_vertices, - LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], - target_indices, - lod_error_threshold, - &result_code); - - - if (result_code < 0) - { - LL_WARNS() << "Negative result code from meshoptimizer for model " << target_model->mLabel - << " target Indices: " << target_indices - << " new Indices: " << new_indices - << " original count: " << size_indices << LL_ENDL; + genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, true); } + } - // repack back into individual faces - - LLVector4a* buffer_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_vertices + tc_bytes_size); - LLVector4a* buffer_normals = buffer_positions + size_vertices; - LLVector2* buffer_tex_coords = (LLVector2*)(buffer_normals + size_vertices); - U16* buffer_indices = (U16*)ll_aligned_malloc_16(U16_MAX * sizeof(U16)); - S32* old_to_new_positions_map = new S32[size_vertices]; - - S32 buf_positions_copied = 0; - S32 buf_indices_copied = 0; - indices_idx_shift = 0; - - // Crude method to copy indices back into face - for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) + if (model_meshopt_mode == MESH_OPTIMIZER_AUTO) + { + F32 allowed_ratio_drift = 2.f; + S32 res = 0; + if (base->mHasGeneratedFaces) { - const LLVolumeFace &face = base->getVolumeFace(face_idx); - - // reset data for new run - buf_positions_copied = 0; - buf_indices_copied = 0; - bool copy_triangle = false; - S32 range = indices_idx_shift + face.mNumVertices; - - for (S32 i = 0; i < size_vertices; i++) - { - old_to_new_positions_map[i] = -1; - } - - // Copy relevant indices and vertices - for (S32 i = 0; i < new_indices; ++i) - { - U32 idx = output_indices[i]; - - if ((i % 3) == 0) - { - copy_triangle = idx >= indices_idx_shift && idx < range; - } - - if (copy_triangle) - { - if (old_to_new_positions_map[idx] == -1) - { - // New position, need to copy it - // Validate size - if (buf_positions_copied >= U16_MAX) - { - // Normally this shouldn't happen since the whole point is to reduce amount of vertices - // but it might happen if user tries to run optimization with too large triangle or error value - // so fallback to 'per face' mode or verify requested limits and copy base model as is. - LL_WARNS() << "Over triangle limit. Failed to optimize in 'per object' mode, falling back to per face variant for" - << " model " << target_model->mLabel - << " target Indices: " << target_indices - << " new Indices: " << new_indices - << " original count: " << size_indices - << " error treshold: " << lod_error_threshold - << LL_ENDL; - model_meshopt_mode = MESH_OPTIMIZER; - break; - } - - // Copy vertice, normals, tcs - buffer_positions[buf_positions_copied] = combined_positions[idx]; - buffer_normals[buf_positions_copied] = combined_normals[idx]; - buffer_tex_coords[buf_positions_copied] = combined_tex_coords[idx]; - - old_to_new_positions_map[idx] = buf_positions_copied; - - buffer_indices[buf_indices_copied] = (U16)buf_positions_copied; - buf_positions_copied++; - } - else - { - // existing position - buffer_indices[buf_indices_copied] = (U16)old_to_new_positions_map[idx]; - } - buf_indices_copied++; - } - } - - if (buf_positions_copied >= U16_MAX) - { - break; - } - - LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); - //new_face = face; //temp + res = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); - if (buf_indices_copied < 3) + if (res * allowed_ratio_drift < indices_decimator) { - // face was optimized away - new_face.resizeIndices(3); - new_face.resizeVertices(1); - memset(new_face.mIndices, 0, sizeof(U16) * 3); - new_face.mPositions[0].clear(); // set first vertice to 0 - new_face.mNormals[0].clear(); - new_face.mTexCoords[0].setZero(); + res = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true); + LL_INFOS() << "Model " << target_model->getName() + << " lod " << which_lod + << " sloppily simplified using per model method." << LL_ENDL; } else { - new_face.resizeIndices(buf_indices_copied); - new_face.resizeVertices(buf_positions_copied); - - S32 idx_size = (buf_indices_copied * sizeof(U16) + 0xF) & ~0xF; - LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)buffer_indices, idx_size); - - LLVector4a::memcpyNonAliased16((F32*)new_face.mPositions, (F32*)buffer_positions, buf_positions_copied * sizeof(LLVector4a)); - LLVector4a::memcpyNonAliased16((F32*)new_face.mNormals, (F32*)buffer_normals, buf_positions_copied * sizeof(LLVector4a)); - - U32 tex_size = (buf_positions_copied * sizeof(LLVector2) + 0xF)&~0xF; - LLVector4a::memcpyNonAliased16((F32*)new_face.mTexCoords, (F32*)buffer_tex_coords, tex_size); + LL_INFOS() << "Model " << target_model->getName() + << " lod " << which_lod + << " simplified using per model method." << LL_ENDL; } - - indices_idx_shift += face.mNumVertices; } - - delete []old_to_new_positions_map; - ll_aligned_free<64>(combined_positions); - ll_aligned_free<64>(buffer_positions); - ll_aligned_free_32(output_indices); - ll_aligned_free_32(buffer_indices); - ll_aligned_free_32(combined_indices); - } - - if (model_meshopt_mode == MESH_OPTIMIZER - || model_meshopt_mode == MESH_OPTIMIZER_SLOPPY) - { - // Run meshoptimizer for each face - for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) + else { - const LLVolumeFace &face = base->getVolumeFace(face_idx); - S32 num_indices = face.mNumIndices; - // todo: do not allocate per each face, add one large buffer somewhere - // faces have limited amount of indices - S32 size = (num_indices * sizeof(U16) + 0xF) & ~0xF; - U16* output = (U16*)ll_aligned_malloc_16(size); - - S32 target_indices = 0; - F32 result_code = 0; // how far from original the model is, 1 == 100% - S32 new_indices = 0; - - if (model_meshopt_mode == MESH_OPTIMIZER_SLOPPY) - { - target_indices = llfloor(num_indices * indices_ratio); - new_indices = LLMeshOptimizer::simplifySloppy( - output, - face.mIndices, - num_indices, - face.mPositions, - face.mNumVertices, - LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], - target_indices, - lod_error_threshold, - &result_code); - } - - if (model_meshopt_mode == MESH_OPTIMIZER) - { - target_indices = llmax(3, llfloor(num_indices * indices_ratio)); // leave at least one triangle - new_indices = LLMeshOptimizer::simplify( - output, - face.mIndices, - num_indices, - face.mPositions, - face.mNumVertices, - LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], - target_indices, - lod_error_threshold, - &result_code); - } - - - if (result_code < 0) + for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - LL_WARNS() << "Negative result code from meshoptimizer for face " << face_idx - << " of model " << target_model->mLabel - << " target Indices: " << target_indices - << " new Indices: " << new_indices - << " original count: " << num_indices - << " error treshold: " << lod_error_threshold - << LL_ENDL; - } - - LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); - - // Copy old values - new_face = face; - - - if (new_indices == 0) - { - if (meshopt_mode != MESH_OPTIMIZER_SLOPPY) + res = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); + + if (res * allowed_ratio_drift < indices_decimator) { - // optimizeSloppy() can optimize triangles away even if target_indices is > 2, - // but optimize() isn't supposed to - LL_INFOS() << "No indices generated by meshoptimizer for face " << face_idx - << " of model " << target_model->mLabel - << " target Indices: " << target_indices - << " original count: " << num_indices - << " error treshold: " << lod_error_threshold - << LL_ENDL; + res = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, true); } - - // Face got optimized away - // Generate empty triangle - new_face.resizeIndices(3); - new_face.resizeVertices(1); - memset(new_face.mIndices, 0, sizeof(U16) * 3); - new_face.mPositions[0].clear(); // set first vertice to 0 - new_face.mNormals[0].clear(); - new_face.mTexCoords[0].setZero(); } - else - { - // Assign new values - new_face.resizeIndices(new_indices); // will wipe out mIndices, so new_face can't substitute output - S32 idx_size = (new_indices * sizeof(U16) + 0xF) & ~0xF; - LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)output, idx_size); - - // clear unused values - new_face.optimize(); - } - - ll_aligned_free_16(output); } } @@ -2799,7 +2856,7 @@ void LLModelPreview::updateLodControls(S32 lod) } else // auto generate, the default case for all LoDs except High { - fmp->mLODMode[lod] = MESH_OPTIMIZER; + fmp->mLODMode[lod] = MESH_OPTIMIZER_AUTO; //don't actually regenerate lod when refreshing UI mLODFrozen = true; @@ -4040,7 +4097,7 @@ bool LLModelPreview::lodQueryCallback() { S32 lod = preview->mLodsQuery.back(); preview->mLodsQuery.pop_back(); - preview->genMeshOptimizerLODs(lod, MESH_OPTIMIZER); + preview->genMeshOptimizerLODs(lod, MESH_OPTIMIZER_AUTO); if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH)) { diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h index b784345b5a..b3296fecf6 100644 --- a/indra/newview/llmodelpreview.h +++ b/indra/newview/llmodelpreview.h @@ -125,9 +125,10 @@ public: { LOD_FROM_FILE = 0, GENERATE, + MESH_OPTIMIZER_AUTO, // automatically selects method based on model or face + MESH_OPTIMIZER_COMBINE, MESH_OPTIMIZER, MESH_OPTIMIZER_SLOPPY, - MESH_OPTIMIZER_COMBINE, USE_LOD_ABOVE, } eLoDMode; @@ -229,6 +230,10 @@ private: // Count amount of original models, excluding sub-models static U32 countRootModels(LLModelLoader::model_list models); + // functions for meshoptimizer, return reached simplification ratio + F32 genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_ratio, F32 error_threshold, bool sloppy); + F32 genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_ratio, F32 error_threshold, bool sloppy); + protected: friend class LLModelLoader; friend class LLFloaterModelPreview; 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 cd67adf42a..db9d296fa5 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -176,6 +176,14 @@ name="Generate" label="Generate" value="Generate" /> + + - + + - + + - + + - Date: Thu, 9 Sep 2021 20:59:21 +0300 Subject: SL-15965 Support wider range of parsing errors --- indra/llprimitive/lldaeloader.cpp | 56 ++++++++++++++++++++-- .../skins/default/xui/en/floater_model_preview.xml | 14 ++++++ 2 files changed, 66 insertions(+), 4 deletions(-) (limited to 'indra/llprimitive') diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index dcf3b5fa0e..73a0b3d673 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -153,7 +153,8 @@ LLModel::EModelStatus load_face_from_dom_triangles( std::vector& face_list, std::vector& materials, domTrianglesRef& tri, - bool &generated_additional_faces) + bool &generated_additional_faces, + LLSD& log_msg) { LLVolumeFace face; std::vector verts; @@ -173,12 +174,18 @@ LLModel::EModelStatus load_face_from_dom_triangles( if ( !get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source)) { + LLSD args; + args["Message"] = "ParsingErrorBadElement"; + log_msg.append(args); return LLModel::BAD_ELEMENT; } if (!pos_source || !pos_source->getFloat_array()) { LL_WARNS() << "Unable to process mesh without position data; invalid model; invalid model." << LL_ENDL; + LLSD args; + args["Message"] = "ParsingErrorPositionInvalidModel"; + log_msg.append(args); return LLModel::BAD_ELEMENT; } @@ -381,6 +388,10 @@ LLModel::EModelStatus load_face_from_dom_polylist( if (!get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source)) { + LL_WARNS() << "Bad element." << LL_ENDL; + LLSD args; + args["Message"] = "ParsingErrorBadElement"; + log_msg.append(args); return LLModel::BAD_ELEMENT; } @@ -432,6 +443,9 @@ LLModel::EModelStatus load_face_from_dom_polylist( if (!cv.getPosition().isFinite3()) { LL_WARNS() << "Found NaN while loading position data from DAE-Model, invalid model." << LL_ENDL; + LLSD args; + args["Message"] = "PositionNaN"; + log_msg.append(args); return LLModel::BAD_ELEMENT; } } @@ -464,6 +478,10 @@ LLModel::EModelStatus load_face_from_dom_polylist( if (!cv.getNormal().isFinite3()) { LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL; + LLSD args; + args["Message"] = "NormalsNaN"; + log_msg.append(args); + return LLModel::BAD_ELEMENT; } } @@ -928,6 +946,9 @@ bool LLDAELoader::OpenFile(const std::string& filename) if (!dom) { LL_INFOS() <<" Error with dae - traditionally indicates a corrupt file."<getVolumeFaces(), pModel->getMaterialList(), tri, pModel->mHasGeneratedFaces); + status = load_face_from_dom_triangles(pModel->getVolumeFaces(), pModel->getMaterialList(), tri, pModel->mHasGeneratedFaces, log_msg); pModel->mStatus = status; if(status != LLModel::NO_ERRORS) { 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 db9d296fa5..05990e28d8 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -45,7 +45,21 @@ Rigged to unrecognized joint name [NAME] Skinning disabled due to [COUNT] unknown joints Model [MODEL_NAME] loaded + Texture coordinates data is not complete. + Found NaN while loading position data from DAE-Model, invalid model. + Found NaN while loading normals from DAE-Model, invalid model. + Negative scale detected, unsupported transform. domInstance_geometry: [LABEL] + Negative scale detected, unsupported post-normalization transform. domInstance_geometry: [LABEL] + Unable to resolve geometry URL. + Bad element + Scene could not be parsed + Error with dae - traditionally indicates a corrupt file. + Could not verify controller + Can't find internal doc + Document has no root + Document has no visual_scene + Unable to process mesh without position data. Invalid model. Date: Mon, 15 Nov 2021 23:23:09 +0200 Subject: SL-15756 Removed mHasGeneratedFaces mHasGeneratedFaces is always true for some types of models and glod was treating faces as one mesh by default, so meshoptimizer should do the same regardles of mHasGeneratedFaces --- indra/llprimitive/lldaeloader.cpp | 16 ++------- indra/llprimitive/llmodel.cpp | 3 +- indra/llprimitive/llmodel.h | 5 --- indra/newview/llmodelpreview.cpp | 73 ++++++++++++--------------------------- 4 files changed, 26 insertions(+), 71 deletions(-) (limited to 'indra/llprimitive') diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index 73a0b3d673..93d492b42d 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -153,7 +153,6 @@ LLModel::EModelStatus load_face_from_dom_triangles( std::vector& face_list, std::vector& materials, domTrianglesRef& tri, - bool &generated_additional_faces, LLSD& log_msg) { LLVolumeFace face; @@ -293,8 +292,6 @@ LLModel::EModelStatus load_face_from_dom_triangles( if (indices.size()%3 == 0 && verts.size() >= 65532) { - generated_additional_faces = true; - std::string material; if (tri->getMaterial()) @@ -360,7 +357,6 @@ LLModel::EModelStatus load_face_from_dom_polylist( std::vector& face_list, std::vector& materials, domPolylistRef& poly, - bool& generated_additional_faces, LLSD& log_msg) { domPRef p = poly->getP(); @@ -574,8 +570,6 @@ LLModel::EModelStatus load_face_from_dom_polylist( if (indices.size()%3 == 0 && indices.size() >= 65532) { - generated_additional_faces = true; - std::string material; if (poly->getMaterial()) @@ -2453,13 +2447,11 @@ bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh, LLSD& LLModel::EModelStatus status = LLModel::NO_ERRORS; domTriangles_Array& tris = mesh->getTriangles_array(); - pModel->mHasGeneratedFaces = false; - for (U32 i = 0; i < tris.getCount(); ++i) { domTrianglesRef& tri = tris.get(i); - status = load_face_from_dom_triangles(pModel->getVolumeFaces(), pModel->getMaterialList(), tri, pModel->mHasGeneratedFaces, log_msg); + status = load_face_from_dom_triangles(pModel->getVolumeFaces(), pModel->getMaterialList(), tri, log_msg); pModel->mStatus = status; if(status != LLModel::NO_ERRORS) { @@ -2472,7 +2464,7 @@ bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh, LLSD& for (U32 i = 0; i < polys.getCount(); ++i) { domPolylistRef& poly = polys.get(i); - status = load_face_from_dom_polylist(pModel->getVolumeFaces(), pModel->getMaterialList(), poly, pModel->mHasGeneratedFaces, log_msg); + status = load_face_from_dom_polylist(pModel->getVolumeFaces(), pModel->getMaterialList(), poly, log_msg); if(status != LLModel::NO_ERRORS) { @@ -2487,9 +2479,6 @@ bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh, LLSD& { domPolygonsRef& poly = polygons.get(i); - // Due to how poligons work, assume that face was 'generated' by default - pModel->mHasGeneratedFaces = true; - status = load_face_from_dom_polygons(pModel->getVolumeFaces(), pModel->getMaterialList(), poly); if(status != LLModel::NO_ERRORS) @@ -2589,7 +2578,6 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector& mo { LLModel* next = new LLModel(volume_params, 0.f); next->mSubmodelID = ++submodelID; - next->mHasGeneratedFaces = ret->mHasGeneratedFaces; next->mLabel = model_name + (char)((int)'a' + next->mSubmodelID) + lod_suffix[mLod]; next->getVolumeFaces() = remainder; next->mNormalizedScale = ret->mNormalizedScale; diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 8b8fde0ea0..702a1b5238 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -55,8 +55,7 @@ LLModel::LLModel(LLVolumeParams& params, F32 detail) mNormalizedTranslation(0,0,0), mPelvisOffset( 0.0f ), mStatus(NO_ERRORS), - mSubmodelID(0), - mHasGeneratedFaces(false) + mSubmodelID(0) { mDecompID = -1; mLocalID = -1; diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index 87a47dcb36..96368d64e5 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -284,11 +284,6 @@ public: // A model/object can only have 8 faces, spillover faces will // be moved to new model/object and assigned a submodel id. int mSubmodelID; - // A .dae face can have more than 65K vertices, but viewer - // is limited to U16 for indices, in such case spilower will - // be moved into new face and this will be set to true. - // Also true in case faces were generated from polygons - bool mHasGeneratedFaces; }; typedef std::vector > model_list; diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index d629358355..f6edbf6bc6 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1775,68 +1775,41 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d if (model_meshopt_mode == MESH_OPTIMIZER_AUTO) { F32 allowed_ratio_drift = 2.f; - F32 res_ratio = 0; - if (base->mHasGeneratedFaces) - { - res_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); + F32 res_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); - if (res_ratio < 0) + if (res_ratio < 0) + { + // U16 vertices overflow, shouldn't happen, but just in case + for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - // U16 vertices overflow, shouldn't happen, but just in case - for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) - { - genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); - } - LL_INFOS() << "Model " << target_model->getName() - << " lod " << which_lod - << " per model method overflow, defaulting to per face." << LL_ENDL; + genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); } - else if (res_ratio * allowed_ratio_drift < indices_decimator) - { - // Try sloppy variant if normal one failed to simplify model enough. - res_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true); - LL_INFOS() << "Model " << target_model->getName() - << " lod " << which_lod - << " sloppily simplified using per model method." << LL_ENDL; + LL_INFOS() << "Model " << target_model->getName() + << " lod " << which_lod + << " per model method overflow, defaulting to per face." << LL_ENDL; + } + else if (res_ratio * allowed_ratio_drift < indices_decimator) + { + // Try sloppy variant if normal one failed to simplify model enough. + res_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true); + LL_INFOS() << "Model " << target_model->getName() + << " lod " << which_lod + << " sloppily simplified using per model method." << LL_ENDL; - if (res_ratio < 0) - { - // Sloppy variant failed to generate triangles. - // Can happen with models that are too simple as is. - // Fallback to normal method. - genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); - } - } - else + if (res_ratio < 0) { - LL_INFOS() << "Model " << target_model->getName() - << " lod " << which_lod - << " simplified using per model method." << LL_ENDL; + // Sloppy variant failed to generate triangles. + // Can happen with models that are too simple as is. + // Fallback to normal method. + genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); } } else { - for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) - { - res_ratio = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); - - if (res_ratio * allowed_ratio_drift < indices_decimator) - { - // normal method failed to sufficiently simplify, try sloppy - res_ratio = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, true); - if (res_ratio < 0) - { - // Sloppy failed to generate triangles. - // Can happen with models that are too simple as is. - // Fallback to normal method. - genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); - } - } - } LL_INFOS() << "Model " << target_model->getName() << " lod " << which_lod - << " simplified using per face methods." << LL_ENDL; + << " simplified using per model method." << LL_ENDL; } } -- cgit v1.3