start of advanced mode

almost done, just need to find out why doubles aren't merging even when perfectly together when using merge to unselected
This commit is contained in:
Onan Chew
2024-08-31 15:29:12 -04:00
parent ae5c72d5d3
commit eff1d9efe2
3 changed files with 170 additions and 23 deletions
+3 -1
View File
@@ -2,7 +2,9 @@
"python.analysis.extraPaths": [
"D:\\SteamLibrary\\steamapps\\common\\Blender\\4.3\\scripts\\addons",
"C:\\Users\\Onan\\AppData\\Roaming\\Blender Foundation\\Blender\\4.3\\extensions\\user_default\\",//C:/Users/Onan/AppData/Roaming/Blender Foundation/Blender/4.0/scripts/addons
"D:\\blender stuff\\blendercodestuff\\4.3"
"D:\\blender stuff\\blendercodestuff\\4.3",
"/Users/frankche/Documents/blendercoding/4.1/",
"/Users/frankche/Library/Application Support/Blender/4.3/extensions/user_default/"
],
"python.analysis.diagnosticSeverityOverrides": {
"reportInvalidTypeForm": "none"
+166 -21
View File
@@ -2,15 +2,18 @@ from ast import Dict
from itertools import count
import bpy
import re
from typing import List, Tuple, Optional, TypedDict
from typing import List, Tuple, Optional, TypedDict, Any
from bpy.types import Material, Operator, Context, Object
from ..core.register import register_wrap
from ..core.common import get_selected_armature, is_valid_armature, select_current_armature, get_all_meshes
from ..functions.translations import t
class meshEntry(TypedDict):
mesh: bpy.types.Object
mesh: Object
shapekeys: list[str]
vertices: int
cur_vertex_pass: int
mesh_shapekeys: dict[str, Object]
@register_wrap
class RemoveDoublesSafely(Operator):
@@ -20,6 +23,7 @@ class RemoveDoublesSafely(Operator):
bl_options = {'REGISTER', 'UNDO'}
objects_to_do: list[meshEntry] = []
merge_distance: bpy.props.FloatProperty(default=0.0001)
advanced: bpy.props.BoolProperty(default=False)
@classmethod
def poll(cls, context: Context) -> bool:
@@ -35,20 +39,49 @@ class RemoveDoublesSafely(Operator):
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
objects: List[Object] = get_all_meshes(context)
self.objects_to_do = []
for mesh in objects:
if mesh.data.name not in [stored_object["mesh"].data.name for stored_object in self.objects_to_do]:
mesh_shapekeys = {"mesh":mesh,"shapekeys":[]}
print("setting up data for object" + mesh.name)
mesh_shapekeys = {"mesh":mesh,"shapekeys":[],"vertices":0,"cur_vertex_pass":0,"mesh_shapekeys":{}}
mesh_data: bpy.types.Mesh = mesh.data
shape: bpy.types.ShapeKey = None
mesh_shapekeys["vertices"] = len(mesh_data.vertices)
bpy.ops.object.mode_set(mode='EDIT')
mesh_data.vertices.foreach_set("select",[False]*len(mesh_data.vertices))
bpy.ops.object.mode_set(mode='OBJECT')
if mesh_data.shape_keys:
for shape in mesh_data.shape_keys.key_blocks:
mesh_shapekeys["shapekeys"].append(shape.name)
if self.advanced:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
context.view_layer.objects.active = mesh
mesh.select_set(True)
mesh.active_shape_key_index = mesh_data.shape_keys.key_blocks.find(shape.name)
bpy.ops.object.duplicate()
newobj = context.view_layer.objects.active
bpy.ops.object.shape_key_move(type='TOP')
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.shape_key_remove(all=True, apply_mix=False)
newobj.name = shape.name+"_object_is_"+mesh.name
mesh_shapekeys["mesh_shapekeys"][shape.name] = newobj
context.view_layer.objects.active = mesh
bpy.ops.object.select_all(action='DESELECT')
print("queued data for "+mesh.name+" is: ")
print(mesh_shapekeys)
self.objects_to_do.append(mesh_shapekeys)
return {'FINISHED'}
def invoke(self, context: Context, event: bpy.types.Event) -> set:
print("starting modal execution of merge doubles safely.")
self.execute(context)
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
@@ -56,7 +89,6 @@ class RemoveDoublesSafely(Operator):
def modify_mesh(self, context: Context, mesh: meshEntry):
mesh["mesh"].select_set(True)
context.view_layer.objects.active = mesh["mesh"]
context.view_layer.objects.active = mesh["mesh"]
mesh_data: bpy.types.Mesh = mesh["mesh"].data
bpy.ops.object.mode_set(mode='EDIT')
@@ -69,19 +101,120 @@ class RemoveDoublesSafely(Operator):
bpy.ops.object.mode_set(mode='OBJECT')
mesh["mesh"].select_set(False)
print("finished shapekey basic.")
def modify_mesh_advanced(self, context: Context, mesh_entry: meshEntry):
final_merged_vertex_group: list[int] = []
for shapekey_name in mesh_entry["shapekeys"]:
mesh = mesh_entry["mesh_shapekeys"][shapekey_name]
#make a copy to do double merge testing on for the current vertex
context.view_layer.objects.active = mesh
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
context.view_layer.objects.active = mesh
mesh_data: bpy.types.Mesh = mesh.data
vertices_original: dict[int,Any] = {}
original_count: int = len(mesh_data.vertices)
mesh.select_set(True)
bpy.ops.object.duplicate()
mesh = context.view_layer.objects.active
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
mesh.select_set(True)
context.view_layer.objects.active = mesh
mesh_data: bpy.types.Mesh = mesh.data
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT')
for index, merged_point in enumerate(mesh_data.vertices):
vertices_original[index] = merged_point.co.xyz
#if point.co.xyz != original_mesh_data.shape_keys.key_blocks[0].points[index].co.xyz:
# mesh_data.vertices[index].select = True
# break
print("vertex indices and their positions.")
print(vertices_original)
print("vertex positions end.")
bpy.ops.object.mode_set(mode='EDIT')
mesh_data.vertices.foreach_set("select",[False]*len(mesh_data.vertices))
bpy.ops.object.mode_set(mode='OBJECT')
select_target_vertex = [False]*len(mesh_data.vertices)
try:
select_target_vertex[mesh_entry["cur_vertex_pass"]] = True
except:
bpy.ops.object.delete() #remove our double merge testing object for this shapekey, since we merged doubles on it, it will be useless.
return True
print("vertex select list:")
print(select_target_vertex)
bpy.ops.object.mode_set(mode='EDIT')
mesh_data.vertices.foreach_set("select",select_target_vertex)
bpy.ops.mesh.remove_doubles(threshold=self.merge_distance, use_unselected=True, use_sharp_edge_from_normals=False)
bpy.ops.object.mode_set(mode='OBJECT')
#this doesn't keep in mind vertices that are exactly in the same place in the shapekey positional wise that also appear right after one another in the array
#Based on my internal thoughts and theories, this will cause problems. I don't know the solution to this, so it's fine for now.
# besides, the chance of this happening should be very very slim, and will require user input to fix. - @989onan
# {
# "1":"0,0,0"
# "2":"0,0,0"
# "3":"1,0,0"
# }
merged_vertices: list[int] = []
for i in range(0,original_count):
if mesh_data.vertices[i+len(merged_vertices)].co.xyz != vertices_original[i]:
merged_vertices.append(i)
#iterate through a copy of final vertex groups to prevent crash. If a vertex was merged before, but didn't merge in this vertex,
# then the vertex shouldn't be merged because it moves away from the vertex we are double merging now (ex: bottom of mouth moving away from top when opening on a shapekey) - @989onan
for merged_point in final_merged_vertex_group[:]:
if merged_point not in merged_vertices:
final_merged_vertex_group.remove(merged_point)
else:
final_merged_vertex_group.append(merged_point)
bpy.ops.object.mode_set(mode='EDIT')
mesh_data.vertices.foreach_set("select",[False]*len(mesh_data.vertices))
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.delete() #remove our double merge testing object for this shapekey, since we merged doubles on it, it will be useless.
context.view_layer.objects.active = mesh_entry["mesh"]
bpy.ops.object.mode_set(mode='EDIT')
original_mesh_data: bpy.types.Mesh = mesh_entry["mesh"].data
select_target_group = [False]*len(mesh_data.vertices)
for vertex_index in final_merged_vertex_group:
select_target_group[vertex_index] = True
original_mesh_data.vertices.foreach_set("select",select_target_group)
bpy.ops.mesh.remove_doubles(threshold=self.merge_distance, use_unselected=False, use_sharp_edge_from_normals=False)
mesh_data.vertices.foreach_set("select",[False]*len(mesh_data.vertices))
bpy.ops.object.mode_set(mode='OBJECT')
print("finished shapekey advanced.")
return not (len(final_merged_vertex_group) > 0)
def modal(self, context: Context, event: bpy.types.Event) -> set:
if len(self.objects_to_do) > 0:
mesh = self.objects_to_do[0]
bpy.ops.object.select_all(action='DESELECT')
mesh: meshEntry = self.objects_to_do[0]
mesh_data: bpy.types.Mesh = mesh["mesh"].data
if len(mesh['shapekeys']) > 0:
if (len(mesh['shapekeys']) > 0) and (not self.advanced):
shapekeyname: str = mesh['shapekeys'].pop(0)
target_shapekey: int = mesh_data.shape_keys.key_blocks.find(shapekeyname)
mesh["mesh"].active_shape_key_index = target_shapekey
print("doing shapekey \""+shapekeyname+"\" on mesh \""+mesh['mesh'].name+"\".")
self.modify_mesh(context, mesh)
elif not (mesh_data.shape_keys):
print("doing mesh with no shapekeys named \""+mesh['mesh'].name+"\".")
mesh["mesh"].select_set(True)
@@ -95,29 +228,41 @@ class RemoveDoublesSafely(Operator):
bpy.ops.object.mode_set(mode='OBJECT')
mesh["mesh"].select_set(False)
self.objects_to_do.pop(0)
elif (not (mesh["cur_vertex_pass"] > mesh["vertices"])) and self.advanced:
print("doing a merge by single vertex index at index "+str(mesh["cur_vertex_pass"]))
if self.modify_mesh_advanced(context, mesh):
mesh["cur_vertex_pass"] = mesh["cur_vertex_pass"]+1
else:
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=self.merge_distance,use_unselected=False)
bpy.ops.object.mode_set(mode='OBJECT')
mesh["mesh"].select_set(False)
self.objects_to_do.pop(0)
if len(self.objects_to_do) > 0:
mesh = self.objects_to_do[0]
print("finishing double merge object.")
if not self.advanced:
mesh["mesh"].select_set(True)
context.view_layer.objects.active = mesh["mesh"]
bpy.ops.object.mode_set(mode='EDIT')
mesh_data.vertices.foreach_set("select",[False]*len(mesh_data.vertices))
bpy.ops.mesh.select_all(action="INVERT")
bpy.ops.mesh.remove_doubles(threshold=self.merge_distance,use_unselected=False)
bpy.ops.object.mode_set(mode='OBJECT')
mesh["mesh"].select_set(False)
else:
mesh["mesh"].select_set(True)
context.view_layer.objects.active = mesh["mesh"]
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
for obj in mesh["mesh_shapekeys"].values():
obj.select_set(True)
bpy.ops.object.delete() #delete all objects that were shapekey types.
self.objects_to_do.pop(0)
else:
self.report({'INFO'}, t("Optimization.remove_doubles_completed"))
print("finishing modal execution of merge doubles safely.")
return {'FINISHED'}
return {'RUNNING_MODAL'}
+1 -1
View File
@@ -33,7 +33,7 @@ class AvatarToolkitToolsPanel(bpy.types.Panel):
row.operator(CreateDigitigradeLegs.bl_idname, text=t("Tools.create_digitigrade_legs.label"), icon='BONE_DATA')
layout.separator()
row = layout.row(align=True)
layout.label(text=t("Tools.separate_by.label"), icon='MESH')
layout.label(text=t("Tools.separate_by.label"), icon='MESH_DATA')
row.operator(SeparateByMaterials.bl_idname, text=t("Tools.separate_by_materials.label"), icon='MATERIAL')
row.operator(SeparateByLooseParts.bl_idname, text=t("Tools.separate_by_loose_parts.label"), icon='OUTLINER_OB_MESH')
row = layout.row(align=True)