From 406a2fde91f9c945f3f1e3ca79877766e61d42e7 Mon Sep 17 00:00:00 2001 From: SebastianWitt Date: Thu, 11 Jan 2024 10:30:04 +1100 Subject: [PATCH] UV: improve performance for lightmap unwrap Replaces this search by representing the 3 edge lengths of each triangle as 3d points and searching with a KD-tree. On a mesh with ~20k Tris the old method runs in 40s while the new method takes 0.23s with a difference of <0.001%. Ref !113720 --- .../startup/bl_operators/uvcalc_lightmap.py | 56 +++++++++++++------ 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/scripts/startup/bl_operators/uvcalc_lightmap.py b/scripts/startup/bl_operators/uvcalc_lightmap.py index b232e48453a..7d49e2408d8 100644 --- a/scripts/startup/bl_operators/uvcalc_lightmap.py +++ b/scripts/startup/bl_operators/uvcalc_lightmap.py @@ -302,28 +302,52 @@ def lightmap_uvpack( tri_lengths = [trylens(f) for f in face_sel if f.loop_total == 3] del trylens - def trilensdiff(t1, t2): - return (abs(t1[1][t1[2][0]] - t2[1][t2[2][0]]) + - abs(t1[1][t1[2][1]] - t2[1][t2[2][1]]) + - abs(t1[1][t1[2][2]] - t2[1][t2[2][2]])) + # To add triangles into the light-map pack triangles are grouped in pairs to fill rectangular areas. + # In the following for each triangle we add the sorted triangle edge lengths (3d point) into a KD-Tree + # then iterate over all triangles and search for pairs of triangles by looking for the closest + # sorted triangle point. + # Additionally clusters of similar/equal triangles are parsed by searching for ranges in a second step. + kd = mathutils.kdtree.KDTree(len(tri_lengths)) + for i, (f, lens, o) in enumerate(tri_lengths): + vector = (lens[o[0]], lens[o[1]], lens[o[2]]) + kd.insert(vector, i) + kd.balance() - while tri_lengths: - tri1 = tri_lengths.pop() + added_ids = [False] * len(tri_lengths) + pairs_added = 0 + tri_equality_threshold = 0.00001 # Add multiple pairs at once that are within this threshold. + for i in range(len(tri_lengths)): + if added_ids[i]: + continue + tri1 = tri_lengths[i] + f1, lens1, lo1 = tri1 - if not tri_lengths: + sorted_l = (lens1[lo1[0]], lens1[lo1[1]], lens1[lo1[2]]) + added_ids[i] = True + vec, nearest, dist = kd.find(sorted_l, filter=lambda idx: not added_ids[idx]) + if not nearest or nearest < 0: pretty_faces.append(prettyface((tri1, None))) break + tri2 = tri_lengths[nearest] + pretty_faces.append(prettyface((tri1, tri2))) + pairs_added = pairs_added + 1 + added_ids[nearest] = True - best_tri_index = -1 - best_tri_diff = 100000000.0 + # Look in threshold proximity to add all similar/equal triangles in one go. + # This code is not necessary but acts as a shortcut (~9% performance improvement). + if dist < tri_equality_threshold: + cluster_tri_ids = [ + idx for _, idx, _ in kd.find_range(sorted_l, tri_equality_threshold) + if not added_ids[idx] + ] - for i, tri2 in enumerate(tri_lengths): - diff = trilensdiff(tri1, tri2) - if diff < best_tri_diff: - best_tri_index = i - best_tri_diff = diff - - pretty_faces.append(prettyface((tri1, tri_lengths.pop(best_tri_index)))) + if len(cluster_tri_ids) > 1: + for ci in range(0, len(cluster_tri_ids) - (len(cluster_tri_ids) % 2), 2): + pretty_faces.append( + prettyface((tri_lengths[cluster_tri_ids[ci]], tri_lengths[cluster_tri_ids[ci + 1]])) + ) + added_ids[cluster_tri_ids[ci]] = added_ids[cluster_tri_ids[ci + 1]] = True + pairs_added = pairs_added + 1 # Get the min, max and total areas max_area = 0.0