diff --git a/__init__.py b/__init__.py index 61d17cd..1f72d53 100644 --- a/__init__.py +++ b/__init__.py @@ -127,7 +127,7 @@ def add_capped_frustum(bm: bmesh.types.BMesh, # Bone → per-end matrices # --------------------------------------------------------------------------- -def _end_matrices(world_mat: Matrix, bone_matrix: Matrix, bone_length: float): +def _end_matrices(bone_matrix: Matrix, bone_length: float): """ Return (head_mat, tail_mat) — world-space matrices for each bone end. @@ -160,15 +160,44 @@ def _end_matrices(world_mat: Matrix, bone_matrix: Matrix, bone_length: float): def _make(pos_local): local_mat = orient.to_4x4() local_mat.col[3][:3] = pos_local - return world_mat @ local_mat + return local_mat return _make(head_local), _make(tail_local) +SUFFIX = "_envelope_mesh" # --------------------------------------------------------------------------- -# Operator +# Operators # --------------------------------------------------------------------------- +class ARMATURE_OT_release_mesh(bpy.types.Operator): + bl_idname = "armature.release_mesh" + bl_label = "Release Mesh" + bl_description = ( + "Clear the target mesh reference from the panel. " + "Does not delete any objects or data, but allows the next build to create a new mesh." + ) + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + obj = context.active_object + if obj is not None and obj.type == 'ARMATURE': + lookup_name = obj.name + SUFFIX + for child in obj.children: + if child.type == 'MESH' and child.name == lookup_name: + return True + + def execute(self, context): + obj = context.active_object + for child in obj.children: + if child.type == 'MESH' and child.name == obj.name + SUFFIX: + m = child.matrix_world.copy() + child.parent = None + child.matrix_world = m + child.name = child.name.replace(SUFFIX, "_snapshot") + + return {'FINISHED'} class ARMATURE_OT_build_envelope_mesh(bpy.types.Operator): bl_idname = "armature.build_envelope_mesh" bl_label = "Build Envelope Mesh" @@ -192,12 +221,11 @@ class ARMATURE_OT_build_envelope_mesh(bpy.types.Operator): def execute(self, context): arm_obj = context.active_object - world_mat = arm_obj.matrix_world bm = bmesh.new() tables = _Tables(self.segments) def meshify(bone_matrix, bone_length, head_radius, tail_radius, draw_head=True): - head_mat, tail_mat = _end_matrices(world_mat, bone_matrix, bone_length) + head_mat, tail_mat = _end_matrices(bone_matrix, bone_length) if draw_head: add_sphere(bm, head_mat, max(head_radius, 0.001), tables) add_sphere(bm, tail_mat, max(tail_radius, 0.001), tables) @@ -217,8 +245,14 @@ class ARMATURE_OT_build_envelope_mesh(bpy.types.Operator): bone.bone.head_radius, bone.bone.tail_radius, bone.parent is None) - panel_props = context.scene.envelope_mesher - target = panel_props.target + lookup_name = arm_obj.name + SUFFIX + target = None + for child in arm_obj.children: + if child.type == 'MESH' and child.name == lookup_name: + target = child + break + + print(target) if target is not None: # Destructively replace the target's mesh data. @@ -231,30 +265,17 @@ class ARMATURE_OT_build_envelope_mesh(bpy.types.Operator): bpy.data.meshes.remove(old_mesh) else: # Create a new object and hand it back to the panel picker. - new_mesh = bpy.data.meshes.new(arm_obj.name + "_envelope_mesh") + new_mesh = bpy.data.meshes.new(lookup_name) bm.to_mesh(new_mesh) bm.free() new_mesh.update() - result_obj = bpy.data.objects.new(arm_obj.name + "_envelope", new_mesh) + result_obj = bpy.data.objects.new(lookup_name, new_mesh) + result_obj.parent = arm_obj context.collection.objects.link(result_obj) - panel_props.target = result_obj return {'FINISHED'} -# --------------------------------------------------------------------------- -# Panel properties -# --------------------------------------------------------------------------- - -class EnvelopeMesherProperties(bpy.types.PropertyGroup): - target: bpy.props.PointerProperty( - name="Target", - description="Object whose mesh will be replaced on each run. " - "Leave empty to create a new object on first run.", - type=bpy.types.Object, - ) - - # --------------------------------------------------------------------------- # Panel # --------------------------------------------------------------------------- @@ -268,9 +289,6 @@ class VIEW3D_PT_armature_mesher(bpy.types.Panel): def draw(self, context): layout = self.layout - props = context.scene.envelope_mesher - - layout.prop(props, "target") op = layout.operator( ARMATURE_OT_build_envelope_mesh.bl_idname, @@ -279,13 +297,19 @@ class VIEW3D_PT_armature_mesher(bpy.types.Panel): ) op.segments = 16 + op = layout.operator( + ARMATURE_OT_release_mesh.bl_idname, + text="Release Mesh", + icon='OUTLINER_OB_MESH', + ) + # --------------------------------------------------------------------------- # Registration # --------------------------------------------------------------------------- classes = ( - EnvelopeMesherProperties, + ARMATURE_OT_release_mesh, ARMATURE_OT_build_envelope_mesh, VIEW3D_PT_armature_mesher, ) @@ -293,12 +317,8 @@ classes = ( def register(): for cls in classes: bpy.utils.register_class(cls) - bpy.types.Scene.envelope_mesher = bpy.props.PointerProperty( - type=EnvelopeMesherProperties, - ) def unregister(): - del bpy.types.Scene.envelope_mesher for cls in reversed(classes): bpy.utils.unregister_class(cls)