Add Lattice Magic to Addons #48

Merged
Nick Alberelli merged 36 commits from feature/lattice_magic into main 2023-05-17 20:48:52 +02:00
Showing only changes of commit c10e50513c - Show all commits

View File

@ -3,9 +3,6 @@
# This one lets you create an empty hooked up to a Lattice to deform all selected objects.
# A root empty is also created that can be (manually) parented to a rig in order to use this for animation.
# TODO:
# Automagically determining the initial radius based on the selected objects' bounding box would be interesting
import bpy
from bpy.props import FloatProperty, IntVectorProperty, FloatVectorProperty, BoolProperty, PointerProperty, StringProperty, EnumProperty
from typing import List
@ -125,13 +122,32 @@ def remove_objects_from_lattice(hook, objects):
add_objects_to_lattice(hook, new_objs)
class TWEAKLAT_OT_Create(bpy.types.Operator):
"""Create a lattice setup at the 3D cursor to deform selected objects"""
"""Create a lattice setup to deform selected objects"""
bl_idname = "lattice.create_tweak_lattice"
bl_label = "Create Tweak Lattice"
bl_options = {'REGISTER', 'UNDO'}
radius: FloatProperty(name="Radius", default=0.1, min=0, soft_max=0.2)
resolution: IntVectorProperty(name="Resolution", default=(12, 12, 12), min=6, max=64)
resolution: IntVectorProperty(
name="Resolution",
default=(12, 12, 12),
min=6,
max=64
)
# TODO: Add an option to spawn at the location of the parent (object or bone).
location: EnumProperty(name="Location", items=[
('CURSOR', "3D Cursor", "Create at the location and orientation of the 3D cursor.")
,('CENTER', "Center", "Create at the bounding box center of all selected objects.")
])
radius: FloatProperty(
name="Radius",
description="Radius of influence of this lattice. Can be changed later",
default=0.1,
min=0.0001,
max=1000,
soft_max=2
)
parent_bone: StringProperty(name="Bone", description="Bone to use as parent")
@classmethod
def poll(cls, context):
@ -140,6 +156,33 @@ class TWEAKLAT_OT_Create(bpy.types.Operator):
return True
return False
def invoke(self, context, _event):
parent_obj = context.object
for m in parent_obj.modifiers:
if m.type == 'ARMATURE' and m.object:
parent_obj = m.object
context.scene.tweak_lattice_parent_ob = parent_obj
wm = context.window_manager
return wm.invoke_props_dialog(self)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(self, 'location', expand=True)
layout.prop(self, 'radius', slider=True)
layout.separator()
col = layout.column(align=True)
col.prop(context.scene, 'tweak_lattice_parent_ob')
scene = context.scene
if scene.tweak_lattice_parent_ob and scene.tweak_lattice_parent_ob.type=='ARMATURE':
col.prop_search(self, 'parent_bone', scene.tweak_lattice_parent_ob.data, 'bones')
def execute(self, context):
scene = context.scene
@ -191,7 +234,7 @@ class TWEAKLAT_OT_Create(bpy.types.Operator):
root = bpy.data.objects.new(root_name, None)
root.empty_display_type = 'CUBE'
root.empty_display_size = 0.5
if context.scene.tweak_lattice_location=='CENTER':
if self.location == 'CENTER':
meshes = [o for o in context.selected_objects if o.type=='MESH']
root.matrix_world.translation = bounding_box_center_of_objects(meshes)
else:
@ -205,7 +248,7 @@ class TWEAKLAT_OT_Create(bpy.types.Operator):
matrix_backup = root.matrix_world.copy()
root.parent = scene.tweak_lattice_parent_ob
if root.parent and root.parent.type=='ARMATURE':
bone = root.parent.pose.bones.get(scene.tweak_lattice_parent_bone)
bone = root.parent.pose.bones.get(self.parent_bone)
if bone:
root.parent_type = 'BONE'
root.parent_bone = bone.name
@ -236,6 +279,7 @@ class TWEAKLAT_OT_Create(bpy.types.Operator):
hook.select_set(True)
context.view_layer.objects.active = hook
scene.tweak_lattice_parent_ob = None
return {'FINISHED'}
class TWEAKLAT_OT_Falloff(bpy.types.Operator):
@ -421,17 +465,8 @@ class TWEAKLAT_PT_Main(bpy.types.Panel):
hook = context.object
layout = layout.column()
if hook.type!='EMPTY' or 'Tweak Lattice' not in hook:
scene = context.scene
layout.row().prop(scene, 'tweak_lattice_location', expand=True)
layout.prop(scene, 'tweak_lattice_radius', slider=True)
layout.separator()
layout.prop(scene, 'tweak_lattice_parent_ob')
if scene.tweak_lattice_parent_ob and scene.tweak_lattice_parent_ob.type=='ARMATURE':
layout.prop_search(scene, 'tweak_lattice_parent_bone', scene.tweak_lattice_parent_ob.data, 'bones')
layout.separator()
op = layout.operator(TWEAKLAT_OT_Create.bl_idname, icon='OUTLINER_OB_LATTICE')
op.radius = scene.tweak_lattice_radius
if hook.type != 'EMPTY' or 'Tweak Lattice' not in hook:
layout.operator(TWEAKLAT_OT_Create.bl_idname, icon='OUTLINER_OB_LATTICE')
return
layout.prop(hook, '["Tweak Lattice"]', slider=True, text="Influence")
@ -502,22 +537,11 @@ def register():
for c in classes:
register_class(c)
bpy.types.Scene.tweak_lattice_radius = FloatProperty(name="Radius", default=0.1, min=0.0001, max=1000, soft_max=2)
# This Pointer might cause a "deleted" (unlinked) object to stick around in a .blend file,
# but if we don't use a Pointer, then we can't differentiate between linked vs local objects.
bpy.types.Scene.tweak_lattice_parent_ob = PointerProperty(type=bpy.types.Object, name="Parent")
bpy.types.Scene.tweak_lattice_parent_bone = StringProperty(name="Bone")
bpy.types.Scene.tweak_lattice_location = EnumProperty(name="Location", items=[
('CURSOR', "3D Cursor", "Create at the location and orientation of the 3D cursor.")
,('CENTER', "Center", "Create at the bounding box center of all selected objects.")
])
def unregister():
from bpy.utils import unregister_class
for c in reversed(classes):
unregister_class(c)
del bpy.types.Scene.tweak_lattice_radius
del bpy.types.Scene.tweak_lattice_parent_ob
del bpy.types.Scene.tweak_lattice_parent_bone
del bpy.types.Scene.tweak_lattice_location