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.
|
# 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
|
|
Loading…
Reference in New Issue
Block a user