From 00875f93d0dfb98a8137cf44435a63f7a3c8319a Mon Sep 17 00:00:00 2001 From: Beq Date: Thu, 28 Jul 2022 15:33:10 +0100 Subject: [PATCH] Restore GLOD alongside MeshOpt --- autobuild.xml | 94 ++++ indra/cmake/CMakeLists.txt | 1 + indra/cmake/Copy3rdPartyLibs.cmake | 2 + indra/cmake/GLOD.cmake | 15 + indra/newview/CMakeLists.txt | 8 + indra/newview/fsperfstats.h | 2 +- indra/newview/licenses-win32.txt | 66 +++ indra/newview/llfloatermodelpreview.cpp | 4 + indra/newview/llmodelpreview.cpp | 523 +++++++++++++++++- indra/newview/llmodelpreview.h | 14 + .../skins/default/xui/en/floater_about.xml | 1 + .../default/xui/en/floater_model_preview.xml | 16 + indra/newview/viewer_manifest.py | 10 + 13 files changed, 751 insertions(+), 5 deletions(-) create mode 100644 indra/cmake/GLOD.cmake diff --git a/autobuild.xml b/autobuild.xml index a597a9db5d..22807205e4 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -235,6 +235,100 @@ + glod + + copyright + Copyright 2003 Jonathan Cohen, Nat Duca, David Luebke, Brenden Schubert - Johns Hopkins University and University of Virginia + license + GLOD Open-Source License Version 1.0 + license_file + LICENSES/GLOD.txt + name + glod + platforms + + darwin + + archive + + hash + 94fc457c46e1fb94b31251bd4747d10f + hash_algorithm + md5 + url + http://3p.firestormviewer.org/glod-1.0pre3.171101143-darwin64-171101143.tar.bz2 + + name + darwin + + darwin64 + + archive + + hash + 94fc457c46e1fb94b31251bd4747d10f + url + http://3p.firestormviewer.org/glod-1.0pre3.171101143-darwin64-171101143.tar.bz2 + + name + darwin64 + + linux + + archive + + hash + 3bae1e9a188680a81c1d5b32571538b9 + hash_algorithm + md5 + url + http://3p.firestormviewer.org/glod-1.0pre3.180990859-linux-180990859.tar.bz2 + + name + linux + + linux64 + + archive + + hash + acc1181cd31ef32c3724eda84ae4b580 + url + http://3p.firestormviewer.org/glod-1.0pre3.180990827-linux64-180990827.tar.bz2 + + name + linux64 + + windows + + archive + + hash + e1f8da12a2b7a6c31830b4bb86d31ed6 + hash_algorithm + md5 + url + http://3p.firestormviewer.org/glod-1.0pre3.vs2017-1906061512-windows-vs2017-1906061512.tar.bz2 + + name + windows + + windows64 + + archive + + hash + e906cf08bfbfbd9d4fc78557e021e7d0 + url + http://3p.firestormviewer.org/glod-1.0pre3.vs2017-1906061512-windows64-vs2017-1906061512.tar.bz2 + + name + windows64 + + + version + 1.0pre3.532346 + gntp-growl copyright diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 4ea6f62f7d..7bd4dce8fc 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -40,6 +40,7 @@ set(cmake_SOURCE_FILES FreeType.cmake GLEXT.cmake GLH.cmake + GLOD.cmake ## GStreamer010Plugin.cmake GoogleMock.cmake Growl.cmake diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index ffc810f6b0..bdeac63e5a 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -57,6 +57,7 @@ if(WINDOWS) libaprutil-1.dll libapriconv-1.dll nghttp2.dll + glod.dll # restore GLOD libhunspell.dll uriparser.dll ) @@ -198,6 +199,7 @@ elseif(DARWIN) libaprutil-1.0.dylib libaprutil-1.dylib ${EXPAT_COPY} + libGLOD.dylib # restore GLOD libhunspell-1.3.0.dylib libndofdev.dylib libnghttp2.dylib diff --git a/indra/cmake/GLOD.cmake b/indra/cmake/GLOD.cmake new file mode 100644 index 0000000000..2580ead67b --- /dev/null +++ b/indra/cmake/GLOD.cmake @@ -0,0 +1,15 @@ +# -*- cmake -*- + +#if (USESYSTEMLIBS) +# set(GLOD_FIND_REQUIRED true) +# include(FindGLOD) +#else (USESYSTEMLIBS) + include(Prebuilt) + use_prebuilt_binary(glod) +set(GLOD_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include) +if(LINUX) + set(GLOD_LIBRARIES GLOD vds) +else() + set(GLOD_LIBRARIES GLOD) +endif() +#endif (USESYSTEMLIBS) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 0ae24b60be..3626ee623f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -17,6 +17,7 @@ include(GLIB) include(DragDrop) include(EXPAT) include(FMODSTUDIO) +include(GLOD) # restore GLOD include(Hunspell) include(JPEGEncoderBasic) include(JsonCpp) @@ -83,6 +84,7 @@ endif(FMODSTUDIO) include_directories( ${DBUSGLIB_INCLUDE_DIRS} ${JSONCPP_INCLUDE_DIR} + ${GLOD_INCLUDE_DIR} # restore GLOD ${LLAUDIO_INCLUDE_DIRS} ${LLCHARACTER_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} @@ -2272,6 +2274,11 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libapr-1.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libaprutil-1.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libapriconv-1.dll + # Restore GLOD build dependencies + ${SHARED_LIB_STAGING_DIR}/Release/glod.dll + ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/glod.dll + ${SHARED_LIB_STAGING_DIR}/Debug/glod.dll + # ${SHARED_LIB_STAGING_DIR}/Release/libcollada14dom22.dll ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libcollada14dom22.dll ${SHARED_LIB_STAGING_DIR}/Debug/libcollada14dom22-d.dll @@ -2521,6 +2528,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${OPENGL_LIBRARIES} ${FMODWRAPPER_LIBRARY} # must come after LLAudio ${OPENAL_LIBRARIES} + ${GLOD_LIBRARIES} # restore GLOD dependencies ${OPENGL_LIBRARIES} ${JSONCPP_LIBRARIES} ${SDL_LIBRARY} diff --git a/indra/newview/fsperfstats.h b/indra/newview/fsperfstats.h index 590c91598d..d690e0bcf2 100644 --- a/indra/newview/fsperfstats.h +++ b/indra/newview/fsperfstats.h @@ -44,7 +44,7 @@ #ifdef TRACY_ENABLE // USAGE_TRACKING - displays overlapping stats that may imply double counting. // ATTACHMENT_TRACKING - displays detailed tracking info for Avatar and Attachment. very heavy overhead. -#define USAGE_TRACKING +// #define USAGE_TRACKING #define ATTACHMENT_TRACKING #else #undef USAGE_TRACKING diff --git a/indra/newview/licenses-win32.txt b/indra/newview/licenses-win32.txt index 837d92139d..d1b3d0c8b7 100644 --- a/indra/newview/licenses-win32.txt +++ b/indra/newview/licenses-win32.txt @@ -796,3 +796,69 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +============= +GLOD license +============= +The GLOD Open-Source License Version 1.0 June 16, 2004 + +Copyright (C) 2003-04 Jonathan Cohen, Nat Duca, Chris Niski, Johns +Hopkins University and David Luebke, Brenden Schubert, University of +Virginia. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, is permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer and + request. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer and + request in the documentation and/or other materials provided with + the distribution. + +3. The name "GLOD" must not be used to endorse or promote products + derived from this software without prior written permission. + +4. Redistributions of any modified version of this source, whether in + source or binary form , must include a form of the following + acknowledgment: "This product is derived from the GLOD library, + which is available from http://www.cs.jhu.edu/~graphics/GLOD." + +5. Redistributions of any modified version of this source in binary + form must provide, free of charge, access to the modified version + of the code. + +6. This license shall be governed by and construed and enforced in + accordance with the laws of the State of Maryland, without + reference to its conflicts of law provisions. The exclusive + jurisdiction and venue for all legal actions relating to this + license shall be in courts of competent subject matter jurisdiction + located in the State of Maryland. + +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, GLOD IS PROVIDED +UNDER THIS LICENSE ON AN AS IS BASIS, WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +THAT GLOD IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR +PURPOSE OR NON-INFRINGING. ALL WARRANTIES ARE DISCLAIMED AND THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE CODE IS WITH +YOU. SHOULD ANY CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE +COPYRIGHT HOLDER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY +NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY +CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY CODE IS +AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL +THE COPYRIGHT HOLDER OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY +SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES FOR LOSS OF +PROFITS, REVENUE, OR FOR LOSS OF INFORMATION OR ANY OTHER LOSS. + +YOU EXPRESSLY AGREE TO FOREVER INDEMNIFY, DEFEND AND HOLD HARMLESS THE +COPYRIGHT HOLDERS AND CONTRIBUTORS OF GLOD AGAINST ALL CLAIMS, +DEMANDS, SUITS OR OTHER ACTIONS ARISING DIRECTLY OR INDIRECTLY FROM +YOUR ACCEPTANCE AND USE OF GLOD. + +Although NOT REQUIRED, we would appreciate it if active users of GLOD +put a link on their web site to the GLOD web site when possible. diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 08eeded896..694b077720 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -828,6 +828,9 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) case LLModelPreview::MESH_OPTIMIZER_COMBINE: mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, mode); break; + case LLModelPreview::GENERATE: + mModelPreview->onLODGLODParamCommit(lod, enforce_tri_limit); + break; default: LL_ERRS() << "Only supposed to be called to generate models" << LL_ENDL; break; @@ -1957,6 +1960,7 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod) S32 index = lod_source_combo->getCurrentIndex(); if (index == LLModelPreview::MESH_OPTIMIZER_AUTO + || index == LLModelPreview::GENERATE // Improved LOD generation || index == LLModelPreview::MESH_OPTIMIZER_SLOPPY || index == LLModelPreview::MESH_OPTIMIZER_COMBINE) { //rebuild LoD to update triangle counts diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 9786e8f857..a0ab52dcfb 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -92,6 +92,7 @@ bool LLModelPreview::sIgnoreLoadedCallback = false; // const F32 SKIN_WEIGHT_CAMERA_DISTANCE = 16.f; +#include "glod/glod.h" // More flexible LOD generation // mesh loader suffix configuration //static const std::array LLModelPreview::sSuffixVarNames @@ -103,6 +104,25 @@ const std::array LLModelPreview::sSuffixVarNames "FSMeshPhysicsSuffix" }; // + +// More flexible LOD generation +BOOL stop_gloderror() +{ + GLuint error = glodGetError(); + + if (error != GLOD_NO_ERROR) + { + std::ostringstream out; + out << "GLOD error detected, cannot generate LOD (try another method?): " << std::hex << error; + LL_WARNS("MeshUpload") << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + return TRUE; + } + + return FALSE; +} +// + LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& material) { LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap(), FTT_DEFAULT, TRUE, LLGLTexture::BOOST_PREVIEW); @@ -222,6 +242,12 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) mLoadState = LLModelLoader::STARTING; mGroup = 0; mLODFrozen = false; + // Improved LOD generation + mBuildShareTolerance = 0.f; + mBuildQueueMode = GLOD_QUEUE_GREEDY; + mBuildBorderMode = GLOD_BORDER_UNLOCK; + mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE; + // mUVGuideTexture = LLViewerTextureManager::getFetchedTextureFromFile(gSavedSettings.getString("FSMeshPreviewUVGuideFile"), FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); // - Add UV guide overlay to pmesh preview for (U32 i = 0; i < LLModel::NUM_LODS; ++i) @@ -234,7 +260,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) mViewOption["show_textures"] = false; mFMP = fmp; - + glodInit(); // Improved LOD generation mHasPivot = false; mModelPivot = LLVector3(0.0f, 0.0f, 0.0f); @@ -918,7 +944,12 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable } mLODFile[lod] = filename; - + // Improved LOD generation + if (lod == LLModel::LOD_HIGH) + { + clearGLODGroup(); + } + // std::map joint_alias_map; getJointAliases(joint_alias_map); @@ -1061,12 +1092,31 @@ void LLModelPreview::clearIncompatible(S32 lod) mBaseModel = mModel[lod]; mBaseScene = mScene[lod]; mVertexBuffer[5].clear(); + clearGLODGroup(); // Improved LOD generation } } } } } +// Improved LOD generation +void LLModelPreview::clearGLODGroup() +{ + if (mGroup) + { + for (std::map, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter) + { + glodDeleteObject(iter->second); + stop_gloderror(); + } + mObject.clear(); + + glodDeleteGroup(mGroup); + stop_gloderror(); + mGroup = 0; + } +} +// void LLModelPreview::loadModelCallback(S32 loaded_lod) { assert_main_thread(); @@ -1220,6 +1270,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod) } mBaseModel = mModel[loaded_lod]; + clearGLODGroup(); // Improved LOD generation mBaseScene = mScene[loaded_lod]; mVertexBuffer[5].clear(); @@ -1450,6 +1501,453 @@ void LLModelPreview::restoreNormals() updateStatusMessages(); } +// Improved LOD generation +// Restore the GLOD entry point. +// There would appear to be quite a lot of commonality which would be well suited to refactoring but +// LL are still playing with Mesh Optimiser code. +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) + { + std::ostringstream out; + out << "Invalid level of detail: " << which_lod; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + assert(which_lod >= -1 && which_lod < LLModel::NUM_LODS); + return; + } + + if (mBaseModel.empty()) + { + return; + } + + LLVertexBuffer::unbind(); + + LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + + if (shader) + { + shader->unbind(); + } + + stop_gloderror(); + static U32 cur_name = 1; + + S32 limit = -1; + + 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 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + U32 lod_mode = 0; + + F32 lod_error_threshold = 0; + + // The LoD should be in range from Lowest to High + if (which_lod > -1 && which_lod < NUM_LOD) + { + LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[which_lod]); + if (iface) + { + lod_mode = iface->getFirstSelectedIndex(); + } + + lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[which_lod]).asReal(); + } + + if (which_lod != -1) + { + mRequestedLoDMode[which_lod] = lod_mode; + } + + if (lod_mode == 0) + { + lod_mode = GLOD_TRIANGLE_BUDGET; + + // The LoD should be in range from Lowest to High + if (which_lod > -1 && which_lod < NUM_LOD) + { + limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[which_lod]).asInteger(); + //convert from "scene wide" to "non-instanced" triangle limit + limit = (S32)((F32)limit*triangle_ratio); + } + } + else + { + lod_mode = GLOD_ERROR_THRESHOLD; + } + + bool object_dirty = false; + + if (mGroup == 0) + { + object_dirty = true; + mGroup = cur_name++; + glodNewGroup(mGroup); + } + + if (object_dirty) + { + for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) + { //build GLOD objects for each model in base model list + LLModel* mdl = *iter; + + if (mObject[mdl] != 0) + { + glodDeleteObject(mObject[mdl]); + } + + mObject[mdl] = cur_name++; + + glodNewObject(mObject[mdl], mGroup, GLOD_DISCRETE); + stop_gloderror(); + + if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty()) + { //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation + mVertexBuffer[5].clear(); + } + + if (mVertexBuffer[5].empty()) + { + genBuffers(5, false); + } + + U32 tri_count = 0; + for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i) + { + LLVertexBuffer* buff = mVertexBuffer[5][mdl][i]; + buff->setBuffer(type_mask & buff->getTypeMask()); + + U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices(); + if (num_indices > 2) + { + // Fix glod so it works when just using the opengl core profile + //glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f); + LLStrider vertex_strider; + LLStrider normal_strider; + LLStrider tc_strider; + + LLStrider< U16 > index_strider; + buff->getIndexStrider( index_strider ); + + glodVBO vbo = {}; + + if( buff->hasDataType( LLVertexBuffer::TYPE_VERTEX ) ) + { + buff->getVertexStrider( vertex_strider ); + vbo.mV.p = vertex_strider.get(); + vbo.mV.size = 3; + vbo.mV.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_VERTEX ]; + vbo.mV.type = GL_FLOAT; + } + if( buff->hasDataType( LLVertexBuffer::TYPE_NORMAL ) ) + { + buff->getNormalStrider( normal_strider ); + vbo.mN.p = normal_strider.get(); + vbo.mN.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_NORMAL ]; + vbo.mN.type = GL_FLOAT; + } + if( buff->hasDataType( LLVertexBuffer::TYPE_TEXCOORD0 ) ) + { + buff->getTexCoord0Strider( tc_strider ); + vbo.mT.p = tc_strider.get(); + vbo.mT.size = 2; + vbo.mT.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_TEXCOORD0 ]; + vbo.mT.type = GL_FLOAT; + } + + glodInsertElements( mObject[ mdl ], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)index_strider.get(), 0, 0.f, &vbo ); + // + } + tri_count += num_indices / 3; + stop_gloderror(); + } + + glodBuildObject(mObject[mdl]); + stop_gloderror(); + } + } + + + S32 start = LLModel::LOD_HIGH; + S32 end = 0; + + if (which_lod != -1) + { + start = end = which_lod; + } + + mMaxTriangleLimit = base_triangle_count; + + for (S32 lod = start; lod >= end; --lod) + { + if (which_lod == -1) + { + if (lod < start) + { + triangle_count /= decimation; + } + } + else + { + if (enforce_tri_limit) + { + triangle_count = limit; + } + else + { + for (S32 j = LLModel::LOD_HIGH; j>which_lod; --j) + { + triangle_count /= decimation; + } + } + } + + mModel[lod].clear(); + mModel[lod].resize(mBaseModel.size()); + mVertexBuffer[lod].clear(); + + U32 actual_tris = 0; + U32 actual_verts = 0; + U32 submeshes = 0; + + mRequestedTriangleCount[lod] = (S32)((F32)triangle_count / triangle_ratio); + mRequestedErrorThreshold[lod] = lod_error_threshold; + + glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode); + stop_gloderror(); + + glodGroupParameteri(mGroup, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR); + stop_gloderror(); + + glodGroupParameterf(mGroup, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, lod_error_threshold); + stop_gloderror(); + + if (lod_mode != GLOD_TRIANGLE_BUDGET) + { + glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, 0); + } + else + { + //SH-632: always add 1 to desired amount to avoid decimating below desired amount + glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, triangle_count + 1); + } + + stop_gloderror(); + glodAdaptGroup(mGroup); + stop_gloderror(); + + for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx) + { + LLModel* base = mBaseModel[mdl_idx]; + + GLint patch_count = 0; + glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count); + stop_gloderror(); + + 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; + + GLint* sizes = new GLint[patch_count * 2]; + glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes); + stop_gloderror(); + + GLint* names = new GLint[patch_count]; + glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names); + stop_gloderror(); + + mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count); + + LLModel* target_model = mModel[lod][mdl_idx]; + + for (GLint i = 0; i < patch_count; ++i) + { + type_mask = mVertexBuffer[5][base][i]->getTypeMask(); + + LLPointer buff = new LLVertexBuffer(type_mask, 0); + + if (sizes[i * 2 + 1] > 0 && sizes[i * 2] > 0) + { + if (!buff->allocateBuffer(sizes[i * 2 + 1], sizes[i * 2], true)) + { + // Todo: find a way to stop preview in this case instead of crashing + LL_ERRS() << "Failed buffer allocation during preview LOD generation." + << " Vertices: " << sizes[i * 2 + 1] + << " Indices: " << sizes[i * 2] << LL_ENDL; + } + buff->setBuffer(type_mask); + // Fix glod so it works when just using the opengl core profile + //glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, (U8*)buff->getIndicesPointer()); + LLStrider vertex_strider; + LLStrider normal_strider; + LLStrider tc_strider; + + LLStrider< U16 > index_strider; + buff->getIndexStrider( index_strider ); + + glodVBO vbo = {}; + + if( buff->hasDataType( LLVertexBuffer::TYPE_VERTEX ) ) + { + buff->getVertexStrider( vertex_strider ); + vbo.mV.p = vertex_strider.get(); + vbo.mV.size = 3; + vbo.mV.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_VERTEX ]; + vbo.mV.type = GL_FLOAT; + } + if( buff->hasDataType( LLVertexBuffer::TYPE_NORMAL ) ) + { + buff->getNormalStrider( normal_strider ); + vbo.mN.p = normal_strider.get(); + vbo.mN.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_NORMAL ]; + vbo.mN.type = GL_FLOAT; + } + if( buff->hasDataType( LLVertexBuffer::TYPE_TEXCOORD0 ) ) + { + buff->getTexCoord0Strider( tc_strider ); + vbo.mT.p = tc_strider.get(); + vbo.mT.size = 2; + vbo.mT.stride = LLVertexBuffer::sTypeSize[ LLVertexBuffer::TYPE_TEXCOORD0 ]; + vbo.mT.type = GL_FLOAT; + } + + glodFillElements( mObject[ base ], names[ i ], GL_UNSIGNED_SHORT, (U8*)index_strider.get(), &vbo ); + // + stop_gloderror(); + } + else + { + // This face was eliminated or we failed to allocate buffer, + // attempt to create a dummy triangle (one vertex, 3 indices, all 0) + buff->allocateBuffer(1, 3, true); + memset((U8*)buff->getMappedData(), 0, buff->getSize()); + // Fix when running with opengl core profile + //memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize()); + LLStrider< U16 > index_strider; + buff->getIndexStrider( index_strider ); + + memset( (U8*)index_strider.get(), 0, buff->getIndicesSize() ); + // + } + + buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); + + LLStrider pos; + LLStrider norm; + LLStrider tc; + LLStrider index; + + buff->getVertexStrider(pos); + if (type_mask & LLVertexBuffer::MAP_NORMAL) + { + buff->getNormalStrider(norm); + } + if (type_mask & LLVertexBuffer::MAP_TEXCOORD0) + { + buff->getTexCoord0Strider(tc); + } + + buff->getIndexStrider(index); + + target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); + actual_tris += buff->getNumIndices() / 3; + actual_verts += buff->getNumVerts(); + ++submeshes; + + if (!validate_face(target_model->getVolumeFace(names[i]))) + { + std::ostringstream out; + out << "Invalid face generated during LOD generation."; + LLFloaterModelPreview::addStringToLog(out,true); + LL_ERRS() << out.str() << 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 + //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; + } + + delete[] sizes; + delete[] names; + } + + //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; + } + } + } + } + } + } + + LLVertexBuffer::unbind(); + if (shader) + { + shader->bind(); + } + refresh(); // refresh once to make sure render gets called with the updated vbos +} +// + // 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 // returns -1 in case of failure @@ -4368,8 +4866,13 @@ bool LLModelPreview::lodQueryCallback() { S32 lod = preview->mLodsQuery.back(); preview->mLodsQuery.pop_back(); +// Improved LOD generation +#ifdef USE_GLOD_AS_DEFAULT + preview->genLODsUsingGLOD(lod); +#else preview->genMeshOptimizerLODs(lod, MESH_OPTIMIZER_AUTO, 3, false); - +#endif +// if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH)) { preview->lookupLODModelFiles(LLModel::LOD_HIGH); @@ -4383,9 +4886,21 @@ bool LLModelPreview::lodQueryCallback() return true; } +// Improved LOD generation +void LLModelPreview::onLODGLODParamCommit(S32 lod, bool enforce_tri_limit) +{ + if (mFMP && !mLODFrozen) + { + genGlodLODs(lod, 3, enforce_tri_limit); + mFMP->refresh(); + refresh(); + mDirty = true; + } + +} void LLModelPreview::onLODMeshOptimizerParamCommit(S32 requested_lod, bool enforce_tri_limit, S32 mode) { - if (!mLODFrozen) + if (mFMP && !mLODFrozen) // minor sidestep of potential crash { genMeshOptimizerLODs(requested_lod, mode, 3, enforce_tri_limit); mFMP->refresh(); // BUG-231970 Fix b0rken upload floater refresh diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h index 30c9afe842..c3b56ba8f2 100644 --- a/indra/newview/llmodelpreview.h +++ b/indra/newview/llmodelpreview.h @@ -128,6 +128,7 @@ public: MESH_OPTIMIZER_AUTO, // automatically selects method based on model or face MESH_OPTIMIZER_COMBINE, // combines faces into a single model, simplifies, then splits back into faces MESH_OPTIMIZER_SLOPPY, // uses sloppy method, works per face + GENERATE, // Use GLOD Improved LOD generation USE_LOD_ABOVE, } eLoDMode; @@ -165,6 +166,7 @@ public: void loadModelCallback(S32 lod); 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, S32 meshopt_mode, U32 decimation = 3, bool enforce_tri_limit = false); void generateNormals(); void restoreNormals(); @@ -175,6 +177,8 @@ public: void clearIncompatible(S32 lod); void updateStatusMessages(); void updateLodControls(S32 lod); + void clearGLODGroup(); + void onLODGLODParamCommit(S32 lod, bool enforce_tri_limit); void onLODMeshOptimizerParamCommit(S32 lod, bool enforce_tri_limit, S32 mode); void addEmptyFace(LLModel* pTarget); @@ -272,6 +276,16 @@ protected: S32 mRequestedTriangleCount[LLModel::NUM_LODS]; F32 mRequestedErrorThreshold[LLModel::NUM_LODS]; F32 mRequestedCreaseAngle[LLModel::NUM_LODS]; + // Improved LOD generation + F32 mBuildShareTolerance; + U32 mBuildQueueMode; + U32 mBuildOperator; + U32 mBuildBorderMode; + U32 mRequestedBuildOperator[LLModel::NUM_LODS]; + U32 mRequestedQueueMode[LLModel::NUM_LODS]; + U32 mRequestedBorderMode[LLModel::NUM_LODS]; + F32 mRequestedShareTolerance[LLModel::NUM_LODS]; + // LLModelLoader* mModelLoader; diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml index b6dd94fe92..74729a2158 100644 --- a/indra/newview/skins/default/xui/en/floater_about.xml +++ b/indra/newview/skins/default/xui/en/floater_about.xml @@ -248,6 +248,7 @@ expat Copyright (C) 1998, 1999, 2000 Thai Open Source Software Center Ltd. FMOD Sound System, Copyright (C) Firelight Technologies Pty, Ltd., 1994-2020 FreeType Copyright (C) 1996-2002, 2006 David Turner, Robert Wilhelm, and Werner Lemberg. GL Copyright (C) 1999-2004 Brian Paul. +GLOD Copyright (C) 2003-04 Jonathan Cohen, Nat Duca, Chris Niski, Johns Hopkins University and David Luebke, Brenden Schubert, University of Virginia. Havok.com(TM) Copyright (C) 1999-2001, Telekinesys Research Limited. HACD Copyright (C) 2011, Khaled Mamou (kmamou@gmail.com) jpeg2000 Copyright (C) 2001, David Taubman, The University of New South Wales (UNSW) 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 e812ea44dd..edbe4d48d8 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -203,6 +203,10 @@ name="MeshOptSloppy" label="Generate Sloppy" value="MeshOptSloppy" /> + + + +