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. # 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. # 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 import bpy
from bpy.props import FloatProperty, IntVectorProperty, FloatVectorProperty, BoolProperty, PointerProperty, StringProperty, EnumProperty from bpy.props import FloatProperty, IntVectorProperty, FloatVectorProperty, BoolProperty, PointerProperty, StringProperty, EnumProperty
from typing import List from typing import List
@ -125,13 +122,32 @@ def remove_objects_from_lattice(hook, objects):
add_objects_to_lattice(hook, new_objs) add_objects_to_lattice(hook, new_objs)
class TWEAKLAT_OT_Create(bpy.types.Operator): 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_idname = "lattice.create_tweak_lattice"
bl_label = "Create Tweak Lattice" bl_label = "Create Tweak Lattice"
bl_options = {'REGISTER', 'UNDO'} bl_options = {'REGISTER', 'UNDO'}
radius: FloatProperty(name="Radius", default=0.1, min=0, soft_max=0.2) resolution: IntVectorProperty(
resolution: IntVectorProperty(name="Resolution", default=(12, 12, 12), min=6, max=64) 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 @classmethod
def poll(cls, context): def poll(cls, context):
@ -140,6 +156,33 @@ class TWEAKLAT_OT_Create(bpy.types.Operator):
return True return True
return False 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): def execute(self, context):
scene = context.scene scene = context.scene
@ -191,7 +234,7 @@ class TWEAKLAT_OT_Create(bpy.types.Operator):
root = bpy.data.objects.new(root_name, None) root = bpy.data.objects.new(root_name, None)
root.empty_display_type = 'CUBE' root.empty_display_type = 'CUBE'
root.empty_display_size = 0.5 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'] meshes = [o for o in context.selected_objects if o.type=='MESH']
root.matrix_world.translation = bounding_box_center_of_objects(meshes) root.matrix_world.translation = bounding_box_center_of_objects(meshes)
else: else:
@ -205,7 +248,7 @@ class TWEAKLAT_OT_Create(bpy.types.Operator):
matrix_backup = root.matrix_world.copy() matrix_backup = root.matrix_world.copy()
root.parent = scene.tweak_lattice_parent_ob root.parent = scene.tweak_lattice_parent_ob
if root.parent and root.parent.type=='ARMATURE': 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: if bone:
root.parent_type = 'BONE' root.parent_type = 'BONE'
root.parent_bone = bone.name root.parent_bone = bone.name
@ -223,7 +266,7 @@ class TWEAKLAT_OT_Create(bpy.types.Operator):
# Add Lattice modifier to the selected objects # Add Lattice modifier to the selected objects
add_objects_to_lattice(hook, context.selected_objects) add_objects_to_lattice(hook, context.selected_objects)
# Set up Radius control. # Set up Radius control.
add_radius_constraint(hook, hook, root) add_radius_constraint(hook, hook, root)
add_radius_constraint(lattice_ob, hook, root) add_radius_constraint(lattice_ob, hook, root)
@ -236,6 +279,7 @@ class TWEAKLAT_OT_Create(bpy.types.Operator):
hook.select_set(True) hook.select_set(True)
context.view_layer.objects.active = hook context.view_layer.objects.active = hook
scene.tweak_lattice_parent_ob = None
return {'FINISHED'} return {'FINISHED'}
class TWEAKLAT_OT_Falloff(bpy.types.Operator): class TWEAKLAT_OT_Falloff(bpy.types.Operator):
@ -421,17 +465,8 @@ class TWEAKLAT_PT_Main(bpy.types.Panel):
hook = context.object hook = context.object
layout = layout.column() layout = layout.column()
if hook.type!='EMPTY' or 'Tweak Lattice' not in hook: if hook.type != 'EMPTY' or 'Tweak Lattice' not in hook:
scene = context.scene layout.operator(TWEAKLAT_OT_Create.bl_idname, icon='OUTLINER_OB_LATTICE')
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
return return
layout.prop(hook, '["Tweak Lattice"]', slider=True, text="Influence") layout.prop(hook, '["Tweak Lattice"]', slider=True, text="Influence")
@ -502,22 +537,11 @@ def register():
for c in classes: for c in classes:
register_class(c) 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_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(): def unregister():
from bpy.utils import unregister_class from bpy.utils import unregister_class
for c in reversed(classes): for c in reversed(classes):
unregister_class(c) 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_ob
del bpy.types.Scene.tweak_lattice_parent_bone
del bpy.types.Scene.tweak_lattice_location