Skip to content

Commit 9a8348c

Browse files
committed
Use vertex colors (if present) as attributes during simplification
When importing meshes with vertex colors, we will now supply them to the simplifier as attributes with weight=1 (in addition to normals). This will guide the simplification to preserve regions where vertex colors change. For this change to take full effect, it is also necessary to respect the full error when selecting LODs; this change does not do that yet, so there are going to still be cases where vertex colors change abruptly during LOD switch in a visible manner.
1 parent 428a762 commit 9a8348c

File tree

1 file changed

+41
-12
lines changed

1 file changed

+41
-12
lines changed

scene/resources/3d/importer_mesh.cpp

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
314314
Vector<Vector2> uv2s = surfaces[i].arrays[RS::ARRAY_TEX_UV2];
315315
Vector<int> bones = surfaces[i].arrays[RS::ARRAY_BONES];
316316
Vector<float> weights = surfaces[i].arrays[RS::ARRAY_WEIGHTS];
317+
Vector<Color> colors = surfaces[i].arrays[RS::ARRAY_COLOR];
317318

318319
unsigned int index_count = indices.size();
319320
unsigned int vertex_count = vertices.size();
@@ -368,6 +369,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
368369
const Vector2 *uvs_ptr = uvs.ptr();
369370
const Vector2 *uv2s_ptr = uv2s.ptr();
370371
const float *tangents_ptr = tangents.ptr();
372+
const Color *colors_ptr = colors.ptr();
371373

372374
for (unsigned int j = 0; j < vertex_count; j++) {
373375
const Vector3 &v = vertices_ptr[j];
@@ -385,7 +387,8 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
385387
bool is_tang_aligned = !tangents_ptr || (tangents_ptr[j * 4 + 3] < 0) == (tangents_ptr[idx.second * 4 + 3] < 0);
386388
ERR_FAIL_INDEX(idx.second, normals.size());
387389
bool is_normals_close = normals[idx.second].dot(n) > normal_merge_threshold;
388-
if (is_uvs_close && is_uv2s_close && is_normals_close && is_tang_aligned) {
390+
bool is_col_close = (!colors_ptr || colors_ptr[j].is_equal_approx(colors_ptr[idx.second]));
391+
if (is_uvs_close && is_uv2s_close && is_normals_close && is_tang_aligned && is_col_close) {
389392
vertex_remap.push_back(idx.first);
390393
merged_normals[idx.first] += normals[idx.second];
391394
merged_normals_counts[idx.first]++;
@@ -424,23 +427,50 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
424427
unsigned int merged_vertex_count = merged_vertices.size();
425428
const Vector3 *merged_vertices_ptr = merged_vertices.ptr();
426429
const int32_t *merged_indices_ptr = merged_indices.ptr();
430+
Vector3 *merged_normals_ptr = merged_normals.ptr();
427431

428432
{
429433
const int *counts_ptr = merged_normals_counts.ptr();
430-
Vector3 *merged_normals_ptrw = merged_normals.ptr();
431434
for (unsigned int j = 0; j < merged_vertex_count; j++) {
432-
merged_normals_ptrw[j] /= counts_ptr[j];
435+
merged_normals_ptr[j] /= counts_ptr[j];
433436
}
434437
}
435438

436-
const float normal_weights[3] = {
437-
// Give some weight to normal preservation, may be worth exposing as an import setting
438-
2.0f, 2.0f, 2.0f
439-
};
440-
441439
Vector<float> merged_vertices_f32 = vector3_to_float32_array(merged_vertices_ptr, merged_vertex_count);
442440
float scale = SurfaceTool::simplify_scale_func(merged_vertices_f32.ptr(), merged_vertex_count, sizeof(float) * 3);
443441

442+
const size_t attrib_count = 6; // 3 for normal + 3 for color (if present)
443+
444+
float attrib_weights[attrib_count] = {};
445+
446+
// Give more weight to normal preservation
447+
attrib_weights[0] = attrib_weights[1] = attrib_weights[2] = 2.0f;
448+
449+
// Give some weight to colors but only if present to avoid redundant computations during simplification
450+
if (colors_ptr) {
451+
attrib_weights[3] = attrib_weights[4] = attrib_weights[5] = 1.0f;
452+
}
453+
454+
LocalVector<float> merged_attribs;
455+
merged_attribs.resize(merged_vertex_count * attrib_count);
456+
float *merged_attribs_ptr = merged_attribs.ptr();
457+
458+
memset(merged_attribs_ptr, 0, merged_attribs.size() * sizeof(float));
459+
460+
for (unsigned int j = 0; j < merged_vertex_count; ++j) {
461+
merged_attribs_ptr[j * attrib_count + 0] = merged_normals_ptr[j].x;
462+
merged_attribs_ptr[j * attrib_count + 1] = merged_normals_ptr[j].y;
463+
merged_attribs_ptr[j * attrib_count + 2] = merged_normals_ptr[j].z;
464+
465+
if (colors_ptr) {
466+
unsigned int rj = vertex_inverse_remap[j];
467+
468+
merged_attribs_ptr[j * attrib_count + 3] = colors_ptr[rj].r;
469+
merged_attribs_ptr[j * attrib_count + 4] = colors_ptr[rj].g;
470+
merged_attribs_ptr[j * attrib_count + 5] = colors_ptr[rj].b;
471+
}
472+
}
473+
444474
unsigned int index_target = 12; // Start with the smallest target, 4 triangles
445475
unsigned int last_index_count = 0;
446476

@@ -451,17 +481,16 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
451481
PackedInt32Array new_indices;
452482
new_indices.resize(index_count);
453483

454-
Vector<float> merged_normals_f32 = vector3_to_float32_array(merged_normals.ptr(), merged_normals.size());
455484
const int simplify_options = SurfaceTool::SIMPLIFY_LOCK_BORDER;
456485

457486
size_t new_index_count = SurfaceTool::simplify_with_attrib_func(
458487
(unsigned int *)new_indices.ptrw(),
459488
(const uint32_t *)merged_indices_ptr, index_count,
460489
merged_vertices_f32.ptr(), merged_vertex_count,
461490
sizeof(float) * 3, // Vertex stride
462-
merged_normals_f32.ptr(),
463-
sizeof(float) * 3, // Attribute stride
464-
normal_weights, 3,
491+
merged_attribs_ptr,
492+
sizeof(float) * attrib_count, // Attribute stride
493+
attrib_weights, attrib_count,
465494
nullptr, // Vertex lock
466495
index_target,
467496
max_mesh_error,

0 commit comments

Comments
 (0)