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:
Vendored
+3
-1
@@ -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"
|
||||
|
||||
@@ -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,7 +228,15 @@ 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:
|
||||
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')
|
||||
@@ -105,19 +246,23 @@ class RemoveDoublesSafely(Operator):
|
||||
|
||||
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]
|
||||
else:
|
||||
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.object.mode_set(mode='OBJECT')
|
||||
mesh["mesh"].select_set(False)
|
||||
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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user