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/newview/llmodelpreview.cpp | 203 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 197 insertions(+), 6 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') 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(); +} + -- cgit v1.2.3 From 1a17e19a610b598650624fb0ae3e67352f00e499 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 8 Jul 2021 20:29:28 +0300 Subject: DRTVWR-542 WIP #2 --- indra/newview/llmodelpreview.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') 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.2.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/newview/llmodelpreview.cpp | 180 +++++++++++++++++++++------------------ 1 file changed, 99 insertions(+), 81 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') 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.2.3 From 17131ac20336b5561b574a9c121c6a5876be2d53 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sun, 11 Jul 2021 22:39:30 +0300 Subject: DRTVWR-542 WIP #4 Made meshoptimizer into default generation mechanism Ensured that at least one triangle will remain of any face --- indra/newview/llmodelpreview.cpp | 460 ++++++++++++++++++++------------------- 1 file changed, 238 insertions(+), 222 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 0cf44ca1cb..27341eff3c 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1342,6 +1342,7 @@ void LLModelPreview::restoreNormals() void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit) { + LL_INFOS() << "Generating lod " << which_lod << " using glod" << LL_ENDL; // Allow LoD from -1 to LLModel::LOD_PHYSICS if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1) { @@ -1705,6 +1706,239 @@ void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri } } +void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit) +{ + LL_INFOS() << "Generating lod " << which_lod << " using meshoptimizer" << LL_ENDL; + // 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(lod >= -1 && lod < LLModel::NUM_LODS); + return; + } + + if (mBaseModel.empty()) + { + return; + } + + //get the triangle count for all base models + S32 base_triangle_count = 0; + for (S32 i = 0; i < mBaseModel.size(); ++i) + { + base_triangle_count += mBaseModel[i]->getNumTriangles(); + } + + // 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! + U32 lod_mode = LIMIT_TRIANGLES; + F32 indices_ratio = 0; + F32 triangle_limit = 0; + F32 lod_error_threshold = 1; //100% + + // If requesting a single lod + if (which_lod > -1 && which_lod < NUM_LOD) + { + LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[which_lod]); + if (iface) + { + lod_mode = iface->getFirstSelectedIndex(); + } + + if (lod_mode == LIMIT_TRIANGLES) + { + if (!enforce_tri_limit) + { + triangle_limit = base_triangle_count; + // reset to default value for this lod + F32 pw = pow((F32)decimation, (F32)(LLModel::LOD_HIGH - which_lod)); + + triangle_limit /= pw; //indices_ratio can be 1/pw + } + else + { + + // UI spacifies limit for all models of single lod + triangle_limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[which_lod]).asInteger(); + + } + // 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; + } + else + { + lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[which_lod]).asReal(); + } + } + else + { + // we are genrating all lods and each lod will get own indices_ratio + indices_ratio = 1; + triangle_limit = base_triangle_count; + } + + 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; + + if (which_lod != -1) + { + start = which_lod; + end = which_lod; + } + + LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + if (shader) + { + shader->unbind(); + } + + for (S32 lod = start; lod >= end; --lod) + { + if (which_lod == -1) + { + // we are genrating all lods and each lod gets own indices_ratio + if (lod < start) + { + indices_ratio /= decimation; + triangle_limit /= decimation; + } + } + + mRequestedTriangleCount[lod] = triangle_limit; + mRequestedErrorThreshold[lod] = lod_error_threshold; + mRequestedLoDMode[lod] = lod_mode; + + mModel[lod].clear(); + mModel[lod].resize(mBaseModel.size()); + mVertexBuffer[lod].clear(); + + + 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(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 target_indices = llmax((F32)3, 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); + + if (result_code < 0) + { + LL_WARNS() << "Negative result from meshoptimizer" << LL_ENDL; + } + + LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); + + // Copy old values + // todo: no point copying faces? + new_face = face; + + if (new_indices == 0) + { + LL_WARNS() << "No indices generated for face " << face_idx + << " of model " << target_model->mLabel + << " 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 + //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(); + + LLVertexBuffer::unbind(); + if (shader) + { + shader->bind(); + } +} + void LLModelPreview::updateStatusMessages() { // bit mask values for physics errors. used to prevent overwrite of single line status @@ -3545,7 +3779,7 @@ bool LLModelPreview::lodQueryCallback() { S32 lod = preview->mLodsQuery.back(); preview->mLodsQuery.pop_back(); - preview->genGlodLODs(lod); + preview->genMeshOptimizerLODs(lod); if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH)) { @@ -3571,228 +3805,10 @@ void LLModelPreview::onLODGenerateParamCommit(S32 lod, bool enforce_tri_limit) void LLModelPreview::onLODMeshOptimizerParamCommit(S32 requested_lod, bool enforce_tri_limit) { - if (mLODFrozen) - { - return; - } - - // Allow LoD from -1 to LLModel::LOD_PHYSICS - if (requested_lod < -1 || requested_lod > LLModel::NUM_LODS - 1) - { - std::ostringstream out; - out << "Invalid level of detail: " << requested_lod; - LL_WARNS() << out.str() << LL_ENDL; - LLFloaterModelPreview::addStringToLog(out, false); - assert(lod >= -1 && lod < LLModel::NUM_LODS); - return; - } - - if (mBaseModel.empty()) - { - return; - } - - //get the triangle count for all base models - S32 base_triangle_count = 0; - for (S32 i = 0; i < mBaseModel.size(); ++i) - { - base_triangle_count += mBaseModel[i]->getNumTriangles(); - } - - // 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! - 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) - { - LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[requested_lod]); - if (iface) - { - lod_mode = iface->getFirstSelectedIndex(); - } - - 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 - - // UI spacifies limit for all models of single lod - triangle_limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[requested_lod]).asInteger(); - - // 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 - { - // we are genrating all lods and each lod will get own indices_ratio - indices_ratio = 1; - triangle_limit = base_triangle_count; - } - - 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; - - if (requested_lod != -1) - { - start = requested_lod; - end = requested_lod; - } - - LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; - if (shader) - { - shader->unbind(); - } - - for (S32 lod = start; lod >= end; --lod) - { - if (requested_lod == -1) - { - // we are genrating all lods and each lod gets own indices_ratio - if (lod < start) - { - 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(); - - - 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(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 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); - - 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(); - } - - //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(); - - LLVertexBuffer::unbind(); - if (shader) + if (!mLODFrozen) { - shader->bind(); + genMeshOptimizerLODs(requested_lod, 3, enforce_tri_limit); + refresh(); } - - refresh(); } -- cgit v1.2.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/newview/llmodelpreview.cpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') 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.2.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/newview/llmodelpreview.cpp | 42 +++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') 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(); } } -- cgit v1.2.3 From 833a82f8593c513b12b59c489760f77d5a806668 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 12 Jul 2021 23:23:08 +0300 Subject: DRTVWR-542 WIP #7 --- indra/newview/llmodelpreview.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') 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.2.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/newview/llmodelpreview.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/newview/llmodelpreview.cpp') 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.2.3 From 33819e157e9a9638d4869e90601f7b7011804e08 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 13 Jul 2021 11:32:59 +0300 Subject: DRTVWR-542 Fixed size of indices array --- indra/newview/llmodelpreview.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index aa065edf92..26eac71281 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1874,7 +1874,9 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en lod_error_threshold, &result_code); } - + + // todo: instead of checking (new_indices <= 0) + // create a dummy triangle if simplifySloppy returns nothing if (new_indices <= 0) { new_indices = LLMeshOptimizer::simplify( @@ -1892,7 +1894,18 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en if (result_code < 0) { - LL_WARNS() << "Negative result from meshoptimizer" << LL_ENDL; + 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; + } + if (new_indices == 0) + { + LL_WARNS() << "No indices generated by meshoptimizer for face " << face_idx + << " of model " << target_model->mLabel + << " target Indices: " << target_indices + << " original count: " << num_indices << LL_ENDL; } LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); @@ -1901,20 +1914,12 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en new_face = face; // 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[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 - << " of model " << target_model->mLabel - << " target Indices: " << target_indices - << " original count: " << num_indices << LL_ENDL; - } } //blind copy skin weights and just take closest skin weight to point on -- cgit v1.2.3 From a607e7642071690dcab9a1fdf5453fcf83480982 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 13 Jul 2021 20:07:28 +0300 Subject: DRTVWR-542 output buffer should be indentical in size to indices array --- indra/newview/llmodelpreview.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 26eac71281..93186657ce 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1845,7 +1845,10 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en { 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: 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 @@ -1864,7 +1867,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en if (sloppy) { new_indices = LLMeshOptimizer::simplifySloppy( - &output[0], + output, face.mIndices, num_indices, face.mPositions, @@ -1880,7 +1883,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en if (new_indices <= 0) { new_indices = LLMeshOptimizer::simplify( - &output[0], + output, face.mIndices, num_indices, face.mPositions, @@ -1916,7 +1919,9 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en // 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[0]), idx_size); + LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)output, idx_size); + + ll_aligned_free_16(output); // clear unused values new_face.optimize(); -- cgit v1.2.3 From d5857b376f1da9ad4fe25ced1a4785a40a82a709 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 20 Jul 2021 21:51:43 +0300 Subject: DRTVWR-542 Generate placehodler triangles when face got optimized away like glod does --- indra/newview/llmodelpreview.cpp | 55 ++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 19 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 93186657ce..f21b05f9dc 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1860,12 +1860,13 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en face.mNumVertices); */ - S32 target_indices = llmax(3, llfloor(num_indices * indices_ratio)); // leave at least one triangle + S32 target_indices = 0; F32 result_code = 0; // how far from original the model is, 1 == 100% S32 new_indices = 0; if (sloppy) { + target_indices = llfloor(num_indices * indices_ratio); new_indices = LLMeshOptimizer::simplifySloppy( output, face.mIndices, @@ -1877,11 +1878,9 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en lod_error_threshold, &result_code); } - - // todo: instead of checking (new_indices <= 0) - // create a dummy triangle if simplifySloppy returns nothing - if (new_indices <= 0) + else { + target_indices = llmax(3, llfloor(num_indices * indices_ratio)); // leave at least one triangle new_indices = LLMeshOptimizer::simplify( output, face.mIndices, @@ -1903,28 +1902,46 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, U32 decimation, bool en << " new Indices: " << new_indices << " original count: " << num_indices << LL_ENDL; } - if (new_indices == 0) - { - LL_WARNS() << "No indices generated by meshoptimizer for face " << face_idx - << " of model " << target_model->mLabel - << " target Indices: " << target_indices - << " original count: " << num_indices << LL_ENDL; - } LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); // Copy old values new_face = face; - // 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); + if (new_indices == 0) + { + if (!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); - // clear unused values - new_face.optimize(); + // clear unused values + new_face.optimize(); + } + + ll_aligned_free_16(output); } //blind copy skin weights and just take closest skin weight to point on -- cgit v1.2.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/newview/llmodelpreview.cpp | 323 +++++++++++++++++++++++++++++---------- 1 file changed, 242 insertions(+), 81 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') 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(); } } -- cgit v1.2.3 From 8e725e74aa4cebdc472844252adf5b2952f7e3ff Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 26 Jul 2021 21:20:53 +0300 Subject: DRTVWR-542 Attempt to simplify all faces of an object as a whole and split back into faces #2 --- indra/newview/llmodelpreview.cpp | 111 ++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 31 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 6542ac9090..b68162e374 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1857,12 +1857,14 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d 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); + // 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 vertices_copied = 0; + S32 positions_copied = 0; S32 indices_shift = 0; S32 indices_copied = 0; for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) @@ -1871,13 +1873,18 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // 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; + LLVector4a::memcpyNonAliased16((F32*)(combined_positions + positions_copied), (F32*)face.mPositions, copy_bytes); - // todo: figure if need to copy normals and text coords + // normals + LLVector4a::memcpyNonAliased16((F32*)(combined_normals + positions_copied), (F32*)face.mNormals, copy_bytes); - // indices, sadly can't do dumb memcpy for indices, we need to adjust each value + // tex coords + copy_bytes = (face.mNumVertices * sizeof(LLVector2) + 0xF) & ~0xF; + LLVector4a::memcpyNonAliased16((F32*)(combined_tex_coords + positions_copied), (F32*)face.mTexCoords, copy_bytes); + + positions_copied += 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]; @@ -1916,20 +1923,31 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // repack back into individual faces indices_shift = 0; + + 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); + S32 position_buffer_known_size = 0; + + U16* buffer_indices = (U16*)ll_aligned_malloc_16(U16_MAX * sizeof(U16)); + + // Crude method to copy indices back into face + // Should have been done in reverse - cycle by indices and figure out target face from ids 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 face's original data + S32 copy_bytes = face.mNumVertices * sizeof(LLVector4a); + LLVector4a::memcpyNonAliased16((F32*)buffer_positions, (F32*)face.mPositions, copy_bytes); + LLVector4a::memcpyNonAliased16((F32*)buffer_normals, (F32*)face.mNormals, copy_bytes); - // Copy old values - new_face = face; + copy_bytes = (face.mNumVertices * sizeof(LLVector2) + 0xF) & ~0xF; + LLVector4a::memcpyNonAliased16((F32*)buffer_tex_coords, (F32*)face.mTexCoords, copy_bytes); - // 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 + position_buffer_known_size = face.mNumVertices; + + // Copy indices S32 indices_copied = 0; for (S32 i = 0; i < new_indices / 3; ++i) { @@ -1941,49 +1959,60 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d bool copy = false; S32 range = indices_shift + face.mNumVertices; + // todo: we should not copy more than U16_MAX items per face if (idx1 >= indices_shift && idx1 < range) { - output[indices_copied] = idx1 - indices_shift; + buffer_indices[indices_copied] = idx1 - indices_shift; copy = true; } if (copy) { if (idx2 >= indices_shift && idx2 < range) { - output[indices_copied + 1] = idx2 - indices_shift; + buffer_indices[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; + // Extend size of face's positions list + buffer_indices[indices_copied + 1] = (U16)position_buffer_known_size; + buffer_positions[position_buffer_known_size] = combined_positions[idx2]; + buffer_normals[position_buffer_known_size] = combined_normals[idx2]; + buffer_tex_coords[position_buffer_known_size] = combined_tex_coords[idx2]; + position_buffer_known_size++; + + LL_WARNS_ONCE() << "Merged triangle into different face" << LL_ENDL; } if (idx3 < face.mNumVertices) { - output[indices_copied + 2] = idx3 - indices_shift; + buffer_indices[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; + // Extend size of face's positions list + buffer_indices[indices_copied + 2] = position_buffer_known_size; + buffer_positions[position_buffer_known_size] = combined_positions[idx3]; + buffer_normals[position_buffer_known_size] = combined_normals[idx3]; + buffer_tex_coords[position_buffer_known_size] = combined_tex_coords[idx3]; + position_buffer_known_size++; + + LL_WARNS_ONCE() << "Merged triangle into different face" << LL_ENDL; } } + 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; + + LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); + //new_face = face; //temp if (indices_copied == 0) { + // face was optimized away new_face.resizeIndices(3); new_face.resizeVertices(1); memset(new_face.mIndices, 0, sizeof(U16) * 3); @@ -1991,10 +2020,30 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d new_face.mNormals[0].clear(); new_face.mTexCoords[0].setZero(); } + else + { + new_face.resizeIndices(indices_copied); + new_face.resizeVertices(position_buffer_known_size); + + S32 idx_size = (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, position_buffer_known_size * sizeof(LLVector4a)); + LLVector4a::memcpyNonAliased16((F32*)new_face.mNormals, (F32*)buffer_normals, position_buffer_known_size * sizeof(LLVector4a)); + + U32 tex_size = (position_buffer_known_size * sizeof(LLVector2) + 0xF)&~0xF; + LLVector4a::memcpyNonAliased16((F32*)new_face.mTexCoords, (F32*)buffer_tex_coords, tex_size); + + new_face.optimize(); + } + + indices_shift += face.mNumVertices; } 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); } else -- cgit v1.2.3 From 61d2717cfc58ade1f0a4647698603c146ee9e012 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 27 Jul 2021 18:54:44 +0300 Subject: DRTVWR-542 Attempt to simplify all faces of an object as a whole and split back into faces #3 --- indra/newview/llmodelpreview.cpp | 141 +++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 74 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index b68162e374..f005eb0436 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1864,35 +1864,35 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d LLVector2* combined_tex_coords = (LLVector2*)(combined_normals + size_vertices); // copy indices and vertices into new buffers - S32 positions_copied = 0; - S32 indices_shift = 0; - S32 indices_copied = 0; + S32 combined_positions_shift = 0; + S32 indices_idx_shift = 0; + S32 combined_indices_shift = 0; 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 + positions_copied), (F32*)face.mPositions, copy_bytes); + LLVector4a::memcpyNonAliased16((F32*)(combined_positions + combined_positions_shift), (F32*)face.mPositions, copy_bytes); // normals - LLVector4a::memcpyNonAliased16((F32*)(combined_normals + positions_copied), (F32*)face.mNormals, copy_bytes); + 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 + positions_copied), (F32*)face.mTexCoords, copy_bytes); + LLVector4a::memcpyNonAliased16((F32*)(combined_tex_coords + combined_positions_shift), (F32*)face.mTexCoords, copy_bytes); - positions_copied += face.mNumVertices; + 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[indices_copied] = idx + indices_shift; - indices_copied++; + combined_indices[combined_indices_shift] = idx + indices_idx_shift; + combined_indices_shift++; } - indices_shift += face.mNumVertices; + indices_idx_shift += face.mNumVertices; } // Now that we have buffers, optimize @@ -1922,95 +1922,87 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d } // repack back into individual faces - indices_shift = 0; 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); - S32 position_buffer_known_size = 0; - 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 - // Should have been done in reverse - cycle by indices and figure out target face from ids for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { const LLVolumeFace &face = base->getVolumeFace(face_idx); - // copy face's original data - S32 copy_bytes = face.mNumVertices * sizeof(LLVector4a); - LLVector4a::memcpyNonAliased16((F32*)buffer_positions, (F32*)face.mPositions, copy_bytes); - LLVector4a::memcpyNonAliased16((F32*)buffer_normals, (F32*)face.mNormals, copy_bytes); - - copy_bytes = (face.mNumVertices * sizeof(LLVector2) + 0xF) & ~0xF; - LLVector4a::memcpyNonAliased16((F32*)buffer_tex_coords, (F32*)face.mTexCoords, copy_bytes); - - position_buffer_known_size = face.mNumVertices; + // reset data for new run + buf_positions_copied = 0; + buf_indices_copied = 0; + bool copy_triangle = false; + S32 range = indices_idx_shift + face.mNumVertices; - // Copy indices - S32 indices_copied = 0; - for (S32 i = 0; i < new_indices / 3; ++i) + for (S32 i = 0; i < size_vertices; i++) { - U32 position = i * 3; - U32 idx1 = output_indices[position]; - U32 idx2 = output_indices[position+1]; - U32 idx3 = output_indices[position+2]; + old_to_new_positions_map[i] = -1; + } - bool copy = false; - S32 range = indices_shift + face.mNumVertices; + // Copy relevant indices and vertices + for (S32 i = 0; i < new_indices; ++i) + { + U32 idx = output_indices[i]; - // todo: we should not copy more than U16_MAX items per face - if (idx1 >= indices_shift && idx1 < range) + if ((i % 3) == 0) { - buffer_indices[indices_copied] = idx1 - indices_shift; - copy = true; + copy_triangle = idx >= indices_idx_shift && idx < range; } - if (copy) + + if (copy_triangle) { - if (idx2 >= indices_shift && idx2 < range) + if (idx >= U16_MAX) { - buffer_indices[indices_copied + 1] = idx2 - indices_shift; - } - else - { - // Extend size of face's positions list - buffer_indices[indices_copied + 1] = (U16)position_buffer_known_size; - buffer_positions[position_buffer_known_size] = combined_positions[idx2]; - buffer_normals[position_buffer_known_size] = combined_normals[idx2]; - buffer_tex_coords[position_buffer_known_size] = combined_tex_coords[idx2]; - position_buffer_known_size++; - - LL_WARNS_ONCE() << "Merged triangle into different face" << LL_ENDL; + // Shouldn't happen due to simplification? + // Todo: add a fallback? + LL_ERRS() << "Over triangle limit" << LL_ENDL; + break; } - if (idx3 < face.mNumVertices) + if (old_to_new_positions_map[idx] == -1) { - buffer_indices[indices_copied + 2] = idx3 - indices_shift; + // 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; + 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 { - // Extend size of face's positions list - buffer_indices[indices_copied + 2] = position_buffer_known_size; - buffer_positions[position_buffer_known_size] = combined_positions[idx3]; - buffer_normals[position_buffer_known_size] = combined_normals[idx3]; - buffer_tex_coords[position_buffer_known_size] = combined_tex_coords[idx3]; - position_buffer_known_size++; - - LL_WARNS_ONCE() << "Merged triangle into different face" << LL_ENDL; + // existing position + buffer_indices[buf_indices_copied] = (U16)old_to_new_positions_map[idx]; } + buf_indices_copied++; } - - if (copy) - { - indices_copied += 3; - } - } LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); //new_face = face; //temp - if (indices_copied == 0) + if (buf_indices_copied < 3) { // face was optimized away new_face.resizeIndices(3); @@ -2022,24 +2014,25 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d } else { - new_face.resizeIndices(indices_copied); - new_face.resizeVertices(position_buffer_known_size); + new_face.resizeIndices(buf_indices_copied); + new_face.resizeVertices(buf_positions_copied); - S32 idx_size = (indices_copied * sizeof(U16) + 0xF) & ~0xF; + 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, position_buffer_known_size * sizeof(LLVector4a)); - LLVector4a::memcpyNonAliased16((F32*)new_face.mNormals, (F32*)buffer_normals, position_buffer_known_size * sizeof(LLVector4a)); + 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 = (position_buffer_known_size * sizeof(LLVector2) + 0xF)&~0xF; + U32 tex_size = (buf_positions_copied * sizeof(LLVector2) + 0xF)&~0xF; LLVector4a::memcpyNonAliased16((F32*)new_face.mTexCoords, (F32*)buffer_tex_coords, tex_size); new_face.optimize(); } - indices_shift += face.mNumVertices; + 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); -- cgit v1.2.3 From d64b1bded9a0ff05d90f00d72b031a8c04715af7 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 27 Jul 2021 20:14:39 +0300 Subject: DRTVWR-542 Already optimized --- indra/newview/llmodelpreview.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index f005eb0436..3704cc2ea5 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -2025,8 +2025,6 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d U32 tex_size = (buf_positions_copied * sizeof(LLVector2) + 0xF)&~0xF; LLVector4a::memcpyNonAliased16((F32*)new_face.mTexCoords, (F32*)buffer_tex_coords, tex_size); - - new_face.optimize(); } indices_idx_shift += face.mNumVertices; -- cgit v1.2.3 From 1531a31cd9907e5294df5cedbda2b9d1e3adb68b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 28 Jul 2021 21:25:18 +0300 Subject: DRTVWR-542 Rename simplification methods in UI and add a fallback --- indra/newview/llmodelpreview.cpp | 56 +++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 26 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') 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 -- cgit v1.2.3 From 7235d333ea24388fc13a6d01dbafc707b658a0d4 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 4 Aug 2021 00:15:49 +0300 Subject: DRTVWR-542 Fix incorect dropbox state --- indra/newview/llmodelpreview.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 96eb9340f5..11a536473c 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1842,6 +1842,8 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d S32 model_meshopt_mode = meshopt_mode; + // Ideally this should run not per model, + // but combine all submodels with origin model as well if (model_meshopt_mode == MESH_OPTIMIZER_COMBINE) { // Figure out buffer size @@ -2758,7 +2760,7 @@ void LLModelPreview::updateLodControls(S32 lod) S32 lod_mode = lod_combo->getCurrentIndex(); if (lod_mode == LOD_FROM_FILE) // LoD from file { - fmp->mLODMode[lod] = 0; + fmp->mLODMode[lod] = LOD_FROM_FILE; for (U32 i = 0; i < num_file_controls; ++i) { mFMP->childSetVisible(file_controls[i] + lod_name[lod], true); @@ -2771,7 +2773,7 @@ void LLModelPreview::updateLodControls(S32 lod) } else if (lod_mode == USE_LOD_ABOVE) // use LoD above { - fmp->mLODMode[lod] = 2; + fmp->mLODMode[lod] = USE_LOD_ABOVE; for (U32 i = 0; i < num_file_controls; ++i) { mFMP->childSetVisible(file_controls[i] + lod_name[lod], false); @@ -2797,7 +2799,7 @@ void LLModelPreview::updateLodControls(S32 lod) } else // auto generate, the default case for all LoDs except High { - fmp->mLODMode[lod] = 1; + fmp->mLODMode[lod] = MESH_OPTIMIZER; //don't actually regenerate lod when refreshing UI mLODFrozen = true; -- cgit v1.2.3 From 1a1793244002effe46cedf63180de60f4bc69a9a Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 4 Aug 2021 21:14:00 +0300 Subject: DRTVWR-542 Automated method selection Normally simplification methods apply for whole upload, but this one selects methods per model or per face. --- indra/newview/llmodelpreview.cpp | 631 +++++++++++++++++++++------------------ 1 file changed, 344 insertions(+), 287 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') 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)) { -- cgit v1.2.3 From 17ce44e24af77708ddd53b1f38447f5e8ed03786 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 6 Aug 2021 09:46:11 +0300 Subject: DRTVWR-542 Automated method selection #2 --- indra/newview/llmodelpreview.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 3a8676d7b9..3ee435fb5f 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -2151,7 +2151,15 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // 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); + F32 res = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); + if (res < 0) + { + // U16 vertices overflow, shouldn't happen, but just in case + for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) + { + genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); + } + } } if (model_meshopt_mode == MESH_OPTIMIZER) @@ -2175,12 +2183,23 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d if (model_meshopt_mode == MESH_OPTIMIZER_AUTO) { F32 allowed_ratio_drift = 2.f; - S32 res = 0; + F32 res = 0; if (base->mHasGeneratedFaces) { res = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); - if (res * allowed_ratio_drift < indices_decimator) + if (res < 0) + { + // U16 vertices overflow, shouldn't happen, but just in case + for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) + { + genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); + } + LL_INFOS() << "Model " << target_model->getName() + << " lod " << which_lod + << " per model method overflow, defaulting to per face." << LL_ENDL; + } + else if (res * allowed_ratio_drift < indices_decimator) { res = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true); LL_INFOS() << "Model " << target_model->getName() -- cgit v1.2.3 From 3772249c2f1fd101e06c1be8c1601b58fd6f04ba Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 2 Sep 2021 21:55:06 +0300 Subject: SL-15756 Falback in case no triangles were generated --- indra/newview/llmodelpreview.cpp | 60 ++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 11 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 3ee435fb5f..e54045d811 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1708,6 +1708,7 @@ 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 +// returns -1 in case of failure F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_decimator, F32 error_threshold, bool sloppy) { // Figure out buffer size @@ -1911,8 +1912,23 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe ll_aligned_free_32(buffer_indices); ll_aligned_free_32(combined_indices); - if (new_indices <= 0) + if (new_indices < 3) { + // Model should have at least one visible triangle + + if (!sloppy) + { + // Should only happen with sloppy + // non sloppy shouldn't be capable of optimizing mesh away + LL_WARNS() << "Failed to generate triangles" + << " model " << target_model->mLabel + << " target Indices: " << target_indices + << " new Indices: " << new_indices + << " original count: " << size_indices + << " error treshold: " << error_threshold + << LL_ENDL; + } + return -1; } @@ -1963,7 +1979,7 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target new_face = face; - if (new_indices == 0) + if (new_indices < 3) { if (!sloppy) { @@ -1999,8 +2015,9 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target ll_aligned_free_16(output); - if (new_indices <= 0) + if (new_indices < 3) { + // At least one triangle is needed return -1; } @@ -2183,12 +2200,12 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d if (model_meshopt_mode == MESH_OPTIMIZER_AUTO) { F32 allowed_ratio_drift = 2.f; - F32 res = 0; + F32 res_ratio = 0; if (base->mHasGeneratedFaces) { - res = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); + res_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); - if (res < 0) + if (res_ratio < 0) { // U16 vertices overflow, shouldn't happen, but just in case for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) @@ -2199,12 +2216,22 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d << " lod " << which_lod << " per model method overflow, defaulting to per face." << LL_ENDL; } - else if (res * allowed_ratio_drift < indices_decimator) + else if (res_ratio * allowed_ratio_drift < indices_decimator) { - res = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true); + // Try sloppy variant if normal one failed to simplify model enough. + res_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true); LL_INFOS() << "Model " << target_model->getName() << " lod " << which_lod << " sloppily simplified using per model method." << LL_ENDL; + + + if (res_ratio < 0) + { + // Sloppy variant failed to generate triangles. + // Can happen with models that are too simple as is. + // Fallback to normal method. + genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); + } } else { @@ -2217,13 +2244,24 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d { for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - res = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); + res_ratio = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); - if (res * allowed_ratio_drift < indices_decimator) + if (res_ratio * allowed_ratio_drift < indices_decimator) { - res = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, true); + // normal method failed to sufficiently simplify, try sloppy + res_ratio = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, true); + if (res_ratio < 0) + { + // Sloppy failed to generate triangles. + // Can happen with models that are too simple as is. + // Fallback to normal method. + genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); + } } } + LL_INFOS() << "Model " << target_model->getName() + << " lod " << which_lod + << " simplified using per face methods." << LL_ENDL; } } -- cgit v1.2.3 From bac30c9ba5fc9f947788b4fbb468d3259a20fc2e Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 3 Sep 2021 21:15:00 +0300 Subject: SL-15940 Limit wasn't adjusting for a model with less than 30 triangles --- indra/newview/llmodelpreview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index e54045d811..bf661d39b2 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -2945,7 +2945,7 @@ void LLModelPreview::updateLodControls(S32 lod) threshold->setVisible(false); limit->setMaxValue(mMaxTriangleLimit); - limit->setIncrement(mMaxTriangleLimit / 32); + limit->setIncrement(llmax((U32)1, mMaxTriangleLimit / 32)); } else { -- cgit v1.2.3 From b45c0e3ed926270e100271f33885b8d31085a858 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 6 Sep 2021 21:29:51 +0300 Subject: SL-15940 Remove ability to set zero triangle limit Setting lod to zero triangles doesn't work and shouldn't work, so UI shouldn't allow setting less then one triangle per model as well. --- indra/newview/llmodelpreview.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index bf661d39b2..b17aa960ce 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -194,6 +194,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) mPreviewLOD = 0; mModelLoader = NULL; mMaxTriangleLimit = 0; + mMinTriangleLimit = 0; mDirty = false; mGenLOD = false; mLoading = false; @@ -1508,6 +1509,7 @@ void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri } mMaxTriangleLimit = base_triangle_count; + mMinTriangleLimit = mBaseModel.size(); for (S32 lod = start; lod >= end; --lod) { @@ -1541,7 +1543,7 @@ void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri U32 actual_verts = 0; U32 submeshes = 0; - mRequestedTriangleCount[lod] = (S32)((F32)triangle_count / triangle_ratio); + mRequestedTriangleCount[lod] = llmax(mMinTriangleLimit, (S32)((F32)triangle_count / triangle_ratio)); mRequestedErrorThreshold[lod] = lod_error_threshold; glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode); @@ -2100,6 +2102,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d } mMaxTriangleLimit = base_triangle_count; + mMinTriangleLimit = mBaseModel.size(); // TODO: Glod regenerates vertex buffer at this stage // check why, it might be needed to regenerate buffer as well @@ -2133,7 +2136,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d } } - mRequestedTriangleCount[lod] = triangle_limit; + mRequestedTriangleCount[lod] = llmax(mMinTriangleLimit, (S32)triangle_limit); mRequestedErrorThreshold[lod] = lod_error_threshold; mRequestedLoDMode[lod] = lod_mode; @@ -2431,6 +2434,7 @@ void LLModelPreview::updateStatusMessages() if (mMaxTriangleLimit == 0) { mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH]; + mMinTriangleLimit = mUploadData.size(); } mHasDegenerate = false; @@ -2933,6 +2937,7 @@ void LLModelPreview::updateLodControls(S32 lod) LLSpinCtrl* limit = mFMP->getChild("lod_triangle_limit_" + lod_name[lod]); limit->setMaxValue(mMaxTriangleLimit); + limit->setMinValue(mMinTriangleLimit); limit->forceSetValue(mRequestedTriangleCount[lod]); threshold->forceSetValue(mRequestedErrorThreshold[lod]); @@ -2945,6 +2950,7 @@ void LLModelPreview::updateLodControls(S32 lod) threshold->setVisible(false); limit->setMaxValue(mMaxTriangleLimit); + limit->setMinValue(mMinTriangleLimit); limit->setIncrement(llmax((U32)1, mMaxTriangleLimit / 32)); } else -- cgit v1.2.3 From a448846ca3bcaf73da428c5ef28d3f130bf128fc Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 21 Oct 2021 18:46:06 +0300 Subject: SL-16226 Crash at genMeshOptimizerPerModel --- indra/newview/llmodelpreview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index b17aa960ce..4f759446f1 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1911,7 +1911,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe 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_16(buffer_indices); ll_aligned_free_32(combined_indices); if (new_indices < 3) -- cgit v1.2.3 From 33f52ee51dad85802d124e987aa6ee9b7faf0cb2 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 10 Nov 2021 17:48:55 +0200 Subject: SL-14403 Removed glod --- indra/newview/llmodelpreview.cpp | 438 --------------------------------------- 1 file changed, 438 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 4f759446f1..d629358355 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -67,7 +67,6 @@ #include "lltabcontainer.h" #include "lltextbox.h" -#include "glod/glod.h" #include bool LLModelPreview::sIgnoreLoadedCallback = false; @@ -90,19 +89,6 @@ static const F32 PREVIEW_ZOOM_LIMIT(10.f); const F32 SKIN_WEIGHT_CAMERA_DISTANCE = 16.f; -BOOL stop_gloderror() -{ - GLuint error = glodGetError(); - - if (error != GLOD_NO_ERROR) - { - LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL; - return TRUE; - } - - return FALSE; -} - LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& material) { LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap(), FTT_DEFAULT, TRUE, LLGLTexture::BOOST_PREVIEW); @@ -202,10 +188,6 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) mLoadState = LLModelLoader::STARTING; mGroup = 0; mLODFrozen = false; - mBuildShareTolerance = 0.f; - mBuildQueueMode = GLOD_QUEUE_GREEDY; - mBuildBorderMode = GLOD_BORDER_UNLOCK; - mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE; for (U32 i = 0; i < LLModel::NUM_LODS; ++i) { @@ -213,10 +195,6 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) mRequestedCreaseAngle[i] = -1.f; mRequestedLoDMode[i] = 0; mRequestedErrorThreshold[i] = 0.f; - mRequestedBuildOperator[i] = 0; - mRequestedQueueMode[i] = 0; - mRequestedBorderMode[i] = 0; - mRequestedShareTolerance[i] = 0.f; } mViewOption["show_textures"] = false; @@ -226,23 +204,11 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) mHasPivot = false; mModelPivot = LLVector3(0.0f, 0.0f, 0.0f); - glodInit(); - createPreviewAvatar(); } LLModelPreview::~LLModelPreview() { - // glod apparently has internal mem alignment issues that are angering - // the heap-check code in windows, these should be hunted down in that - // TP code, if possible - // - // kernel32.dll!HeapFree() + 0x14 bytes - // msvcr100.dll!free(void * pBlock) Line 51 C - // glod.dll!glodGetGroupParameteriv() + 0x119 bytes - // glod.dll!glodShutdown() + 0x77 bytes - // - //glodShutdown(); if (mModelLoader) { mModelLoader->shutdown(); @@ -828,11 +794,6 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable mLODFile[lod] = filename; - if (lod == LLModel::LOD_HIGH) - { - clearGLODGroup(); - } - std::map joint_alias_map; getJointAliases(joint_alias_map); @@ -933,7 +894,6 @@ void LLModelPreview::clearIncompatible(S32 lod) if (i == LLModel::LOD_HIGH) { mBaseModel = mModel[lod]; - clearGLODGroup(); mBaseScene = mScene[lod]; mVertexBuffer[5].clear(); } @@ -942,23 +902,6 @@ void LLModelPreview::clearIncompatible(S32 lod) } } -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(); @@ -1110,7 +1053,6 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod) } mBaseModel = mModel[loaded_lod]; - clearGLODGroup(); mBaseScene = mScene[loaded_lod]; mVertexBuffer[5].clear(); @@ -1341,373 +1283,6 @@ void LLModelPreview::restoreNormals() updateStatusMessages(); } -void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit) -{ - LL_INFOS() << "Generating lod " << which_lod << " using glod" << LL_ENDL; - // 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(); - - bool no_ff = LLGLSLShader::sNoFixedFunction; - LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; - LLGLSLShader::sNoFixedFunction = false; - - 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 = LIMIT_TRIANGLES; - - 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 == LIMIT_TRIANGLES) - { - 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) - { - glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f); - } - 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; - mMinTriangleLimit = mBaseModel.size(); - - 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] = llmax(mMinTriangleLimit, (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); - glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, (U8*)buff->getIndicesPointer()); - 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()); - memset((U8*)buff->getIndicesPointer(), 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]))) - { - LL_ERRS() << "Invalid face generated during LOD generation." << 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; - } - } - } - } - } - } - - mResourceCost = calcResourceCost(); - - LLVertexBuffer::unbind(); - LLGLSLShader::sNoFixedFunction = no_ff; - if (shader) - { - shader->bind(); - } -} - // 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 @@ -2104,9 +1679,6 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d mMaxTriangleLimit = base_triangle_count; mMinTriangleLimit = mBaseModel.size(); - // 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; @@ -3573,7 +3145,6 @@ BOOL LLModelPreview::render() { genBuffers(-1, skin_weight); //genBuffers(3); - //genGlodLODs(); } if (!mModel[mPreviewLOD].empty()) @@ -4175,15 +3746,6 @@ bool LLModelPreview::lodQueryCallback() return true; } -void LLModelPreview::onLODGenerateParamCommit(S32 lod, bool enforce_tri_limit) -{ - if (!mLODFrozen) - { - genGlodLODs(lod, 3, enforce_tri_limit); - refresh(); - } -} - void LLModelPreview::onLODMeshOptimizerParamCommit(S32 requested_lod, bool enforce_tri_limit, S32 mode) { if (!mLODFrozen) -- cgit v1.2.3 From 3641541c6cc7f33f0e0dc2e1eb2cfdfcec23322b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 15 Nov 2021 23:23:09 +0200 Subject: SL-15756 Removed mHasGeneratedFaces mHasGeneratedFaces is always true for some types of models and glod was treating faces as one mesh by default, so meshoptimizer should do the same regardles of mHasGeneratedFaces --- indra/newview/llmodelpreview.cpp | 73 +++++++++++++--------------------------- 1 file changed, 23 insertions(+), 50 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index d629358355..f6edbf6bc6 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1775,68 +1775,41 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d if (model_meshopt_mode == MESH_OPTIMIZER_AUTO) { F32 allowed_ratio_drift = 2.f; - F32 res_ratio = 0; - if (base->mHasGeneratedFaces) - { - res_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); + F32 res_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); - if (res_ratio < 0) + if (res_ratio < 0) + { + // U16 vertices overflow, shouldn't happen, but just in case + for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - // U16 vertices overflow, shouldn't happen, but just in case - for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) - { - genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); - } - LL_INFOS() << "Model " << target_model->getName() - << " lod " << which_lod - << " per model method overflow, defaulting to per face." << LL_ENDL; + genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); } - else if (res_ratio * allowed_ratio_drift < indices_decimator) - { - // Try sloppy variant if normal one failed to simplify model enough. - res_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true); - LL_INFOS() << "Model " << target_model->getName() - << " lod " << which_lod - << " sloppily simplified using per model method." << LL_ENDL; + LL_INFOS() << "Model " << target_model->getName() + << " lod " << which_lod + << " per model method overflow, defaulting to per face." << LL_ENDL; + } + else if (res_ratio * allowed_ratio_drift < indices_decimator) + { + // Try sloppy variant if normal one failed to simplify model enough. + res_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true); + LL_INFOS() << "Model " << target_model->getName() + << " lod " << which_lod + << " sloppily simplified using per model method." << LL_ENDL; - if (res_ratio < 0) - { - // Sloppy variant failed to generate triangles. - // Can happen with models that are too simple as is. - // Fallback to normal method. - genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); - } - } - else + if (res_ratio < 0) { - LL_INFOS() << "Model " << target_model->getName() - << " lod " << which_lod - << " simplified using per model method." << LL_ENDL; + // Sloppy variant failed to generate triangles. + // Can happen with models that are too simple as is. + // Fallback to normal method. + genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); } } else { - for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) - { - res_ratio = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); - - if (res_ratio * allowed_ratio_drift < indices_decimator) - { - // normal method failed to sufficiently simplify, try sloppy - res_ratio = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, true); - if (res_ratio < 0) - { - // Sloppy failed to generate triangles. - // Can happen with models that are too simple as is. - // Fallback to normal method. - genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); - } - } - } LL_INFOS() << "Model " << target_model->getName() << " lod " << which_lod - << " simplified using per face methods." << LL_ENDL; + << " simplified using per model method." << LL_ENDL; } } -- cgit v1.2.3 From cba1daaf6be4dfd031cd10d9aea07c7124d32726 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 17 Nov 2021 21:46:43 +0200 Subject: SL-16226 Fix crash with extra large faces --- indra/newview/llmodelpreview.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index f6edbf6bc6..6a827207ae 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1373,7 +1373,8 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe 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 buffer_idx_size = (size_indices * sizeof(U16) + 0xF) & ~0xF; + U16* buffer_indices = (U16*)ll_aligned_malloc_16(buffer_idx_size); S32* old_to_new_positions_map = new S32[size_vertices]; S32 buf_positions_copied = 0; -- cgit v1.2.3