diff --git a/.gitignore b/.gitignore index fd20fdd..5aea8c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.pyc +.vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json index e69de29..96df99e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "python.analysis.extraPaths": [ + "D:\\SteamLibrary\\steamapps\\common\\Blender\\4.0\\scripts\\addons", + "C:\\Users\\Onan\\AppData\\Roaming\\Blender Foundation\\Blender\\4.0\\scripts\\addons",//C:/Users/Onan/AppData/Roaming/Blender Foundation/Blender/4.0/scripts/addons + "D:\\blender stuff\\blendercodestuff\\4.0" + ], + "python.analysis.diagnosticSeverityOverrides": { + "reportInvalidTypeForm": "none" + }, + "python.REPL.enableREPLSmartSend": false, +} \ No newline at end of file diff --git a/functions/remove_doubles_safely.py b/functions/remove_doubles_safely.py new file mode 100644 index 0000000..e962df6 --- /dev/null +++ b/functions/remove_doubles_safely.py @@ -0,0 +1,103 @@ +from ast import Dict +from itertools import count +import bpy +import re +from typing import List, Tuple, Optional, TypedDict +from bpy.types import Material, Operator, Context, Object +from ..core.register import register_wrap + + +class meshEntry(TypedDict): + mesh: bpy.types.Object + shapekeys: list[str] + +@register_wrap +class RemoveDoublesSafely(Operator): + bl_idname = "avatar_toolkit.remove_doubles_safely" + bl_label = "Remove Doubles Safely" + bl_description = "Remove Doubles on all meshes, making sure to not fuse things like mouths together." + bl_options = {'REGISTER', 'UNDO'} + objects_to_do: list[meshEntry] = [] + + @classmethod + def poll(cls, context: Context) -> bool: + return context.mode == 'OBJECT' + + def execute(self, context: Context) -> set: + if not bpy.data.objects: + self.report({'INFO'}, "No objects in the scene") + return + + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.select_all(action='DESELECT') + + meshes: List[Object] = [obj for obj in context.view_layer.objects if obj.type == 'MESH'] + + for mesh in meshes: + if mesh.data.name not in [stored_object["mesh"].data.name for stored_object in self.objects_to_do]: + mesh_shapekeys = {"mesh":mesh,"shapekeys":[]} + mesh_data: bpy.types.Mesh = mesh.data + shape: bpy.types.ShapeKey = None + if mesh_data.shape_keys: + for shape in mesh_data.shape_keys.key_blocks: + mesh_shapekeys["shapekeys"].append(shape.name) + self.objects_to_do.append(mesh_shapekeys) + + + return {'FINISHED'} + + def invoke(self, context: Context, event: bpy.types.Event) -> set: + + self.execute(context) + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} + + def modify_mesh(self, context: Context, mesh: meshEntry): + mesh["mesh"].select_set(True) + context.view_layer.objects.active = mesh["mesh"] + bpy.ops.object.mode_set(mode='EDIT') + + + + + + + + + bpy.ops.object.mode_set(mode='OBJECT') + mesh["mesh"].select_set(False) + + + def modal(self, context: Context, event: bpy.types.Event) -> set: + + + + + if len(self.objects_to_do) > 0: + mesh = self.objects_to_do[0] + mesh_data: bpy.types.Mesh = mesh["mesh"].data + if len(mesh['shapekeys']) > 0: + 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+"\".") + self.modify_mesh(context, mesh) + self.objects_to_do.pop(0) + else: + self.objects_to_do.pop(0) + else: + return {'FINISHED'} + + return {'RUNNING_MODAL'} + + + + + + + diff --git a/ui/optimization.py b/ui/optimization.py index 2f6e69a..0de9bae 100644 --- a/ui/optimization.py +++ b/ui/optimization.py @@ -25,6 +25,7 @@ class AvatarToolkitOptimizationPanel(bpy.types.Panel): row.scale_y = 1.2 row.operator("avatar_toolkit.join_all_meshes", text="Join All Meshes") row.operator("avatar_toolkit.join_selected_meshes", text="Join Selected Meshes") + row.operator("avatar_toolkit.remove_doubles_safely", text="Remove Doubles Safely") # Add optimization options here diff --git a/ui/tools.py b/ui/tools.py index 408a95d..2c2ea05 100644 --- a/ui/tools.py +++ b/ui/tools.py @@ -19,4 +19,6 @@ class AvatarToolkitToolsPanel(bpy.types.Panel): row = layout.row(align=True) row.scale_y = 1.5 - row.operator("avatar_toolkit.convert_to_resonite", text="Translate to Resonite") \ No newline at end of file + row.operator("avatar_toolkit.convert_to_resonite", text="Translate to Resonite") + row = layout.row(align=True) + row.operator("avatar_toolkit.remove_doubles_safely", text="Remove Doubles Safely") \ No newline at end of file