msh release

This commit is contained in:
Seth Trowbridge 2026-02-28 20:27:15 -05:00
parent 209197e4aa
commit 3987cc9ed5

View File

@ -127,7 +127,7 @@ def add_capped_frustum(bm: bmesh.types.BMesh,
# Bone → per-end matrices # 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. 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): def _make(pos_local):
local_mat = orient.to_4x4() local_mat = orient.to_4x4()
local_mat.col[3][:3] = pos_local local_mat.col[3][:3] = pos_local
return world_mat @ local_mat return local_mat
return _make(head_local), _make(tail_local) 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): class ARMATURE_OT_build_envelope_mesh(bpy.types.Operator):
bl_idname = "armature.build_envelope_mesh" bl_idname = "armature.build_envelope_mesh"
bl_label = "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): def execute(self, context):
arm_obj = context.active_object arm_obj = context.active_object
world_mat = arm_obj.matrix_world
bm = bmesh.new() bm = bmesh.new()
tables = _Tables(self.segments) tables = _Tables(self.segments)
def meshify(bone_matrix, bone_length, head_radius, tail_radius, draw_head=True): 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: if draw_head:
add_sphere(bm, head_mat, max(head_radius, 0.001), tables) add_sphere(bm, head_mat, max(head_radius, 0.001), tables)
add_sphere(bm, tail_mat, max(tail_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.bone.head_radius, bone.bone.tail_radius,
bone.parent is None) bone.parent is None)
panel_props = context.scene.envelope_mesher lookup_name = arm_obj.name + SUFFIX
target = panel_props.target 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: if target is not None:
# Destructively replace the target's mesh data. # 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) bpy.data.meshes.remove(old_mesh)
else: else:
# Create a new object and hand it back to the panel picker. # 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.to_mesh(new_mesh)
bm.free() bm.free()
new_mesh.update() 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) context.collection.objects.link(result_obj)
panel_props.target = result_obj
return {'FINISHED'} 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 # Panel
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -268,9 +289,6 @@ class VIEW3D_PT_armature_mesher(bpy.types.Panel):
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
props = context.scene.envelope_mesher
layout.prop(props, "target")
op = layout.operator( op = layout.operator(
ARMATURE_OT_build_envelope_mesh.bl_idname, ARMATURE_OT_build_envelope_mesh.bl_idname,
@ -279,13 +297,19 @@ class VIEW3D_PT_armature_mesher(bpy.types.Panel):
) )
op.segments = 16 op.segments = 16
op = layout.operator(
ARMATURE_OT_release_mesh.bl_idname,
text="Release Mesh",
icon='OUTLINER_OB_MESH',
)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Registration # Registration
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
classes = ( classes = (
EnvelopeMesherProperties, ARMATURE_OT_release_mesh,
ARMATURE_OT_build_envelope_mesh, ARMATURE_OT_build_envelope_mesh,
VIEW3D_PT_armature_mesher, VIEW3D_PT_armature_mesher,
) )
@ -293,12 +317,8 @@ classes = (
def register(): def register():
for cls in classes: for cls in classes:
bpy.utils.register_class(cls) bpy.utils.register_class(cls)
bpy.types.Scene.envelope_mesher = bpy.props.PointerProperty(
type=EnvelopeMesherProperties,
)
def unregister(): def unregister():
del bpy.types.Scene.envelope_mesher
for cls in reversed(classes): for cls in reversed(classes):
bpy.utils.unregister_class(cls) bpy.utils.unregister_class(cls)