SL-17475 Use a shadow indices buffer before simplification
parent
45bcefd981
commit
b1cbf369cf
|
|
@ -4955,7 +4955,7 @@ bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a
|
|||
|
||||
void LLVolumeFace::remap()
|
||||
{
|
||||
// generate a remap buffer
|
||||
// Generate a remap buffer
|
||||
std::vector<unsigned int> remap(mNumIndices);
|
||||
S32 remap_vertices_count = LLMeshOptimizer::generateRemapMultiU16(&remap[0],
|
||||
mIndices,
|
||||
|
|
@ -4966,26 +4966,29 @@ void LLVolumeFace::remap()
|
|||
mNumVertices);
|
||||
|
||||
// Allocate new buffers
|
||||
U16* remap_indices = (U16*)ll_aligned_malloc_16(mNumIndices * sizeof(U16));
|
||||
S32 size = ((mNumIndices * sizeof(U16)) + 0xF) & ~0xF;
|
||||
U16* remap_indices = (U16*)ll_aligned_malloc_16(size);
|
||||
|
||||
S32 tc_bytes_size = ((remap_vertices_count * sizeof(LLVector2)) + 0xF) & ~0xF;
|
||||
LLVector4a* remap_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * remap_vertices_count + tc_bytes_size);
|
||||
LLVector4a* remap_normals = remap_positions + remap_vertices_count;
|
||||
LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + remap_vertices_count);
|
||||
|
||||
// fill the buffers
|
||||
// Fill the buffers
|
||||
LLMeshOptimizer::remapIndexBufferU16(remap_indices, mIndices, mNumIndices, &remap[0]);
|
||||
LLMeshOptimizer::remapPositionsBuffer(remap_positions, mPositions, mNumVertices, &remap[0]);
|
||||
LLMeshOptimizer::remapNormalsBuffer(remap_normals, mNormals, mNumVertices, &remap[0]);
|
||||
LLMeshOptimizer::remapUVBuffer(remap_tex_coords, mTexCoords, mNumVertices, &remap[0]);
|
||||
|
||||
// free unused buffers
|
||||
// Free unused buffers
|
||||
ll_aligned_free_16(mIndices);
|
||||
ll_aligned_free<64>(mPositions);
|
||||
ll_aligned_free_16(mTangents);
|
||||
|
||||
// Tangets are now invalid
|
||||
ll_aligned_free_16(mTangents);
|
||||
mTangents = NULL;
|
||||
|
||||
// Assign new values
|
||||
mIndices = remap_indices;
|
||||
mPositions = remap_positions;
|
||||
mNormals = remap_normals;
|
||||
|
|
|
|||
|
|
@ -184,12 +184,6 @@ size_t LLMeshOptimizer::generateRemapMultiU16(
|
|||
const LLVector2 * text_coords,
|
||||
U64 vertex_count)
|
||||
{
|
||||
meshopt_Stream streams[] = {
|
||||
{(const float*)vertex_positions, sizeof(F32) * 3, sizeof(F32) * 4},
|
||||
{(const float*)normals, sizeof(F32) * 3, sizeof(F32) * 4},
|
||||
{(const float*)text_coords, sizeof(F32) * 2, sizeof(F32) * 2},
|
||||
};
|
||||
|
||||
S32 out_of_range_count = 0;
|
||||
U32* indices_u32 = NULL;
|
||||
if (indices)
|
||||
|
|
@ -199,7 +193,7 @@ size_t LLMeshOptimizer::generateRemapMultiU16(
|
|||
{
|
||||
if (indices[i] < vertex_count)
|
||||
{
|
||||
indices_u32[i] = indices[i];
|
||||
indices_u32[i] = (U32)indices[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -211,14 +205,10 @@ size_t LLMeshOptimizer::generateRemapMultiU16(
|
|||
|
||||
if (out_of_range_count)
|
||||
{
|
||||
LL_WARNS() << out_of_range_count << " indexes are out of range." << LL_ENDL;
|
||||
LL_WARNS() << out_of_range_count << " indices are out of range." << LL_ENDL;
|
||||
}
|
||||
|
||||
// Remap can function without indices,
|
||||
// but providing indices helps with removing unused vertices
|
||||
U64 indeces_cmp = indices_u32 ? index_count : vertex_count;
|
||||
|
||||
size_t unique = meshopt_generateVertexRemapMulti(&remap[0], indices_u32, indeces_cmp, vertex_count, streams, sizeof(streams) / sizeof(streams[0]));
|
||||
size_t unique = generateRemapMultiU32(remap, indices_u32, index_count, vertex_positions, normals, text_coords, vertex_count);
|
||||
|
||||
ll_aligned_free_32(indices_u32);
|
||||
|
||||
|
|
|
|||
|
|
@ -1221,7 +1221,7 @@ void LLModelPreview::restoreNormals()
|
|||
// 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)
|
||||
F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_decimator, F32 error_threshold, eSimplificationMode simplification_mode)
|
||||
{
|
||||
// I. Weld faces together
|
||||
// Figure out buffer size
|
||||
|
|
@ -1258,20 +1258,21 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
|
|||
{
|
||||
const LLVolumeFace &face = base_model->getVolumeFace(face_idx);
|
||||
|
||||
// vertices
|
||||
// Vertices
|
||||
S32 copy_bytes = face.mNumVertices * sizeof(LLVector4a);
|
||||
LLVector4a::memcpyNonAliased16((F32*)(combined_positions + combined_positions_shift), (F32*)face.mPositions, copy_bytes);
|
||||
|
||||
// normals
|
||||
// Normals
|
||||
LLVector4a::memcpyNonAliased16((F32*)(combined_normals + combined_positions_shift), (F32*)face.mNormals, copy_bytes);
|
||||
|
||||
// tex coords
|
||||
// Tex coords
|
||||
copy_bytes = face.mNumVertices * sizeof(LLVector2);
|
||||
memcpy((void*)(combined_tex_coords + combined_positions_shift), (void*)face.mTexCoords, copy_bytes);
|
||||
|
||||
combined_positions_shift += face.mNumVertices;
|
||||
|
||||
// indices, sadly can't do dumb memcpy for indices, need to adjust each value
|
||||
// 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];
|
||||
|
|
@ -1282,38 +1283,42 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
|
|||
indices_idx_shift += face.mNumVertices;
|
||||
}
|
||||
|
||||
// II. Remap.
|
||||
std::vector<unsigned int> remap(size_indices);
|
||||
S32 size_remap_vertices = LLMeshOptimizer::generateRemapMultiU32(&remap[0],
|
||||
combined_indices,
|
||||
size_indices,
|
||||
combined_positions,
|
||||
combined_normals,
|
||||
combined_tex_coords,
|
||||
size_vertices);
|
||||
// II. Generate a shadow buffer if nessesary.
|
||||
// Welds together vertices if possible
|
||||
|
||||
// Allocate new buffers
|
||||
U32* remap_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32));
|
||||
U32* shadow_indices = NULL;
|
||||
// if MESH_OPTIMIZER_FULL, just leave as is, since generateShadowIndexBufferU32
|
||||
// won't do anything new, model was remaped on a per face basis.
|
||||
// Similar for MESH_OPTIMIZER_NO_TOPOLOGY, it's pointless
|
||||
// since 'simplifySloppy' ignores all topology, including normals and uvs.
|
||||
// Note: simplifySloppy can affect UVs significantly.
|
||||
if (simplification_mode == MESH_OPTIMIZER_NO_NORMALS)
|
||||
{
|
||||
// strip normals, reflections should restore relatively correctly
|
||||
shadow_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32));
|
||||
LLMeshOptimizer::generateShadowIndexBufferU32(shadow_indices, combined_indices, size_indices, combined_positions, NULL, combined_tex_coords, size_vertices);
|
||||
}
|
||||
if (simplification_mode == MESH_OPTIMIZER_NO_UVS)
|
||||
{
|
||||
// strip uvs, can heavily affect textures
|
||||
shadow_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32));
|
||||
LLMeshOptimizer::generateShadowIndexBufferU32(shadow_indices, combined_indices, size_indices, combined_positions, NULL, NULL, size_vertices);
|
||||
}
|
||||
|
||||
S32 remap_tc_bytes_size = ((size_remap_vertices * sizeof(LLVector2)) + 0xF) & ~0xF;
|
||||
LLVector4a* remap_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_remap_vertices + remap_tc_bytes_size);
|
||||
LLVector4a* remap_normals = remap_positions + size_remap_vertices;
|
||||
LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + size_remap_vertices);
|
||||
|
||||
// fill the buffers
|
||||
LLMeshOptimizer::remapIndexBufferU32(remap_indices, combined_indices, size_indices, &remap[0]);
|
||||
LLMeshOptimizer::remapPositionsBuffer(remap_positions, combined_positions, size_vertices, &remap[0]);
|
||||
LLMeshOptimizer::remapNormalsBuffer(remap_normals, combined_normals, size_vertices, &remap[0]);
|
||||
LLMeshOptimizer::remapUVBuffer(remap_tex_coords, combined_tex_coords, size_vertices, &remap[0]);
|
||||
|
||||
// free unused buffers
|
||||
ll_aligned_free<64>(combined_positions);
|
||||
ll_aligned_free_32(combined_indices);
|
||||
U32* source_indices = NULL;
|
||||
if (shadow_indices)
|
||||
{
|
||||
source_indices = shadow_indices;
|
||||
}
|
||||
else
|
||||
{
|
||||
source_indices = combined_indices;
|
||||
}
|
||||
|
||||
// III. Simplify
|
||||
S32 target_indices = 0;
|
||||
F32 result_error = 0; // how far from original the model is, 1 == 100%
|
||||
S32 new_indices = 0;
|
||||
S32 size_new_indices = 0;
|
||||
|
||||
if (indices_decimator > 0)
|
||||
{
|
||||
|
|
@ -1324,32 +1329,36 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
|
|||
target_indices = 3;
|
||||
}
|
||||
|
||||
new_indices = LLMeshOptimizer::simplifyU32(
|
||||
size_new_indices = LLMeshOptimizer::simplifyU32(
|
||||
output_indices,
|
||||
remap_indices,
|
||||
source_indices,
|
||||
size_indices,
|
||||
remap_positions,
|
||||
size_remap_vertices,
|
||||
combined_positions,
|
||||
size_vertices,
|
||||
LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX],
|
||||
target_indices,
|
||||
error_threshold,
|
||||
sloppy,
|
||||
simplification_mode == MESH_OPTIMIZER_NO_TOPOLOGY,
|
||||
&result_error);
|
||||
|
||||
if (result_error < 0)
|
||||
{
|
||||
LL_WARNS() << "Negative result error from meshoptimizer for model " << target_model->mLabel
|
||||
<< " target Indices: " << target_indices
|
||||
<< " new Indices: " << new_indices
|
||||
<< " new Indices: " << size_new_indices
|
||||
<< " original count: " << size_indices << LL_ENDL;
|
||||
}
|
||||
|
||||
ll_aligned_free_32(remap_indices);
|
||||
// free unused buffers
|
||||
ll_aligned_free_32(combined_indices);
|
||||
ll_aligned_free_32(shadow_indices);
|
||||
combined_indices = NULL;
|
||||
shadow_indices = NULL;
|
||||
|
||||
if (new_indices < 3)
|
||||
if (size_new_indices < 3)
|
||||
{
|
||||
// Model should have at least one visible triangle
|
||||
ll_aligned_free<64>(remap_positions);
|
||||
ll_aligned_free<64>(combined_positions);
|
||||
ll_aligned_free_32(output_indices);
|
||||
|
||||
return -1;
|
||||
|
|
@ -1357,12 +1366,12 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
|
|||
|
||||
// IV. Repack back into individual faces
|
||||
|
||||
LLVector4a* buffer_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_remap_vertices + tc_bytes_size);
|
||||
LLVector4a* buffer_normals = buffer_positions + size_remap_vertices;
|
||||
LLVector2* buffer_tex_coords = (LLVector2*)(buffer_normals + size_remap_vertices);
|
||||
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 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_remap_vertices];
|
||||
S32* old_to_new_positions_map = new S32[size_vertices];
|
||||
|
||||
S32 buf_positions_copied = 0;
|
||||
S32 buf_indices_copied = 0;
|
||||
|
|
@ -1380,13 +1389,13 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
|
|||
bool copy_triangle = false;
|
||||
S32 range = indices_idx_shift + face.mNumVertices;
|
||||
|
||||
for (S32 i = 0; i < size_remap_vertices; i++)
|
||||
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)
|
||||
for (S32 i = 0; i < size_new_indices; ++i)
|
||||
{
|
||||
U32 idx = output_indices[i];
|
||||
|
||||
|
|
@ -1409,19 +1418,19 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
|
|||
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
|
||||
<< " new Indices: " << size_new_indices
|
||||
<< " original count: " << size_indices
|
||||
<< " error treshold: " << error_threshold
|
||||
<< LL_ENDL;
|
||||
|
||||
// U16 vertices overflow shouldn't happen, but just in case
|
||||
new_indices = 0;
|
||||
size_new_indices = 0;
|
||||
valid_faces = 0;
|
||||
for (U32 face_idx = 0; face_idx < base_model->getNumVolumeFaces(); ++face_idx)
|
||||
{
|
||||
genMeshOptimizerPerFace(base_model, target_model, face_idx, indices_decimator, error_threshold, false);
|
||||
genMeshOptimizerPerFace(base_model, target_model, face_idx, indices_decimator, error_threshold, simplification_mode);
|
||||
const LLVolumeFace &face = target_model->getVolumeFace(face_idx);
|
||||
new_indices += face.mNumIndices;
|
||||
size_new_indices += face.mNumIndices;
|
||||
if (face.mNumIndices >= 3)
|
||||
{
|
||||
valid_faces++;
|
||||
|
|
@ -1429,7 +1438,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
|
|||
}
|
||||
if (valid_faces)
|
||||
{
|
||||
return (F32)size_indices / (F32)new_indices;
|
||||
return (F32)size_indices / (F32)size_new_indices;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1438,9 +1447,9 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
|
|||
}
|
||||
|
||||
// Copy vertice, normals, tcs
|
||||
buffer_positions[buf_positions_copied] = remap_positions[idx];
|
||||
buffer_normals[buf_positions_copied] = remap_normals[idx];
|
||||
buffer_tex_coords[buf_positions_copied] = remap_tex_coords[idx];
|
||||
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;
|
||||
|
||||
|
|
@ -1495,21 +1504,21 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
|
|||
}
|
||||
|
||||
delete[]old_to_new_positions_map;
|
||||
ll_aligned_free<64>(remap_positions);
|
||||
ll_aligned_free<64>(combined_positions);
|
||||
ll_aligned_free<64>(buffer_positions);
|
||||
ll_aligned_free_32(output_indices);
|
||||
ll_aligned_free_16(buffer_indices);
|
||||
|
||||
if (new_indices < 3 || valid_faces == 0)
|
||||
if (size_new_indices < 3 || valid_faces == 0)
|
||||
{
|
||||
// Model should have at least one visible triangle
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (F32)size_indices / (F32)new_indices;
|
||||
return (F32)size_indices / (F32)size_new_indices;
|
||||
}
|
||||
|
||||
F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_decimator, F32 error_threshold, bool sloppy)
|
||||
F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_decimator, F32 error_threshold, eSimplificationMode simplification_mode)
|
||||
{
|
||||
const LLVolumeFace &face = base_model->getVolumeFace(face_idx);
|
||||
S32 size_indices = face.mNumIndices;
|
||||
|
|
@ -1521,9 +1530,36 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
|
|||
S32 size = (size_indices * sizeof(U16) + 0xF) & ~0xF;
|
||||
U16* output_indices = (U16*)ll_aligned_malloc_16(size);
|
||||
|
||||
U16* shadow_indices = NULL;
|
||||
// if MESH_OPTIMIZER_FULL, just leave as is, since generateShadowIndexBufferU32
|
||||
// won't do anything new, model was remaped on a per face basis.
|
||||
// Similar for MESH_OPTIMIZER_NO_TOPOLOGY, it's pointless
|
||||
// since 'simplifySloppy' ignores all topology, including normals and uvs.
|
||||
if (simplification_mode == MESH_OPTIMIZER_NO_NORMALS)
|
||||
{
|
||||
U16* shadow_indices = (U16*)ll_aligned_malloc_16(size);
|
||||
LLMeshOptimizer::generateShadowIndexBufferU16(shadow_indices, face.mIndices, size_indices, face.mPositions, NULL, face.mTexCoords, face.mNumVertices);
|
||||
}
|
||||
if (simplification_mode == MESH_OPTIMIZER_NO_UVS)
|
||||
{
|
||||
U16* shadow_indices = (U16*)ll_aligned_malloc_16(size);
|
||||
LLMeshOptimizer::generateShadowIndexBufferU16(shadow_indices, face.mIndices, size_indices, face.mPositions, NULL, NULL, face.mNumVertices);
|
||||
}
|
||||
// Don't run ShadowIndexBuffer for MESH_OPTIMIZER_NO_TOPOLOGY, it's pointless
|
||||
|
||||
U16* source_indices = NULL;
|
||||
if (shadow_indices)
|
||||
{
|
||||
source_indices = shadow_indices;
|
||||
}
|
||||
else
|
||||
{
|
||||
source_indices = face.mIndices;
|
||||
}
|
||||
|
||||
S32 target_indices = 0;
|
||||
F32 result_error = 0; // how far from original the model is, 1 == 100%
|
||||
S32 new_indices = 0;
|
||||
S32 size_new_indices = 0;
|
||||
|
||||
if (indices_decimator > 0)
|
||||
{
|
||||
|
|
@ -1534,16 +1570,16 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
|
|||
target_indices = 3;
|
||||
}
|
||||
|
||||
new_indices = LLMeshOptimizer::simplify(
|
||||
size_new_indices = LLMeshOptimizer::simplify(
|
||||
output_indices,
|
||||
face.mIndices,
|
||||
source_indices,
|
||||
size_indices,
|
||||
face.mPositions,
|
||||
face.mNumVertices,
|
||||
LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX],
|
||||
target_indices,
|
||||
error_threshold,
|
||||
sloppy,
|
||||
simplification_mode == MESH_OPTIMIZER_NO_TOPOLOGY,
|
||||
&result_error);
|
||||
|
||||
if (result_error < 0)
|
||||
|
|
@ -1551,7 +1587,7 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
|
|||
LL_WARNS() << "Negative result error from meshoptimizer for face " << face_idx
|
||||
<< " of model " << target_model->mLabel
|
||||
<< " target Indices: " << target_indices
|
||||
<< " new Indices: " << new_indices
|
||||
<< " new Indices: " << size_new_indices
|
||||
<< " original count: " << size_indices
|
||||
<< " error treshold: " << error_threshold
|
||||
<< LL_ENDL;
|
||||
|
|
@ -1562,9 +1598,9 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
|
|||
// Copy old values
|
||||
new_face = face;
|
||||
|
||||
if (new_indices < 3)
|
||||
if (size_new_indices < 3)
|
||||
{
|
||||
if (!sloppy)
|
||||
if (simplification_mode != MESH_OPTIMIZER_NO_TOPOLOGY)
|
||||
{
|
||||
// meshopt_optimizeSloppy() can optimize triangles away even if target_indices is > 2,
|
||||
// but optimize() isn't supposed to
|
||||
|
|
@ -1588,23 +1624,24 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
|
|||
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;
|
||||
new_face.resizeIndices(size_new_indices); // will wipe out mIndices, so new_face can't substitute output
|
||||
S32 idx_size = (size_new_indices * sizeof(U16) + 0xF) & ~0xF;
|
||||
LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)output_indices, idx_size);
|
||||
|
||||
// clear unused values
|
||||
// Clear unused values
|
||||
new_face.optimize();
|
||||
}
|
||||
|
||||
ll_aligned_free_16(output_indices);
|
||||
ll_aligned_free_16(shadow_indices);
|
||||
|
||||
if (new_indices < 3)
|
||||
if (size_new_indices < 3)
|
||||
{
|
||||
// At least one triangle is needed
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (F32)size_indices / (F32)new_indices;
|
||||
return (F32)size_indices / (F32)size_new_indices;
|
||||
}
|
||||
|
||||
void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 decimation, bool enforce_tri_limit)
|
||||
|
|
@ -1740,14 +1777,17 @@ 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_PRECISE)
|
||||
{
|
||||
// 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
|
||||
F32 res = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false);
|
||||
if (res < 0)
|
||||
// Run meshoptimizer for each face
|
||||
for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx)
|
||||
{
|
||||
target_model->copyVolumeFaces(base);
|
||||
F32 res = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
|
||||
if (res < 0)
|
||||
{
|
||||
// Mesh optimizer failed and returned an invalid model
|
||||
const LLVolumeFace &face = base->getVolumeFace(face_idx);
|
||||
LLVolumeFace &new_face = target_model->getVolumeFace(face_idx);
|
||||
new_face = face;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1756,19 +1796,29 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
// Run meshoptimizer for each face
|
||||
for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx)
|
||||
{
|
||||
if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, true) < 0)
|
||||
if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY) < 0)
|
||||
{
|
||||
// Sloppy failed and returned an invalid model
|
||||
genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false);
|
||||
genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (model_meshopt_mode == MESH_OPTIMIZER_AUTO)
|
||||
{
|
||||
// Switches between 'combine' method and 'sloppy' based on combine's result.
|
||||
F32 allowed_ratio_drift = 2.f;
|
||||
F32 precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false);
|
||||
// Remove progressively more data if we can't reach the target.
|
||||
F32 allowed_ratio_drift = 1.8f;
|
||||
F32 precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
|
||||
|
||||
if (precise_ratio < 0 || (precise_ratio * allowed_ratio_drift < indices_decimator))
|
||||
{
|
||||
precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_NORMALS);
|
||||
}
|
||||
|
||||
if (precise_ratio < 0 || (precise_ratio * allowed_ratio_drift < indices_decimator))
|
||||
{
|
||||
precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_UVS);
|
||||
}
|
||||
|
||||
if (precise_ratio < 0 || (precise_ratio * allowed_ratio_drift < indices_decimator))
|
||||
{
|
||||
|
|
@ -1776,10 +1826,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
// Sloppy variant can fail entirely and has issues with precision,
|
||||
// so code needs to do multiple attempts with different decimators.
|
||||
// Todo: this is a bit of a mess, needs to be refined and improved
|
||||
|
||||
F32 last_working_decimator = 0.f;
|
||||
F32 last_working_ratio = F32_MAX;
|
||||
|
||||
F32 sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true);
|
||||
F32 sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY);
|
||||
|
||||
if (sloppy_ratio > 0)
|
||||
{
|
||||
|
|
@ -1802,13 +1853,13 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
// side due to overal lack of precision, and we don't need an ideal result, which
|
||||
// likely does not exist, just a better one, so a partial correction is enough.
|
||||
F32 sloppy_decimator = indices_decimator * (indices_decimator / sloppy_ratio + 1) / 2;
|
||||
sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, true);
|
||||
sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY);
|
||||
}
|
||||
|
||||
if (last_working_decimator > 0 && sloppy_ratio < last_working_ratio)
|
||||
{
|
||||
// Compensation didn't work, return back to previous decimator
|
||||
sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true);
|
||||
sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY);
|
||||
}
|
||||
|
||||
if (sloppy_ratio < 0)
|
||||
|
|
@ -1841,7 +1892,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
&& sloppy_decimator > precise_ratio
|
||||
&& sloppy_decimator > 1)// precise_ratio isn't supposed to be below 1, but check just in case
|
||||
{
|
||||
sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, true);
|
||||
sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY);
|
||||
sloppy_decimator = sloppy_decimator / sloppy_decimation_step;
|
||||
}
|
||||
}
|
||||
|
|
@ -1861,7 +1912,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
else
|
||||
{
|
||||
// Fallback to normal method
|
||||
precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false);
|
||||
precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
|
||||
}
|
||||
|
||||
LL_INFOS() << "Model " << target_model->getName()
|
||||
|
|
|
|||
|
|
@ -225,13 +225,21 @@ private:
|
|||
// Count amount of original models, excluding sub-models
|
||||
static U32 countRootModels(LLModelLoader::model_list models);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MESH_OPTIMIZER_FULL,
|
||||
MESH_OPTIMIZER_NO_NORMALS,
|
||||
MESH_OPTIMIZER_NO_UVS,
|
||||
MESH_OPTIMIZER_NO_TOPOLOGY,
|
||||
} eSimplificationMode;
|
||||
|
||||
// Merges faces into single mesh, simplifies using mesh optimizer,
|
||||
// then splits back into faces.
|
||||
// Returns reached simplification ratio. -1 in case of a failure.
|
||||
F32 genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_ratio, F32 error_threshold, bool sloppy);
|
||||
F32 genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_ratio, F32 error_threshold, eSimplificationMode simplification_mode);
|
||||
// Simplifies specified face using mesh optimizer.
|
||||
// Returns reached simplification ratio. -1 in case of a failure.
|
||||
F32 genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_ratio, F32 error_threshold, bool sloppy);
|
||||
F32 genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_ratio, F32 error_threshold, eSimplificationMode simplification_mode);
|
||||
|
||||
protected:
|
||||
friend class LLModelLoader;
|
||||
|
|
|
|||
Loading…
Reference in New Issue