Fix remove doubles perf issue and fix it not working

Why did the merge doubles at point get removed? it was a core component.

Anyways this should fix the performance issue greatly and fix advanced mode literally doing nothing
This commit is contained in:
989onan
2025-01-26 20:01:47 -05:00
parent af311d7d2e
commit fc9b1e42a2
+51 -18
View File
@@ -54,6 +54,28 @@ def process_vertex_merging(mesh_data: bpy.types.Mesh, vertices_original: dict[in
return merged_vertices return merged_vertices
def vertex_moves(mesh_data: bpy.types.Mesh, vertex: int) -> bool:
for shapekey in mesh_data.shape_keys.key_blocks:
data: bpy.types.ShapeKey = shapekey
if data.points[vertex].co.xyz != mesh_data.vertices[vertex].co.xyz:
return True
return False
def merge_vertex_at_index(mesh_data: bpy.types.Mesh, index: int, distance: float):
select_target_vertex = [False]*len(mesh_data.vertices)
select_target_vertex[index] = True
bpy.ops.object.mode_set(mode='OBJECT')
mesh_data.vertices.foreach_set("select",select_target_vertex)
bpy.ops.object.mode_set(mode='EDIT')
for _ in range(0,20): #for some reason, if using merge to unselected on a vertex, the vertex will only merge to 1 other vertex. so we gotta spam it to fix it.
bpy.ops.mesh.remove_doubles(threshold=distance, use_unselected=True, use_sharp_edge_from_normals=False)
bpy.ops.object.mode_set(mode='OBJECT')
class AvatarToolkit_OT_RemoveDoublesAdvanced(Operator): class AvatarToolkit_OT_RemoveDoublesAdvanced(Operator):
bl_idname = "avatar_toolkit.remove_doubles_advanced" bl_idname = "avatar_toolkit.remove_doubles_advanced"
bl_label = t("Optimization.remove_doubles_advanced") bl_label = t("Optimization.remove_doubles_advanced")
@@ -168,7 +190,7 @@ class AvatarToolkit_OT_RemoveDoubles(Operator):
except Exception as e: except Exception as e:
logger.error(f"Error in modify_mesh: {str(e)}") logger.error(f"Error in modify_mesh: {str(e)}")
def modify_mesh_advanced(self, context: Context, mesh_entry: MeshEntry) -> bool: def modify_mesh_advanced(self, context: Context, mesh_entry: MeshEntry) -> int:
"""Advanced mesh modification with shape key handling""" """Advanced mesh modification with shape key handling"""
try: try:
final_merged_vertex_group = [] final_merged_vertex_group = []
@@ -179,26 +201,28 @@ class AvatarToolkit_OT_RemoveDoubles(Operator):
duplicate = create_duplicate_for_merge(context, mesh_entry["mesh"], shapekey_name) duplicate = create_duplicate_for_merge(context, mesh_entry["mesh"], shapekey_name)
vertices_original = {i: v.co.xyz for i, v in enumerate(duplicate.data.vertices)} vertices_original = {i: v.co.xyz for i, v in enumerate(duplicate.data.vertices)}
merge_vertex_at_index(duplicate.data, mesh_entry["cur_vertex_pass"], merge_distance) #merge the vertex at our pass to find vertices that would merge to our vertex at this shapekey.
# Process merging # Process merging
merged_vertices = process_vertex_merging(duplicate.data, vertices_original, mesh_entry["cur_vertex_pass"]) merged_vertices = process_vertex_merging(duplicate.data, vertices_original, mesh_entry["cur_vertex_pass"]) # find what vertices actually merged.
if not initialized_final: if not initialized_final:
final_merged_vertex_group = merged_vertices.copy() final_merged_vertex_group = merged_vertices.copy()
initialized_final = True initialized_final = True
else: else:
final_merged_vertex_group = [v for v in final_merged_vertex_group if v in merged_vertices] final_merged_vertex_group = [v for v in final_merged_vertex_group if v in merged_vertices] # remove vertices that merged from the list if they didn't merge during this shapkey.
bpy.ops.object.delete() bpy.ops.object.delete()
# Apply final merging # Apply final merging
if final_merged_vertex_group: if final_merged_vertex_group:
self.apply_final_merging(context, mesh_entry, final_merged_vertex_group, merge_distance) self.apply_final_merging(context, mesh_entry, final_merged_vertex_group, merge_distance) # merge all vertices that merged on every shapekey no matter the shapekey during the loop.
return not (len(final_merged_vertex_group) > 1) return len(final_merged_vertex_group)
except Exception as e: except Exception as e:
logger.error(f"Error in modify_mesh_advanced: {str(e)}") logger.error(f"Error in modify_mesh_advanced: {str(e)}")
return True return 1
def apply_final_merging(self, context: Context, mesh_entry: MeshEntry, vertex_group: list[int], merge_distance: float) -> None: def apply_final_merging(self, context: Context, mesh_entry: MeshEntry, vertex_group: list[int], merge_distance: float) -> None:
"""Apply final vertex merging operations""" """Apply final vertex merging operations"""
@@ -232,16 +256,14 @@ class AvatarToolkit_OT_RemoveDoubles(Operator):
def finish_mesh_processing(self, context: Context, mesh: MeshEntry, advanced: bool, merge_distance: float) -> None: def finish_mesh_processing(self, context: Context, mesh: MeshEntry, advanced: bool, merge_distance: float) -> None:
"""Complete the mesh processing by performing final merge operations""" """Complete the mesh processing by performing final merge operations"""
logger.debug("Finishing mesh processing") logger.debug("Finishing mesh processing")
mesh["mesh"].select_set(True)
context.view_layer.objects.active = mesh["mesh"]
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action="INVERT")
bpy.ops.mesh.remove_doubles(threshold=merge_distance, use_unselected=False)
if not advanced: bpy.ops.object.mode_set(mode='OBJECT')
mesh["mesh"].select_set(True) mesh["mesh"].select_set(False)
context.view_layer.objects.active = mesh["mesh"]
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action="INVERT")
bpy.ops.mesh.remove_doubles(threshold=merge_distance, use_unselected=False)
bpy.ops.object.mode_set(mode='OBJECT')
mesh["mesh"].select_set(False)
def modal(self, context: Context, event: Event) -> set[ModalReturnType]: def modal(self, context: Context, event: Event) -> set[ModalReturnType]:
"""Modal operator execution""" """Modal operator execution"""
@@ -266,10 +288,21 @@ class AvatarToolkit_OT_RemoveDoubles(Operator):
self.process_simple_mesh(context, mesh, merge_distance) self.process_simple_mesh(context, mesh, merge_distance)
self.objects_to_do.pop(0) self.objects_to_do.pop(0)
elif not (mesh["cur_vertex_pass"] > mesh["vertices"]) and advanced: elif not (mesh["cur_vertex_pass"] > mesh["vertices"]) and advanced: #advanced merging vertex by vertex
if self.modify_mesh_advanced(context, mesh): if(mesh["cur_vertex_pass"] < 0): #make sure it doesn't go below 0 and explode when advancing backwards from a previous step
mesh["cur_vertex_pass"] = 0
if vertex_moves(mesh["mesh"].data, mesh["cur_vertex_pass"]): # do not do advanced merging for vertices that don't move
mesh["cur_vertex_pass"] -= self.modify_mesh_advanced(context, mesh)-2 #advance forward or backwards based on how many vertices actually got merged, changing the list size.
#if above returns 1 (no vertices other than this one being merged to ourselves), advance by 1. else don't advance or go backwards. Makes sure all vertices get merged in the end.
else:
mesh["cur_vertex_pass"] += 1 mesh["cur_vertex_pass"] += 1
elif (mesh["cur_vertex_pass"] > mesh["vertices"]) and advanced and len(mesh['shapekeys']) > 0: #after advanced merging has gone past all the moving vertices, now we need to merge non moving vertices.
shapekeyname = mesh['shapekeys'].pop(0)
mesh["mesh"].active_shape_key_index = mesh_data.shape_keys.key_blocks.find(shapekeyname)
logger.debug(f"Processing shapekey {shapekeyname}")
self.modify_mesh(context, mesh)
else: else:
self.finish_mesh_processing(context, mesh, advanced, merge_distance) self.finish_mesh_processing(context, mesh, advanced, merge_distance)
self.objects_to_do.pop(0) self.objects_to_do.pop(0)