diff options
Diffstat (limited to 'indra/newview/llmodelpreview.cpp')
-rw-r--r-- | indra/newview/llmodelpreview.cpp | 295 |
1 files changed, 288 insertions, 7 deletions
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index a9e80ab5da..93186657ce 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,8 +1340,9 @@ 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) { + 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) { @@ -1403,7 +1405,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim 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; @@ -1424,7 +1426,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; @@ -1704,6 +1706,276 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim } } +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 + 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 = 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; + // 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 = llmax(3, llfloor(num_indices * indices_ratio)); // leave at least one triangle + F32 result_code = 0; // how far from original the model is, 1 == 100% + S32 new_indices = 0; + + if (sloppy) + { + 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); + } + + // todo: instead of checking (new_indices <= 0) + // create a dummy triangle if simplifySloppy returns nothing + if (new_indices <= 0) + { + 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; + } + 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); + + // 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 @@ -2957,7 +3229,7 @@ BOOL LLModelPreview::render() { genBuffers(-1, skin_weight); //genBuffers(3); - //genLODs(); + //genGlodLODs(); } if (!mModel[mPreviewLOD].empty()) @@ -3544,7 +3816,7 @@ bool LLModelPreview::lodQueryCallback() { S32 lod = preview->mLodsQuery.back(); preview->mLodsQuery.pop_back(); - preview->genLODs(lod); + preview->genMeshOptimizerLODs(lod); if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH)) { @@ -3559,11 +3831,20 @@ 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) + { + genGlodLODs(lod, 3, enforce_tri_limit); + refresh(); + } +} + +void LLModelPreview::onLODMeshOptimizerParamCommit(S32 requested_lod, bool enforce_tri_limit, bool sloppy) { if (!mLODFrozen) { - genLODs(lod, 3, enforce_tri_limit); + genMeshOptimizerLODs(requested_lod, 3, enforce_tri_limit, sloppy); refresh(); } } |