Add Lattice Magic
to Addons
#48
@ -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):
|
||||
@ -422,16 +466,7 @@ 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
|
||||
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
|
Loading…
Reference in New Issue
Block a user