From 97c954dd84d50ec7b739d2dd3adb7365589f3034 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 6 Jul 2021 21:26:15 +0300 Subject: SL-15522 Initial addition of meshoptimizer --- indra/llmeshoptimizer/CMakeLists.txt | 41 +++++++++++++++++++++++++++++ indra/llmeshoptimizer/llmeshoptimizer.cpp | 43 +++++++++++++++++++++++++++++++ indra/llmeshoptimizer/llmeshoptimizer.h | 37 ++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 indra/llmeshoptimizer/CMakeLists.txt create mode 100644 indra/llmeshoptimizer/llmeshoptimizer.cpp create mode 100644 indra/llmeshoptimizer/llmeshoptimizer.h (limited to 'indra/llmeshoptimizer') diff --git a/indra/llmeshoptimizer/CMakeLists.txt b/indra/llmeshoptimizer/CMakeLists.txt new file mode 100644 index 0000000000..1eea19de66 --- /dev/null +++ b/indra/llmeshoptimizer/CMakeLists.txt @@ -0,0 +1,41 @@ +# -*- cmake -*- + +project(llmeshoptimizer) + +include(MESHOPTIMIZER) + +include(00-Common) +include(LLCommon) + +include_directories( + ${LLCOMMON_INCLUDE_DIRS} + ${LLMESHOPTIMIZER_INCLUDE_DIR} + ${MESHOPTIMIZER_INCLUDE_DIRS} + ${LIBS_PREBUILT_DIR}/include #access to boost headers, needed for LLError + ) + +set(llmeshoptimizer_SOURCE_FILES + llmeshoptimizer.cpp + ) + +set(llmeshoptimizer_HEADER_FILES + CMakeLists.txt + + llmeshoptimizer.h + ) + +set_source_files_properties(${llmeshoptimizer_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND llmeshoptimizer_SOURCE_FILES ${llmeshoptimizer_HEADER_FILES}) + +#if (USE_MESHOPT) + add_library (llmeshoptimizer ${llmeshoptimizer_SOURCE_FILES}) + + target_link_libraries(llmeshoptimizer + ${LLCOMMON_LIBRARIES} + ${MESHOPTIMIZER_LIBRARY}) + + # Add tests + +#endif (USE_MESHOPT) diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp new file mode 100644 index 0000000000..770cd5aa3c --- /dev/null +++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp @@ -0,0 +1,43 @@ + /** +* @file llmeshoptimizer.cpp +* @brief Wrapper around meshoptimizer +* +* $LicenseInfo:firstyear=2021&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2021, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "llmeshoptimizer.h" + +#include "linden_common.h" + +#include "meshoptimizer.h" +#include "llmath.h" + + +LLMeshOptimizer::LLMeshOptimizer() +{ + +} + +LLMeshOptimizer::~LLMeshOptimizer() +{ + +} diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h new file mode 100644 index 0000000000..edce4edf05 --- /dev/null +++ b/indra/llmeshoptimizer/llmeshoptimizer.h @@ -0,0 +1,37 @@ +/** +* @file llmeshoptimizer.h +* @brief Wrapper around meshoptimizer +* +* $LicenseInfo:firstyear=2021&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2021, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ +#ifndef LLMESHOPTIMIZER_H +#define LLMESHOPTIMIZER_H + +class LLMeshOptimizer +{ +public: + LLMeshOptimizer(); + ~LLMeshOptimizer(); +private: +}; + +#endif //LLMESHOPTIMIZER_H -- cgit v1.3 From 7b7b8a8da8f3a7e726b7de2b152cd00c67df0f18 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 7 Jul 2021 22:34:26 +0300 Subject: DRTVWR-542 WIP --- indra/llmeshoptimizer/llmeshoptimizer.cpp | 14 +- indra/llmeshoptimizer/llmeshoptimizer.h | 5 + indra/newview/llfloatermodelpreview.cpp | 19 +- indra/newview/llmodelpreview.cpp | 203 ++++++++++++++++++++- indra/newview/llmodelpreview.h | 12 +- .../skins/default/xui/en/floater_model_preview.xml | 16 ++ 6 files changed, 255 insertions(+), 14 deletions(-) (limited to 'indra/llmeshoptimizer') diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp index 770cd5aa3c..c46fa8dcf3 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.cpp +++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp @@ -26,18 +26,24 @@ #include "llmeshoptimizer.h" -#include "linden_common.h" - #include "meshoptimizer.h" -#include "llmath.h" LLMeshOptimizer::LLMeshOptimizer() { - + // Todo: Looks like for memory management, we can add allocator and deallocator callbacks + // Should be one time + // meshopt_setAllocator(allocate, deallocate); } LLMeshOptimizer::~LLMeshOptimizer() { } + +//static +U32 LLMeshOptimizer::simplifyModel() +{ + LL_WARNS() << "NOT IMPLEMENTED" << LL_ENDL; + return 0; +} diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h index edce4edf05..d53ec2e24c 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.h +++ b/indra/llmeshoptimizer/llmeshoptimizer.h @@ -26,11 +26,16 @@ #ifndef LLMESHOPTIMIZER_H #define LLMESHOPTIMIZER_H +#include "linden_common.h" + class LLMeshOptimizer { public: LLMeshOptimizer(); ~LLMeshOptimizer(); + + // returns state + static U32 simplifyModel(); private: }; diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index d9edd4dc30..d8f3021fb2 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -722,7 +722,20 @@ void LLFloaterModelPreview::onAutoFillCommit(LLUICtrl* ctrl, void* userdata) void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) { - mModelPreview->onLODParamCommit(lod, enforce_tri_limit); + LLComboBox* lod_source_combo = getChild("lod_source_" + lod_name[lod]); + S32 mode = lod_source_combo->getCurrentIndex(); + switch (mode) + { + case LLModelPreview::GENERATE: + mModelPreview->onLODGenerateParamCommit(lod, enforce_tri_limit); + break; + case LLModelPreview::MESH_OPTIMIZER: + mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit); + break; + default: + LL_ERRS() << "Only supposed to be called to generate models" << LL_ENDL; + break; + } //refresh LoDs that reference this one for (S32 i = lod - 1; i >= 0; --i) @@ -1721,7 +1734,9 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod) refresh(); LLComboBox* lod_source_combo = getChild("lod_source_" + lod_name[lod]); - if (lod_source_combo->getCurrentIndex() == LLModelPreview::GENERATE) + S32 index = lod_source_combo->getCurrentIndex(); + if (index == LLModelPreview::GENERATE + || index == LLModelPreview::MESH_OPTIMIZER) { //rebuild LoD to update triangle counts onLODParamCommit(lod, true); } diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index a9e80ab5da..a6eef74cdc 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -41,6 +41,7 @@ #include "lliconctrl.h" #include "llmatrix4a.h" #include "llmeshrepository.h" +#include "llmeshoptimizer.h" #include "llrender.h" #include "llsdutil_math.h" #include "llskinningutil.h" @@ -1339,7 +1340,7 @@ void LLModelPreview::restoreNormals() updateStatusMessages(); } -void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit) +void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit) { // Allow LoD from -1 to LLModel::LOD_PHYSICS if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1) @@ -1424,7 +1425,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim mRequestedLoDMode[which_lod] = lod_mode; } - if (lod_mode == 0) + if (lod_mode == LIMIT_TRIANGLES) { lod_mode = GLOD_TRIANGLE_BUDGET; @@ -2957,7 +2958,7 @@ BOOL LLModelPreview::render() { genBuffers(-1, skin_weight); //genBuffers(3); - //genLODs(); + //genGlodLODs(); } if (!mModel[mPreviewLOD].empty()) @@ -3544,7 +3545,7 @@ bool LLModelPreview::lodQueryCallback() { S32 lod = preview->mLodsQuery.back(); preview->mLodsQuery.pop_back(); - preview->genLODs(lod); + preview->genGlodLODs(lod); if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH)) { @@ -3559,12 +3560,202 @@ bool LLModelPreview::lodQueryCallback() return true; } -void LLModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) +void LLModelPreview::onLODGenerateParamCommit(S32 lod, bool enforce_tri_limit) { if (!mLODFrozen) { - genLODs(lod, 3, enforce_tri_limit); + genGlodLODs(lod, 3, enforce_tri_limit); refresh(); } } +void LLModelPreview::onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_limit) +{ + if (mLODFrozen) + { + return; + } + + // Allow LoD from -1 to LLModel::LOD_PHYSICS + if (lod < -1 || lod > LLModel::NUM_LODS - 1) + { + std::ostringstream out; + out << "Invalid level of detail: " << lod; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + assert(lod >= -1 && lod < LLModel::NUM_LODS); + return; + } + + if (mBaseModel.empty()) + { + return; + } + + + U32 triangle_count = 0; + U32 instanced_triangle_count = 0; + + //get the triangle count for the whole scene + for (LLModelLoader::scene::iterator iter = mBaseScene.begin(), endIter = mBaseScene.end(); iter != endIter; ++iter) + { + for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) + { + LLModel* mdl = instance->mModel; + if (mdl) + { + instanced_triangle_count += mdl->getNumTriangles(); + } + } + } + + //get the triangle count for the non-instanced set of models + for (U32 i = 0; i < mBaseModel.size(); ++i) + { + triangle_count += mBaseModel[i]->getNumTriangles(); + } + + //get ratio of uninstanced triangles to instanced triangles + F32 triangle_ratio = (F32)triangle_count / (F32)instanced_triangle_count; + U32 base_triangle_count = triangle_count; + U32 lod_mode = 0; + F32 lod_error_threshold = 0; + + + // Urgh... + // 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! + LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[lod]); + if (iface) + { + lod_mode = iface->getFirstSelectedIndex(); + } + + lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[lod]).asReal(); + + if (lod != -1) + { + mRequestedLoDMode[lod] = lod_mode; + } + + S32 limit = -1; + if (lod_mode == LIMIT_TRIANGLES) + { + limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[lod]).asInteger(); + //convert from "scene wide" to "non-instanced" triangle limit + limit = (S32)((F32)limit*triangle_ratio); + } + + // Glod regenerates vertex buffer at this stage + + // Build models + + S32 start = LLModel::LOD_HIGH; + S32 end = 0; + S32 decimation = 3; + + if (lod != -1) + { + start = lod; + end = lod; + } + + mMaxTriangleLimit = base_triangle_count; + + for (S32 lod = start; lod >= end; --lod) + { + if (lod == -1) + { + if (lod < start) + { + triangle_count /= decimation; + } + } + else + { + if (enforce_tri_limit) + { + triangle_count = limit; + } + else + { + for (S32 j = LLModel::LOD_HIGH; j > lod; --j) + { + triangle_count /= decimation; + } + } + } + + mModel[lod].clear(); + mModel[lod].resize(mBaseModel.size()); + mVertexBuffer[lod].clear(); + + mRequestedTriangleCount[lod] = (S32)((F32)triangle_count / triangle_ratio); + mRequestedErrorThreshold[lod] = lod_error_threshold; + + for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx) + { + LLModel* base = mBaseModel[mdl_idx]; + + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f); + + std::string name = base->mLabel + getLodSuffix(lod); + + mModel[lod][mdl_idx]->mLabel = name; + mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID; + //mModel[lod][mdl_idx]->setNumVolumeFaces( ); + + LLModel* target_model = mModel[lod][mdl_idx]; + + + // + LLMeshOptimizer::simplifyModel(/*Some params*/); + + //blind copy skin weights and just take closest skin weight to point on + //decimated mesh for now (auto-generating LODs with skin weights is still a bit + //of an open problem). + target_model->mPosition = base->mPosition; + target_model->mSkinWeights = base->mSkinWeights; + target_model->mSkinInfo = base->mSkinInfo; + //copy material list + target_model->mMaterialList = base->mMaterialList; + + if (!validate_model(target_model)) + { + LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL; + } + } + + //rebuild scene based on mBaseScene + mScene[lod].clear(); + mScene[lod] = mBaseScene; + + for (U32 i = 0; i < mBaseModel.size(); ++i) + { + LLModel* mdl = mBaseModel[i]; + LLModel* target = mModel[lod][i]; + if (target) + { + for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) + { + for (U32 j = 0; j < iter->second.size(); ++j) + { + if (iter->second[j].mModel == mdl) + { + iter->second[j].mModel = target; + } + } + } + } + } + } + + mResourceCost = calcResourceCost(); + + + LLMeshOptimizer::simplifyModel(); + refresh(); +} + diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h index 3664a27a72..df66d79b7b 100644 --- a/indra/newview/llmodelpreview.h +++ b/indra/newview/llmodelpreview.h @@ -125,9 +125,16 @@ public: { LOD_FROM_FILE = 0, GENERATE, + MESH_OPTIMIZER, USE_LOD_ABOVE, } eLoDMode; + typedef enum + { + LIMIT_TRIANGLES = 0, + LIMIT_ERROR_TRESHOLD, + } eLoDLimit; + public: // Todo: model preview shouldn't need floater dependency, it // should just expose data to floater, not control flaoter like it does @@ -155,7 +162,7 @@ public: void loadModelCallback(S32 lod); bool lodsReady() { return !mGenLOD && mLodsQuery.empty(); } void queryLODs() { mGenLOD = true; }; - void genLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false); + void genGlodLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false); void generateNormals(); void restoreNormals(); U32 calcResourceCost(); @@ -166,7 +173,8 @@ public: void updateStatusMessages(); void updateLodControls(S32 lod); void clearGLODGroup(); - void onLODParamCommit(S32 lod, bool enforce_tri_limit); + void onLODGenerateParamCommit(S32 lod, bool enforce_tri_limit); + void onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_limit); void addEmptyFace(LLModel* pTarget); const bool getModelPivot(void) const { return mHasPivot; } 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 7f863756eb..96bda42316 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,10 @@ name="Generate" label="Generate" value="Generate" /> + + + + Date: Thu, 8 Jul 2021 20:29:28 +0300 Subject: DRTVWR-542 WIP #2 --- indra/cmake/CMakeLists.txt | 1 + indra/cmake/MESHOPTIMIZER.cmake | 8 ++++---- indra/llmeshoptimizer/CMakeLists.txt | 5 ++++- indra/llmeshoptimizer/llmeshoptimizer.cpp | 24 +++++++++++++++++++----- indra/llmeshoptimizer/llmeshoptimizer.h | 12 +++++++++++- indra/newview/llmodelpreview.cpp | 27 +++++++++++++++++++++++---- 6 files changed, 62 insertions(+), 15 deletions(-) (limited to 'indra/llmeshoptimizer') diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 7795aa7db1..527a9cba43 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -75,6 +75,7 @@ set(cmake_SOURCE_FILES LLXML.cmake Linking.cmake MediaPluginBase.cmake + MESHOPTIMIZER.cmake NDOF.cmake OPENAL.cmake OpenGL.cmake diff --git a/indra/cmake/MESHOPTIMIZER.cmake b/indra/cmake/MESHOPTIMIZER.cmake index 7600a56999..6a31dc5569 100644 --- a/indra/cmake/MESHOPTIMIZER.cmake +++ b/indra/cmake/MESHOPTIMIZER.cmake @@ -1,16 +1,16 @@ # -*- cmake -*- +include(Linking) include(Prebuilt) use_prebuilt_binary(meshoptimizer) if (WINDOWS) - set(MESHOPTIMIZER_LIBRARIES - debug meshoptimizer - optimized meshoptimizer) + set(MESHOPTIMIZER_LIBRARIES meshoptimizer.lib) elseif (LINUX) - set(MESHOPTIMIZER_LIBRARIES meshoptimizer) + set(MESHOPTIMIZER_LIBRARIES meshoptimizer.o) elseif (DARWIN) set(MESHOPTIMIZER_LIBRARIES libmeshoptimizer.o) endif (WINDOWS) + set(MESHOPTIMIZER_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/meshoptimizer) diff --git a/indra/llmeshoptimizer/CMakeLists.txt b/indra/llmeshoptimizer/CMakeLists.txt index 1eea19de66..016794cfad 100644 --- a/indra/llmeshoptimizer/CMakeLists.txt +++ b/indra/llmeshoptimizer/CMakeLists.txt @@ -6,9 +6,11 @@ include(MESHOPTIMIZER) include(00-Common) include(LLCommon) +include(LLMath) include_directories( ${LLCOMMON_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} ${LLMESHOPTIMIZER_INCLUDE_DIR} ${MESHOPTIMIZER_INCLUDE_DIRS} ${LIBS_PREBUILT_DIR}/include #access to boost headers, needed for LLError @@ -34,7 +36,8 @@ list(APPEND llmeshoptimizer_SOURCE_FILES ${llmeshoptimizer_HEADER_FILES}) target_link_libraries(llmeshoptimizer ${LLCOMMON_LIBRARIES} - ${MESHOPTIMIZER_LIBRARY}) + ${LLMATH_LIBRARIES} + ${MESHOPTIMIZER_LIBRARIES}) # Add tests diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp index c46fa8dcf3..02e97ef984 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.cpp +++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp @@ -28,7 +28,6 @@ #include "meshoptimizer.h" - LLMeshOptimizer::LLMeshOptimizer() { // Todo: Looks like for memory management, we can add allocator and deallocator callbacks @@ -38,12 +37,27 @@ LLMeshOptimizer::LLMeshOptimizer() LLMeshOptimizer::~LLMeshOptimizer() { - } //static -U32 LLMeshOptimizer::simplifyModel() +U64 LLMeshOptimizer::simplify(U16 *destination, + const U16 *indices, + U64 index_count, + const LLVector4a *vertex_positions, + U64 vertex_count, + U64 target_index_count, + F32 target_error, + F32* result_error + ) { - LL_WARNS() << "NOT IMPLEMENTED" << LL_ENDL; - return 0; + return meshopt_simplify(destination, + indices, + index_count, + (const float*)vertex_positions, // verify that it is correct to convert to float + vertex_count, + sizeof(LLVector4a), // should be either 0 or 4 + target_index_count, + target_error, + result_error + ); } diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h index d53ec2e24c..a0c53ed9e1 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.h +++ b/indra/llmeshoptimizer/llmeshoptimizer.h @@ -28,6 +28,8 @@ #include "linden_common.h" +#include "llmath.h" + class LLMeshOptimizer { public: @@ -35,7 +37,15 @@ public: ~LLMeshOptimizer(); // returns state - static U32 simplifyModel(); + static U64 simplify( + U16 *destination, + const U16 *indices, + U64 index_count, + const LLVector4a *vertex_positions, + U64 vertex_count, + U64 target_index_count, + F32 target_error, + F32* result_error); private: }; diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index a6eef74cdc..f23143e3fa 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -3705,13 +3705,33 @@ void LLModelPreview::onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_lim mModel[lod][mdl_idx]->mLabel = name; mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID; - //mModel[lod][mdl_idx]->setNumVolumeFaces( ); + mModel[lod][mdl_idx]->setNumVolumeFaces(base->getNumVolumeFaces()); LLModel* target_model = mModel[lod][mdl_idx]; + // Run meshoptimizer for each face + for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) + { + const LLVolumeFace &face = base->getVolumeFace(face_idx); + S32 num_indices = face.mNumIndices; + std::vector output(num_indices); //todo: do not allocate per each face, add one large buffer somewhere + + // todo: run generateShadowIndexBuffer, at some stage, potentially inside simplify + + F32 error_code = 0; + LLMeshOptimizer::simplify(&output[0], + &face.mIndices[0], + num_indices, + &face.mPositions[0], + face.mNumVertices, + triangle_count * 3, //todo: indices limit, not triangles limit*3 + lod_error_threshold, + &error_code); + + // todo: copy result into face + } - // - LLMeshOptimizer::simplifyModel(/*Some params*/); + LL_WARNS() << "WORK IN PROGRESS" << LL_ENDL; //blind copy skin weights and just take closest skin weight to point on //decimated mesh for now (auto-generating LODs with skin weights is still a bit @@ -3755,7 +3775,6 @@ void LLModelPreview::onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_lim mResourceCost = calcResourceCost(); - LLMeshOptimizer::simplifyModel(); refresh(); } -- cgit v1.3 From 6047b7c4383be53718fc72d7058a7435ccda7785 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 9 Jul 2021 23:42:37 +0300 Subject: DRTVWR-542 WIP #3 First functional meshoptimizer --- indra/llmeshoptimizer/llmeshoptimizer.h | 3 +- indra/newview/llmodelpreview.cpp | 180 ++++++++++++++++++-------------- 2 files changed, 101 insertions(+), 82 deletions(-) (limited to 'indra/llmeshoptimizer') diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h index a0c53ed9e1..157de0251d 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.h +++ b/indra/llmeshoptimizer/llmeshoptimizer.h @@ -36,7 +36,8 @@ public: LLMeshOptimizer(); ~LLMeshOptimizer(); - // returns state + // returns amount of indices in destiantion + // result_error returns how far from original the model is in % if not NULL static U64 simplify( U16 *destination, const U16 *indices, diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index f23143e3fa..0cf44ca1cb 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1404,7 +1404,7 @@ void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - U32 lod_mode = 0; + U32 lod_mode = LIMIT_TRIANGLES; F32 lod_error_threshold = 0; @@ -3569,7 +3569,7 @@ void LLModelPreview::onLODGenerateParamCommit(S32 lod, bool enforce_tri_limit) } } -void LLModelPreview::onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_limit) +void LLModelPreview::onLODMeshOptimizerParamCommit(S32 requested_lod, bool enforce_tri_limit) { if (mLODFrozen) { @@ -3577,10 +3577,10 @@ void LLModelPreview::onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_lim } // Allow LoD from -1 to LLModel::LOD_PHYSICS - if (lod < -1 || lod > LLModel::NUM_LODS - 1) + if (requested_lod < -1 || requested_lod > LLModel::NUM_LODS - 1) { std::ostringstream out; - out << "Invalid level of detail: " << lod; + out << "Invalid level of detail: " << requested_lod; LL_WARNS() << out.str() << LL_ENDL; LLFloaterModelPreview::addStringToLog(out, false); assert(lod >= -1 && lod < LLModel::NUM_LODS); @@ -3592,106 +3592,108 @@ void LLModelPreview::onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_lim return; } - - U32 triangle_count = 0; - U32 instanced_triangle_count = 0; - - //get the triangle count for the whole scene - for (LLModelLoader::scene::iterator iter = mBaseScene.begin(), endIter = mBaseScene.end(); iter != endIter; ++iter) - { - for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) - { - LLModel* mdl = instance->mModel; - if (mdl) - { - instanced_triangle_count += mdl->getNumTriangles(); - } - } - } - - //get the triangle count for the non-instanced set of models - for (U32 i = 0; i < mBaseModel.size(); ++i) + //get the triangle count for all base models + S32 base_triangle_count = 0; + for (S32 i = 0; i < mBaseModel.size(); ++i) { - triangle_count += mBaseModel[i]->getNumTriangles(); + base_triangle_count += mBaseModel[i]->getNumTriangles(); } - //get ratio of uninstanced triangles to instanced triangles - F32 triangle_ratio = (F32)triangle_count / (F32)instanced_triangle_count; - U32 base_triangle_count = triangle_count; - U32 lod_mode = 0; - F32 lod_error_threshold = 0; - - // Urgh... // 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! - LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[lod]); - if (iface) + U32 lod_mode = LIMIT_TRIANGLES; + F32 indices_ratio = 0; + F32 triangle_limit = 0; + F32 lod_error_threshold = 100; + F32 decimation = 3.f; + + // If requesting a single lod + if (requested_lod > -1 && requested_lod < NUM_LOD) { - lod_mode = iface->getFirstSelectedIndex(); - } + LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[requested_lod]); + if (iface) + { + lod_mode = iface->getFirstSelectedIndex(); + } - lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[lod]).asReal(); + if (lod_mode == LIMIT_TRIANGLES) + { + if (!enforce_tri_limit && lod_mode == LIMIT_TRIANGLES) + { + triangle_limit = base_triangle_count; + // reset to default value for this lod + for (S32 j = LLModel::LOD_HIGH; j > requested_lod; --j) + { + triangle_limit /= decimation; + indices_ratio /= decimation; + } + } + else + { + // meshoptimizer doesn't use triangle limit, it uses indices limit, so convert it to ratio - if (lod != -1) - { - mRequestedLoDMode[lod] = lod_mode; - } + // UI spacifies limit for all models of single lod + triangle_limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[requested_lod]).asInteger(); - S32 limit = -1; - if (lod_mode == LIMIT_TRIANGLES) + // calculate aproximate ratio + indices_ratio = (F32)triangle_limit / (F32)base_triangle_count; + } + } + else + { + lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[requested_lod]).asReal(); + } + } + else { - limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[lod]).asInteger(); - //convert from "scene wide" to "non-instanced" triangle limit - limit = (S32)((F32)limit*triangle_ratio); + // we are genrating all lods and each lod will get own indices_ratio + indices_ratio = 1; + triangle_limit = base_triangle_count; } - // Glod regenerates vertex buffer at this stage + mMaxTriangleLimit = base_triangle_count; + + // TODO: Glod regenerates vertex buffer at this stage + // check why, it might be needed to regenerate buffer as well // Build models S32 start = LLModel::LOD_HIGH; S32 end = 0; - S32 decimation = 3; - if (lod != -1) + if (requested_lod != -1) { - start = lod; - end = lod; + start = requested_lod; + end = requested_lod; } - mMaxTriangleLimit = base_triangle_count; + LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + if (shader) + { + shader->unbind(); + } for (S32 lod = start; lod >= end; --lod) { - if (lod == -1) + if (requested_lod == -1) { + // we are genrating all lods and each lod gets own indices_ratio if (lod < start) { - triangle_count /= decimation; - } - } - else - { - if (enforce_tri_limit) - { - triangle_count = limit; - } - else - { - for (S32 j = LLModel::LOD_HIGH; j > lod; --j) - { - triangle_count /= decimation; - } + indices_ratio /= decimation; + triangle_limit /= decimation; } } + mRequestedTriangleCount[lod] = triangle_limit; + mRequestedErrorThreshold[lod] = lod_error_threshold; + mRequestedLoDMode[requested_lod] = lod_mode; + mModel[lod].clear(); mModel[lod].resize(mBaseModel.size()); mVertexBuffer[lod].clear(); - mRequestedTriangleCount[lod] = (S32)((F32)triangle_count / triangle_ratio); - mRequestedErrorThreshold[lod] = lod_error_threshold; for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx) { @@ -3718,20 +3720,30 @@ void LLModelPreview::onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_lim // todo: run generateShadowIndexBuffer, at some stage, potentially inside simplify - F32 error_code = 0; - LLMeshOptimizer::simplify(&output[0], - &face.mIndices[0], - num_indices, - &face.mPositions[0], - face.mNumVertices, - triangle_count * 3, //todo: indices limit, not triangles limit*3 - lod_error_threshold, - &error_code); + F32 result_code = 0; // how far from original the model is + S32 new_indices = LLMeshOptimizer::simplify(&output[0], + face.mIndices, + num_indices, + &face.mPositions[0], + face.mNumVertices, + num_indices * indices_ratio, + lod_error_threshold, + &result_code); - // todo: copy result into face - } + LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); + + // Copy old values + // todo: no point copying faces? + new_face = face; - LL_WARNS() << "WORK IN PROGRESS" << LL_ENDL; + // Assign new values + S32 idx_size = (new_indices * sizeof(U16) + 0xF) & ~0xF; + LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)(&output[0]), idx_size); + new_face.mNumIndices = new_indices; + + // clear unused values + new_face.optimize(); + } //blind copy skin weights and just take closest skin weight to point on //decimated mesh for now (auto-generating LODs with skin weights is still a bit @@ -3739,6 +3751,7 @@ void LLModelPreview::onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_lim target_model->mPosition = base->mPosition; target_model->mSkinWeights = base->mSkinWeights; target_model->mSkinInfo = base->mSkinInfo; + //copy material list target_model->mMaterialList = base->mMaterialList; @@ -3774,6 +3787,11 @@ void LLModelPreview::onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_lim mResourceCost = calcResourceCost(); + LLVertexBuffer::unbind(); + if (shader) + { + shader->bind(); + } refresh(); } -- cgit v1.3 From eb13133e3e0020c73399414cea4d9b39ef526cd3 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 12 Jul 2021 16:47:45 +0300 Subject: DRTVWR-542 WIP #5 --- indra/cmake/MESHOPTIMIZER.cmake | 2 +- indra/llmeshoptimizer/llmeshoptimizer.cpp | 7 ++++++- indra/newview/llmodelpreview.cpp | 23 ++++++++++------------- 3 files changed, 17 insertions(+), 15 deletions(-) (limited to 'indra/llmeshoptimizer') diff --git a/indra/cmake/MESHOPTIMIZER.cmake b/indra/cmake/MESHOPTIMIZER.cmake index 6a31dc5569..1c5b47b9bd 100644 --- a/indra/cmake/MESHOPTIMIZER.cmake +++ b/indra/cmake/MESHOPTIMIZER.cmake @@ -10,7 +10,7 @@ if (WINDOWS) elseif (LINUX) set(MESHOPTIMIZER_LIBRARIES meshoptimizer.o) elseif (DARWIN) - set(MESHOPTIMIZER_LIBRARIES libmeshoptimizer.o) + set(MESHOPTIMIZER_LIBRARIES libmeshoptimizer.a) endif (WINDOWS) set(MESHOPTIMIZER_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/meshoptimizer) diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp index 02e97ef984..dbf8e5b631 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.cpp +++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp @@ -50,12 +50,17 @@ U64 LLMeshOptimizer::simplify(U16 *destination, F32* result_error ) { + const size_t vertex_stride = 4; // should be either 0 or 4 + + // Consider running meshopt_generateShadowIndexBuffer first. + // meshopt_generateShadowIndexBuffer is only needed if models don't use some of the vertices, + // but since we call optimize() in a lot of cases, it likely isn't needed return meshopt_simplify(destination, indices, index_count, (const float*)vertex_positions, // verify that it is correct to convert to float vertex_count, - sizeof(LLVector4a), // should be either 0 or 4 + vertex_stride, target_index_count, target_error, result_error diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 27341eff3c..5eb49ee938 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1767,7 +1767,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en } // meshoptimizer doesn't use triangle limit, it uses indices limit, so convert it to aproximate ratio - indices_ratio = (F32)triangle_limit / (F32)base_triangle_count; + indices_ratio = triangle_limit / (F32)base_triangle_count; } else { @@ -1849,7 +1849,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en // todo: run generateShadowIndexBuffer, at some stage, potentially inside simplify - F32 target_indices = llmax((F32)3, num_indices * indices_ratio); // leave at least one triangle + S32 target_indices = llmax(3, llfloor(num_indices * indices_ratio)); // leave at least one triangle F32 result_code = 0; // how far from original the model is S32 new_indices = LLMeshOptimizer::simplify(&output[0], face.mIndices, @@ -1868,9 +1868,16 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); // Copy old values - // todo: no point copying faces? new_face = face; + // Assign new values + S32 idx_size = (new_indices * sizeof(U16) + 0xF) & ~0xF; + LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)(&output[0]), idx_size); + new_face.mNumIndices = new_indices; + + // clear unused values + new_face.optimize(); + if (new_indices == 0) { LL_WARNS() << "No indices generated for face " << face_idx @@ -1878,16 +1885,6 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en << " target Indices: " << target_indices << " original count: " << num_indices << LL_ENDL; } - else - { - // Assign new values - S32 idx_size = (new_indices * sizeof(U16) + 0xF) & ~0xF; - LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)(&output[0]), idx_size); - new_face.mNumIndices = new_indices; - - // clear unused values - new_face.optimize(); - } } //blind copy skin weights and just take closest skin weight to point on -- cgit v1.3 From 938969c811732a3e2faf0229301de286bd12c1a5 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 12 Jul 2021 19:18:25 +0300 Subject: DRTVWR-542 WIP #6 Trying out 'sloppy' variant --- indra/llmeshoptimizer/llmeshoptimizer.cpp | 24 +++++++++++++ indra/llmeshoptimizer/llmeshoptimizer.h | 12 +++++++ indra/newview/llfloatermodelpreview.cpp | 8 +++-- indra/newview/llmodelpreview.cpp | 42 ++++++++++++++++------ indra/newview/llmodelpreview.h | 5 +-- .../skins/default/xui/en/floater_model_preview.xml | 16 +++++++++ 6 files changed, 92 insertions(+), 15 deletions(-) (limited to 'indra/llmeshoptimizer') diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp index dbf8e5b631..2f3d2491c2 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.cpp +++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp @@ -66,3 +66,27 @@ U64 LLMeshOptimizer::simplify(U16 *destination, result_error ); } + +//static +U64 LLMeshOptimizer::simplifySloppy(U16 *destination, + const U16 *indices, + U64 index_count, + const LLVector4a *vertex_positions, + U64 vertex_count, + U64 target_index_count, + F32 target_error, + F32* result_error + ) +{ + const size_t vertex_stride = 4; // should be either 0 or 4 + return meshopt_simplifySloppy(destination, + indices, + index_count, + (const float*)vertex_positions, // verify that it is correct to convert to float + vertex_count, + vertex_stride, + target_index_count, + target_error, + result_error + ); +} diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h index 157de0251d..c4250c537d 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.h +++ b/indra/llmeshoptimizer/llmeshoptimizer.h @@ -47,6 +47,18 @@ public: 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 target_index_count, + F32 target_error, + F32* result_error); private: }; diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 85250abefc..d61311d610 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -730,7 +730,10 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) mModelPreview->onLODGenerateParamCommit(lod, enforce_tri_limit); break; case LLModelPreview::MESH_OPTIMIZER: - mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit); + mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, false); + break; + case LLModelPreview::MESH_OPTIMIZER_SLOPPY: + mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, true); break; default: LL_ERRS() << "Only supposed to be called to generate models" << LL_ENDL; @@ -1736,7 +1739,8 @@ 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) + || index == LLModelPreview::MESH_OPTIMIZER + || index == LLModelPreview::MESH_OPTIMIZER_SLOPPY) { //rebuild LoD to update triangle counts onLODParamCommit(lod, true); } diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 5eb49ee938..c33b48c16e 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1706,7 +1706,7 @@ void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri } } -void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit) +void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit, bool sloppy) { LL_INFOS() << "Generating lod " << which_lod << " using meshoptimizer" << LL_ENDL; // Allow LoD from -1 to LLModel::LOD_PHYSICS @@ -1851,14 +1851,34 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en S32 target_indices = llmax(3, llfloor(num_indices * indices_ratio)); // leave at least one triangle F32 result_code = 0; // how far from original the model is - S32 new_indices = LLMeshOptimizer::simplify(&output[0], - face.mIndices, - num_indices, - &face.mPositions[0], - face.mNumVertices, - target_indices, - lod_error_threshold, - &result_code); + S32 new_indices = 0; + + if (sloppy) + { + new_indices = LLMeshOptimizer::simplifySloppy( + &output[0], + face.mIndices, + num_indices, + &face.mPositions[0], + face.mNumVertices, + target_indices, + lod_error_threshold, + &result_code); + } + + if (new_indices <= 0) + { + new_indices = LLMeshOptimizer::simplify( + &output[0], + face.mIndices, + num_indices, + &face.mPositions[0], + face.mNumVertices, + target_indices, + lod_error_threshold, + &result_code); + } + if (result_code < 0) { @@ -3800,11 +3820,11 @@ void LLModelPreview::onLODGenerateParamCommit(S32 lod, bool enforce_tri_limit) } } -void LLModelPreview::onLODMeshOptimizerParamCommit(S32 requested_lod, bool enforce_tri_limit) +void LLModelPreview::onLODMeshOptimizerParamCommit(S32 requested_lod, bool enforce_tri_limit, bool sloppy) { if (!mLODFrozen) { - genMeshOptimizerLODs(requested_lod, 3, enforce_tri_limit); + genMeshOptimizerLODs(requested_lod, 3, enforce_tri_limit, sloppy); refresh(); } } diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h index f6f4ce580d..9f38156ca2 100644 --- a/indra/newview/llmodelpreview.h +++ b/indra/newview/llmodelpreview.h @@ -126,6 +126,7 @@ public: LOD_FROM_FILE = 0, GENERATE, MESH_OPTIMIZER, + MESH_OPTIMIZER_SLOPPY, USE_LOD_ABOVE, } eLoDMode; @@ -163,7 +164,7 @@ public: bool lodsReady() { return !mGenLOD && mLodsQuery.empty(); } void queryLODs() { mGenLOD = true; }; void genGlodLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false); - void genMeshOptimizerLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false); + void genMeshOptimizerLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false, bool sloppy = false); void generateNormals(); void restoreNormals(); U32 calcResourceCost(); @@ -175,7 +176,7 @@ public: void updateLodControls(S32 lod); void clearGLODGroup(); void onLODGenerateParamCommit(S32 lod, bool enforce_tri_limit); - void onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_limit); + void onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_limit, bool sloppy); void addEmptyFace(LLModel* pTarget); const bool getModelPivot(void) const { return mHasPivot; } 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 96bda42316..230284555c 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -180,6 +180,10 @@ name="MeshOpt" label="MeshOpt" value="MeshOpt" /> + + + + Date: Mon, 12 Jul 2021 23:23:08 +0300 Subject: DRTVWR-542 WIP #7 --- indra/llmeshoptimizer/llmeshoptimizer.cpp | 23 ++++++++++++++++++++--- indra/llmeshoptimizer/llmeshoptimizer.h | 7 +++++++ indra/newview/llmodelpreview.cpp | 16 ++++++++++++---- 3 files changed, 39 insertions(+), 7 deletions(-) (limited to 'indra/llmeshoptimizer') diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp index 2f3d2491c2..0ddaa82c3d 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.cpp +++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp @@ -39,6 +39,26 @@ LLMeshOptimizer::~LLMeshOptimizer() { } +//static +void LLMeshOptimizer::generateShadowIndexBuffer(U16 *destination, + const U16 *indices, + U64 index_count, + const LLVector4a *vertex_positions, + U64 vertex_count +) +{ + const size_t vertex_stride = 4; // should be either 0 or 4 + + meshopt_generateShadowIndexBuffer(destination, + indices, + index_count, + (const float*)vertex_positions, // verify that it is correct to convert to float + vertex_count, + sizeof(LLVector4a), + vertex_stride + ); +} + //static U64 LLMeshOptimizer::simplify(U16 *destination, const U16 *indices, @@ -52,9 +72,6 @@ U64 LLMeshOptimizer::simplify(U16 *destination, { const size_t vertex_stride = 4; // should be either 0 or 4 - // Consider running meshopt_generateShadowIndexBuffer first. - // meshopt_generateShadowIndexBuffer is only needed if models don't use some of the vertices, - // but since we call optimize() in a lot of cases, it likely isn't needed return meshopt_simplify(destination, indices, index_count, diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h index c4250c537d..744856361c 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.h +++ b/indra/llmeshoptimizer/llmeshoptimizer.h @@ -36,6 +36,13 @@ public: LLMeshOptimizer(); ~LLMeshOptimizer(); + static void LLMeshOptimizer::generateShadowIndexBuffer( + U16 *destination, + const U16 *indices, + U64 index_count, + const LLVector4a *vertex_positions, + U64 vertex_count); + // returns amount of indices in destiantion // result_error returns how far from original the model is in % if not NULL static U64 simplify( diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index c33b48c16e..471b29d850 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1847,10 +1847,18 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en S32 num_indices = face.mNumIndices; std::vector output(num_indices); //todo: do not allocate per each face, add one large buffer somewhere - // todo: run generateShadowIndexBuffer, at some stage, potentially inside simplify + /* + generateShadowIndexBuffer appears to deform model + LLMeshOptimizer::generateShadowIndexBuffer( + &shadow[0], + face.mIndices, + num_indices, + &face.mPositions[0], + face.mNumVertices); + */ S32 target_indices = llmax(3, llfloor(num_indices * indices_ratio)); // leave at least one triangle - F32 result_code = 0; // how far from original the model is + F32 result_code = 0; // how far from original the model is, 1 == 100% S32 new_indices = 0; if (sloppy) @@ -1859,7 +1867,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en &output[0], face.mIndices, num_indices, - &face.mPositions[0], + face.mPositions, face.mNumVertices, target_indices, lod_error_threshold, @@ -1872,7 +1880,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en &output[0], face.mIndices, num_indices, - &face.mPositions[0], + face.mPositions, face.mNumVertices, target_indices, lod_error_threshold, -- cgit v1.3 From 66ba1c4c8e840bb5e9da23e2b5772cd24b23714f Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 13 Jul 2021 02:14:16 +0300 Subject: DRTVWR-542 WIP Fixed Stride --- indra/llmeshoptimizer/llmeshoptimizer.cpp | 19 +++++++++---------- indra/llmeshoptimizer/llmeshoptimizer.h | 7 +++++-- indra/newview/llmodelpreview.cpp | 2 ++ 3 files changed, 16 insertions(+), 12 deletions(-) (limited to 'indra/llmeshoptimizer') diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp index 0ddaa82c3d..09fa7cb235 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.cpp +++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp @@ -44,18 +44,17 @@ void LLMeshOptimizer::generateShadowIndexBuffer(U16 *destination, const U16 *indices, U64 index_count, const LLVector4a *vertex_positions, - U64 vertex_count + U64 vertex_count, + U64 vertex_positions_stride ) { - const size_t vertex_stride = 4; // should be either 0 or 4 - meshopt_generateShadowIndexBuffer(destination, indices, index_count, (const float*)vertex_positions, // verify that it is correct to convert to float vertex_count, sizeof(LLVector4a), - vertex_stride + vertex_positions_stride ); } @@ -65,19 +64,19 @@ U64 LLMeshOptimizer::simplify(U16 *destination, U64 index_count, const LLVector4a *vertex_positions, U64 vertex_count, + U64 vertex_positions_stride, U64 target_index_count, F32 target_error, F32* result_error ) { - const size_t vertex_stride = 4; // should be either 0 or 4 return meshopt_simplify(destination, indices, index_count, - (const float*)vertex_positions, // verify that it is correct to convert to float + (const float*)vertex_positions, vertex_count, - vertex_stride, + vertex_positions_stride, target_index_count, target_error, result_error @@ -90,18 +89,18 @@ U64 LLMeshOptimizer::simplifySloppy(U16 *destination, U64 index_count, const LLVector4a *vertex_positions, U64 vertex_count, + U64 vertex_positions_stride, U64 target_index_count, F32 target_error, F32* result_error ) { - const size_t vertex_stride = 4; // should be either 0 or 4 return meshopt_simplifySloppy(destination, indices, index_count, - (const float*)vertex_positions, // verify that it is correct to convert to float + (const float*)vertex_positions, vertex_count, - vertex_stride, + vertex_positions_stride, target_index_count, target_error, result_error diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h index 744856361c..1aa02372fa 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.h +++ b/indra/llmeshoptimizer/llmeshoptimizer.h @@ -36,12 +36,13 @@ public: LLMeshOptimizer(); ~LLMeshOptimizer(); - static void LLMeshOptimizer::generateShadowIndexBuffer( + static void generateShadowIndexBuffer( U16 *destination, const U16 *indices, U64 index_count, const LLVector4a *vertex_positions, - U64 vertex_count); + U64 vertex_count, + U64 vertex_positions_stride); // returns amount of indices in destiantion // result_error returns how far from original the model is in % if not NULL @@ -51,6 +52,7 @@ public: U64 index_count, const LLVector4a *vertex_positions, U64 vertex_count, + U64 vertex_positions_stride, U64 target_index_count, F32 target_error, F32* result_error); @@ -63,6 +65,7 @@ public: U64 index_count, const LLVector4a *vertex_positions, U64 vertex_count, + U64 vertex_positions_stride, U64 target_index_count, F32 target_error, F32* result_error); diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 471b29d850..aa065edf92 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1869,6 +1869,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en num_indices, face.mPositions, face.mNumVertices, + LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], target_indices, lod_error_threshold, &result_code); @@ -1882,6 +1883,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en num_indices, face.mPositions, face.mNumVertices, + LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], target_indices, lod_error_threshold, &result_code); -- cgit v1.3 From 9aaac1bb985f5bd65f9b0fe985c47cd30dcfd166 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 23 Jul 2021 21:30:13 +0300 Subject: DRTVWR-542 Attempt to simplify all faces of an object as a whole and split back into faces --- indra/llmeshoptimizer/llmeshoptimizer.cpp | 25 +- indra/llmeshoptimizer/llmeshoptimizer.h | 13 + indra/newview/llfloatermodelpreview.cpp | 8 +- indra/newview/llmodelpreview.cpp | 323 +++++++++++++++------ indra/newview/llmodelpreview.h | 5 +- .../skins/default/xui/en/floater_model_preview.xml | 16 + 6 files changed, 302 insertions(+), 88 deletions(-) (limited to 'indra/llmeshoptimizer') diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp index 09fa7cb235..8097a05511 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.cpp +++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp @@ -58,6 +58,30 @@ void LLMeshOptimizer::generateShadowIndexBuffer(U16 *destination, ); } +//static +U64 LLMeshOptimizer::simplifyU32(U32 *destination, + const U32 *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_simplify(destination, + indices, + index_count, + (const float*)vertex_positions, + vertex_count, + vertex_positions_stride, + target_index_count, + target_error, + result_error + ); +} + //static U64 LLMeshOptimizer::simplify(U16 *destination, const U16 *indices, @@ -70,7 +94,6 @@ U64 LLMeshOptimizer::simplify(U16 *destination, F32* result_error ) { - return meshopt_simplify(destination, indices, index_count, diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h index 1aa02372fa..2696733eb2 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.h +++ b/indra/llmeshoptimizer/llmeshoptimizer.h @@ -44,6 +44,19 @@ public: U64 vertex_count, U64 vertex_positions_stride); + // returns amount of indices in destiantion + // result_error returns how far from original the model is in % if not NULL + static U64 simplifyU32( + U32 *destination, + const U32 *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); + // returns amount of indices in destiantion // result_error returns how far from original the model is in % if not NULL static U64 simplify( diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index d61311d610..3f2fcb148c 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -730,10 +730,9 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) mModelPreview->onLODGenerateParamCommit(lod, enforce_tri_limit); break; case LLModelPreview::MESH_OPTIMIZER: - mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, false); - break; case LLModelPreview::MESH_OPTIMIZER_SLOPPY: - mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, true); + case LLModelPreview::MESH_OPTIMIZER_COMBINE: + mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, mode); break; default: LL_ERRS() << "Only supposed to be called to generate models" << LL_ENDL; @@ -1740,7 +1739,8 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod) S32 index = lod_source_combo->getCurrentIndex(); if (index == LLModelPreview::GENERATE || index == LLModelPreview::MESH_OPTIMIZER - || index == LLModelPreview::MESH_OPTIMIZER_SLOPPY) + || index == LLModelPreview::MESH_OPTIMIZER_SLOPPY + || index == LLModelPreview::MESH_OPTIMIZER_COMBINE) { //rebuild LoD to update triangle counts onLODParamCommit(lod, true); } diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index f21b05f9dc..6542ac9090 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1706,7 +1706,7 @@ void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri } } -void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit, bool sloppy) +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; // Allow LoD from -1 to LLModel::LOD_PHYSICS @@ -1840,108 +1840,269 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en LLModel* target_model = mModel[lod][mdl_idx]; - // Run meshoptimizer for each face - for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) - { - 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); - - /* - generateShadowIndexBuffer appears to deform model - LLMeshOptimizer::generateShadowIndexBuffer( - &shadow[0], - face.mIndices, - num_indices, - &face.mPositions[0], - face.mNumVertices); - */ - - S32 target_indices = 0; - F32 result_code = 0; // how far from original the model is, 1 == 100% - S32 new_indices = 0; + if (meshopt_mode == MESH_OPTIMIZER_COMBINE) + { + // Figure out buffer size + S32 size_indices = 0; + S32 size_vertices = 0; - if (sloppy) + for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - 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); + const LLVolumeFace &face = base->getVolumeFace(face_idx); + size_indices += face.mNumIndices; + size_vertices += face.mNumVertices; } - else + + // 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, is it needed? + S32 size = ((size_vertices * sizeof(LLVector2)) + 0xF) & ~0xF; + LLVector4a* combined_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_vertices + size); + + // copy indices and vertices into new buffers + S32 vertices_copied = 0; + S32 indices_shift = 0; + S32 indices_copied = 0; + for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - 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); + const LLVolumeFace &face = base->getVolumeFace(face_idx); + + // vertices + S32 copy_bytes = face.mNumVertices * sizeof(LLVector4a); + LLVector4a* dest = combined_positions + vertices_copied; + LLVector4a::memcpyNonAliased16((F32*)dest, (F32*)face.mPositions, copy_bytes); + vertices_copied += face.mNumVertices; + + // todo: figure if need to copy normals and text coords + + // indices, sadly can't do dumb memcpy for indices, we need to adjust each value + for (S32 i = 0; i < face.mNumIndices; ++i) + { + U16 idx = face.mIndices[i]; + + combined_indices[indices_copied] = idx + indices_shift; + indices_copied++; + } + indices_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 face " << face_idx - << " of model " << target_model->mLabel + LL_WARNS() << "Negative result code from meshoptimizer for model " << target_model->mLabel << " target Indices: " << target_indices << " new Indices: " << new_indices - << " original count: " << num_indices << LL_ENDL; + << " original count: " << size_indices << LL_ENDL; } - LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); + // repack back into individual faces + indices_shift = 0; + for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) + { + const LLVolumeFace &face = base->getVolumeFace(face_idx); + LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); + + S32 size = (face.mNumIndices * sizeof(U16) + 0xF) & ~0xF; + U16* output = (U16*)ll_aligned_malloc_16(size); - // Copy old values - new_face = face; + // Copy old values + new_face = face; + // Fow now crude method to copy indices back into face + // Should have been done in reverse - cycle by indices and figure out target face from id's size + // Experiment + S32 indices_copied = 0; + for (S32 i = 0; i < new_indices / 3; ++i) + { + U32 position = i * 3; + U32 idx1 = output_indices[position]; + U32 idx2 = output_indices[position+1]; + U32 idx3 = output_indices[position+2]; + + bool copy = false; + S32 range = indices_shift + face.mNumVertices; + + if (idx1 >= indices_shift && idx1 < range) + { + output[indices_copied] = idx1 - indices_shift; + copy = true; + } + if (copy) + { + if (idx2 >= indices_shift && idx2 < range) + { + output[indices_copied + 1] = idx2 - indices_shift; + } + else + { + // todo: extend size of face's vertices list and add new vertices + LL_WARNS_ONCE() << "incomplete code, dropped triangle" << LL_ENDL; + output[indices_copied + 1] = 0; + } - if (new_indices == 0) + if (idx3 < face.mNumVertices) + { + output[indices_copied + 2] = idx3 - indices_shift; + } + else + { + // todo: extend size of face's vertices list and add new vertices + LL_WARNS_ONCE() << "incomplete code, dropped triangle" << LL_ENDL; + output[indices_copied + 2] = 0; + } + } + if (copy) + { + indices_copied += 3; + } + + new_face.resizeIndices(indices_copied); + S32 idx_size = (indices_copied * sizeof(U16) + 0xF) & ~0xF; + LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)output, idx_size); + + } + indices_shift += face.mNumVertices; + + if (indices_copied == 0) + { + 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(); + } + } + + ll_aligned_free<64>(combined_positions); + ll_aligned_free_32(output_indices); + ll_aligned_free_32(combined_indices); + } + else + { + // Run meshoptimizer for each face + for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - if (!sloppy) + 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); + + /* + generateShadowIndexBuffer appears to deform model + LLMeshOptimizer::generateShadowIndexBuffer( + &shadow[0], + face.mIndices, + num_indices, + &face.mPositions[0], + face.mNumVertices); + */ + + S32 target_indices = 0; + F32 result_code = 0; // how far from original the model is, 1 == 100% + S32 new_indices = 0; + + if (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 (meshopt_mode == MESH_OPTIMIZER) { - // 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 + 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) + { + 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 << 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); + LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); - // clear unused values - new_face.optimize(); - } + // Copy old values + new_face = face; + + + if (new_indices == 0) + { + if (meshopt_mode != MESH_OPTIMIZER_SLOPPY) + { + // 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 << 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); - ll_aligned_free_16(output); + // clear unused values + new_face.optimize(); + } + + ll_aligned_free_16(output); + } } //blind copy skin weights and just take closest skin weight to point on @@ -3833,7 +3994,7 @@ bool LLModelPreview::lodQueryCallback() { S32 lod = preview->mLodsQuery.back(); preview->mLodsQuery.pop_back(); - preview->genMeshOptimizerLODs(lod); + preview->genMeshOptimizerLODs(lod, MESH_OPTIMIZER); if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH)) { @@ -3857,11 +4018,11 @@ void LLModelPreview::onLODGenerateParamCommit(S32 lod, bool enforce_tri_limit) } } -void LLModelPreview::onLODMeshOptimizerParamCommit(S32 requested_lod, bool enforce_tri_limit, bool sloppy) +void LLModelPreview::onLODMeshOptimizerParamCommit(S32 requested_lod, bool enforce_tri_limit, S32 mode) { if (!mLODFrozen) { - genMeshOptimizerLODs(requested_lod, 3, enforce_tri_limit, sloppy); + genMeshOptimizerLODs(requested_lod, mode, 3, enforce_tri_limit); refresh(); } } diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h index 9f38156ca2..b784345b5a 100644 --- a/indra/newview/llmodelpreview.h +++ b/indra/newview/llmodelpreview.h @@ -127,6 +127,7 @@ public: GENERATE, MESH_OPTIMIZER, MESH_OPTIMIZER_SLOPPY, + MESH_OPTIMIZER_COMBINE, USE_LOD_ABOVE, } eLoDMode; @@ -164,7 +165,7 @@ public: bool lodsReady() { return !mGenLOD && mLodsQuery.empty(); } void queryLODs() { mGenLOD = true; }; void genGlodLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false); - void genMeshOptimizerLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false, bool sloppy = false); + void genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 decimation = 3, bool enforce_tri_limit = false); void generateNormals(); void restoreNormals(); U32 calcResourceCost(); @@ -176,7 +177,7 @@ public: void updateLodControls(S32 lod); void clearGLODGroup(); void onLODGenerateParamCommit(S32 lod, bool enforce_tri_limit); - void onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_limit, bool sloppy); + void onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_limit, S32 mode); void addEmptyFace(LLModel* pTarget); const bool getModelPivot(void) const { return mHasPivot; } 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 230284555c..5c78ef55d1 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -184,6 +184,10 @@ name="MeshOptSloppy" label="MeshOptSloppy" value="MeshOptSloppy" /> + + + + Date: Wed, 28 Jul 2021 21:25:18 +0300 Subject: DRTVWR-542 Rename simplification methods in UI and add a fallback --- indra/llmeshoptimizer/llmeshoptimizer.h | 6 ++- indra/newview/llmodelpreview.cpp | 56 ++++++++++++---------- .../skins/default/xui/en/floater_model_preview.xml | 24 +++++----- 3 files changed, 46 insertions(+), 40 deletions(-) (limited to 'indra/llmeshoptimizer') diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h index 2696733eb2..e881ec26c5 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.h +++ b/indra/llmeshoptimizer/llmeshoptimizer.h @@ -46,6 +46,7 @@ public: // returns amount of indices in destiantion // 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( U32 *destination, const U32 *indices, @@ -57,8 +58,9 @@ public: 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 + // Returns amount of indices in destiantion + // 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, const U16 *indices, diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 3704cc2ea5..96eb9340f5 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1840,7 +1840,9 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d LLModel* target_model = mModel[lod][mdl_idx]; - if (meshopt_mode == MESH_OPTIMIZER_COMBINE) + S32 model_meshopt_mode = meshopt_mode; + + if (model_meshopt_mode == MESH_OPTIMIZER_COMBINE) { // Figure out buffer size S32 size_indices = 0; @@ -1961,22 +1963,23 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d if (copy_triangle) { - if (idx >= U16_MAX) - { - // Shouldn't happen due to simplification? - // Todo: add a fallback? - LL_ERRS() << "Over triangle limit" << LL_ENDL; - break; - } - if (old_to_new_positions_map[idx] == -1) { // New position, need to copy it // Validate size if (buf_positions_copied >= U16_MAX) { - // Shouldn't happen due to simplification? - LL_ERRS() << "Over triangle limit" << LL_ENDL; + // 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; } @@ -1999,6 +2002,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d } } + if (buf_positions_copied >= U16_MAX) + { + break; + } + LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); //new_face = face; //temp @@ -2037,7 +2045,9 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d ll_aligned_free_32(buffer_indices); ll_aligned_free_32(combined_indices); } - else + + 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) @@ -2049,21 +2059,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d S32 size = (num_indices * sizeof(U16) + 0xF) & ~0xF; U16* output = (U16*)ll_aligned_malloc_16(size); - /* - generateShadowIndexBuffer appears to deform model - LLMeshOptimizer::generateShadowIndexBuffer( - &shadow[0], - face.mIndices, - num_indices, - &face.mPositions[0], - face.mNumVertices); - */ - S32 target_indices = 0; F32 result_code = 0; // how far from original the model is, 1 == 100% S32 new_indices = 0; - if (meshopt_mode == MESH_OPTIMIZER_SLOPPY) + if (model_meshopt_mode == MESH_OPTIMIZER_SLOPPY) { target_indices = llfloor(num_indices * indices_ratio); new_indices = LLMeshOptimizer::simplifySloppy( @@ -2078,7 +2078,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d &result_code); } - if (meshopt_mode == MESH_OPTIMIZER) + if (model_meshopt_mode == MESH_OPTIMIZER) { target_indices = llmax(3, llfloor(num_indices * indices_ratio)); // leave at least one triangle new_indices = LLMeshOptimizer::simplify( @@ -2100,7 +2100,9 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d << " of model " << target_model->mLabel << " target Indices: " << target_indices << " new Indices: " << new_indices - << " original count: " << num_indices << LL_ENDL; + << " original count: " << num_indices + << " error treshold: " << lod_error_threshold + << LL_ENDL; } LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); @@ -2118,7 +2120,9 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d LL_INFOS() << "No indices generated by meshoptimizer for face " << face_idx << " of model " << target_model->mLabel << " target Indices: " << target_indices - << " original count: " << num_indices << LL_ENDL; + << " original count: " << num_indices + << " error treshold: " << lod_error_threshold + << LL_ENDL; } // Face got optimized away 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 5c78ef55d1..cd67adf42a 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -178,15 +178,15 @@ value="Generate" /> 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/llmeshoptimizer') 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" /> + + - + + - + + - + + -