diff --git a/__init__.py b/__init__.py index 1cb186d..61d17cd 100644 --- a/__init__.py +++ b/__init__.py @@ -101,7 +101,6 @@ def add_capped_frustum(bm: bmesh.types.BMesh, hr = [bm.verts.new(p) for p in _apply(head_mat, _ring_positions(head_radius, tables))] tr = [bm.verts.new(p) for p in _apply(tail_mat, _ring_positions(tail_radius, tables))] - # Side quads for i in range(segs): nxt = (i + 1) % segs try: @@ -109,7 +108,6 @@ def add_capped_frustum(bm: bmesh.types.BMesh, except ValueError: pass - # Head cap hcv = bm.verts.new(head_pos) for i in range(segs): try: @@ -117,7 +115,6 @@ def add_capped_frustum(bm: bmesh.types.BMesh, except ValueError: pass - # Tail cap tcv = bm.verts.new(tail_pos) for i in range(segs): try: @@ -148,7 +145,7 @@ def _end_matrices(world_mat: Matrix, bone_matrix: Matrix, bone_length: float): our Z = bone Y (ring normal → along bone) """ bx = bone_matrix.col[0].to_3d() - by = bone_matrix.col[1].to_3d() # along bone + by = bone_matrix.col[1].to_3d() bz = bone_matrix.col[2].to_3d() orient = Matrix(( @@ -177,7 +174,8 @@ class ARMATURE_OT_build_envelope_mesh(bpy.types.Operator): bl_label = "Build Envelope Mesh" bl_description = ( "Convert the selected armature into an envelope-style mesh. " - "Object/Pose mode = posed bones | Edit mode = edit bones." + "If a target object is set in the panel, its mesh is replaced in place. " + "Otherwise a new object is created and assigned as the target." ) bl_options = {'REGISTER', 'UNDO'} @@ -219,17 +217,44 @@ class ARMATURE_OT_build_envelope_mesh(bpy.types.Operator): bone.bone.head_radius, bone.bone.tail_radius, bone.parent is None) - mesh = bpy.data.meshes.new(arm_obj.name + "_envelope_mesh") - bm.to_mesh(mesh) - bm.free() - mesh.update() + panel_props = context.scene.envelope_mesher + target = panel_props.target - result_obj = bpy.data.objects.new(arm_obj.name + "_envelope", mesh) - context.collection.objects.link(result_obj) + if target is not None: + # Destructively replace the target's mesh data. + old_mesh = target.data + new_mesh = bpy.data.meshes.new(old_mesh.name) + bm.to_mesh(new_mesh) + bm.free() + new_mesh.update() + target.data = new_mesh + 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") + bm.to_mesh(new_mesh) + bm.free() + new_mesh.update() + result_obj = bpy.data.objects.new(arm_obj.name + "_envelope", new_mesh) + 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 # --------------------------------------------------------------------------- @@ -243,6 +268,10 @@ 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, text="Build Envelope Mesh", @@ -255,13 +284,21 @@ class VIEW3D_PT_armature_mesher(bpy.types.Panel): # Registration # --------------------------------------------------------------------------- -classes = (ARMATURE_OT_build_envelope_mesh, VIEW3D_PT_armature_mesher) +classes = ( + EnvelopeMesherProperties, + ARMATURE_OT_build_envelope_mesh, + VIEW3D_PT_armature_mesher, +) 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)