Add Lattice Magic
to Addons
#48
152
tweak_lattice.py
152
tweak_lattice.py
@ -25,7 +25,7 @@ from .utils import clamp, get_lattice_vertex_index, simple_driver
|
|||||||
|
|
||||||
coll_name = 'Tweak Lattices'
|
coll_name = 'Tweak Lattices'
|
||||||
|
|
||||||
def ensure_tweak_lattice_collection(scene):
|
def ensure_tweak_lattice_collection(scene: bpy.types.Scene) -> bpy.types.Collection:
|
||||||
coll = bpy.data.collections.get(coll_name)
|
coll = bpy.data.collections.get(coll_name)
|
||||||
if not coll:
|
if not coll:
|
||||||
coll = bpy.data.collections.new(coll_name)
|
coll = bpy.data.collections.new(coll_name)
|
||||||
@ -33,7 +33,9 @@ def ensure_tweak_lattice_collection(scene):
|
|||||||
|
|
||||||
return coll
|
return coll
|
||||||
|
|
||||||
def ensure_falloff_vgroup(lattice_ob: bpy.types.Object, vg_name="Group", multiplier=1, power=1) -> bpy.types.VertexGroup:
|
def ensure_falloff_vgroup(
|
||||||
|
lattice_ob: bpy.types.Object,
|
||||||
|
vg_name="Group", multiplier=1, power=1) -> bpy.types.VertexGroup:
|
||||||
lattice = lattice_ob.data
|
lattice = lattice_ob.data
|
||||||
res_x, res_y, res_z = lattice.points_u, lattice.points_v, lattice.points_w
|
res_x, res_y, res_z = lattice.points_u, lattice.points_v, lattice.points_w
|
||||||
|
|
||||||
@ -62,6 +64,62 @@ def add_radius_constraint(obj, hook, target):
|
|||||||
simple_driver(trans_con, prop, hook, '["Radius"]')
|
simple_driver(trans_con, prop, hook, '["Radius"]')
|
||||||
return trans_con
|
return trans_con
|
||||||
|
|
||||||
|
def get_objects_of_lattice(hook: bpy.types.Object) -> List[bpy.types.Object]:
|
||||||
|
objs = []
|
||||||
|
ob_count = 0
|
||||||
|
ob_prop_name = "object_"+str(ob_count)
|
||||||
|
while ob_prop_name in hook:
|
||||||
|
objs.append(hook[ob_prop_name])
|
||||||
|
return objs
|
||||||
|
|
||||||
|
def add_objects_to_lattice(
|
||||||
|
hook: bpy.types.Object,
|
||||||
|
objects: List[bpy.types.Object]):
|
||||||
|
lattice_ob = hook['Lattice']
|
||||||
|
|
||||||
|
# Check for existing
|
||||||
|
offset = 0
|
||||||
|
while "object_"+str(offset) in hook:
|
||||||
|
offset += 1
|
||||||
|
|
||||||
|
for i, o in enumerate(objects):
|
||||||
|
o.select_set(False)
|
||||||
|
if o.type!='MESH' or o in hook.values():
|
||||||
|
offset -= 1
|
||||||
|
continue
|
||||||
|
m = o.modifiers.new(name="Tweak Lattice", type='LATTICE')
|
||||||
|
m.object = lattice_ob
|
||||||
|
hook["object_"+str(i+offset)] = o
|
||||||
|
# Add driver to the modifier influence
|
||||||
|
simple_driver(m, 'strength', hook, '["Tweak Lattice"]')
|
||||||
|
|
||||||
|
def remove_all_objects_from_lattice(hook: bpy.types.Object) -> List[bpy.types.Object]:
|
||||||
|
lattice_ob = hook['Lattice']
|
||||||
|
objs = []
|
||||||
|
ob_count = 0
|
||||||
|
ob_prop_name = "object_"+str(ob_count)
|
||||||
|
while ob_prop_name in hook:
|
||||||
|
ob = hook[ob_prop_name]
|
||||||
|
for m in ob.modifiers:
|
||||||
|
if m.type!='LATTICE': continue
|
||||||
|
if m.object == lattice_ob:
|
||||||
|
m.driver_remove('strength')
|
||||||
|
ob.modifiers.remove(m)
|
||||||
|
break
|
||||||
|
ob_count += 1
|
||||||
|
objs.append(ob)
|
||||||
|
del hook[ob_prop_name]
|
||||||
|
ob_prop_name = "object_"+str(ob_count)
|
||||||
|
return objs
|
||||||
|
|
||||||
|
def remove_objects_from_lattice(hook, objects):
|
||||||
|
new_objs = []
|
||||||
|
prev_objs = remove_all_objects_from_lattice(hook)
|
||||||
|
for o in prev_objs:
|
||||||
|
if o not in objects:
|
||||||
|
new_objs.append(o)
|
||||||
|
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 at the 3D cursor to deform selected objects"""
|
||||||
bl_idname = "lattice.create_tweak_lattice"
|
bl_idname = "lattice.create_tweak_lattice"
|
||||||
@ -154,13 +212,7 @@ class TWEAKLAT_OT_Create(bpy.types.Operator):
|
|||||||
hook_mod.vertex_group = vg.name
|
hook_mod.vertex_group = vg.name
|
||||||
|
|
||||||
# Add Lattice modifier to the selected objects
|
# Add Lattice modifier to the selected objects
|
||||||
for i, o in enumerate(context.selected_objects):
|
add_objects_to_lattice(hook, context.selected_objects)
|
||||||
if o.type!='MESH': continue
|
|
||||||
m = o.modifiers.new(name="Tweak Lattice", type='LATTICE')
|
|
||||||
m.object = lattice_ob
|
|
||||||
hook["object_"+str(i)] = o
|
|
||||||
# Add driver to the modifier influence
|
|
||||||
simple_driver(m, 'strength', hook, '["Tweak Lattice"]')
|
|
||||||
|
|
||||||
# Set up Radius control.
|
# Set up Radius control.
|
||||||
add_radius_constraint(hook, hook, root)
|
add_radius_constraint(hook, hook, root)
|
||||||
@ -243,18 +295,7 @@ class TWEAKLAT_OT_Delete(bpy.types.Operator):
|
|||||||
root = hook['Root']
|
root = hook['Root']
|
||||||
|
|
||||||
# Remove Lattice modifiers and their drivers.
|
# Remove Lattice modifiers and their drivers.
|
||||||
ob_count = 0
|
remove_all_objects_from_lattice(hook)
|
||||||
ob_prop_name = "object_"+str(ob_count)
|
|
||||||
while ob_prop_name in hook:
|
|
||||||
ob = hook[ob_prop_name]
|
|
||||||
for m in ob.modifiers:
|
|
||||||
if m.type!='LATTICE': continue
|
|
||||||
if m.object == lattice:
|
|
||||||
m.driver_remove('strength')
|
|
||||||
ob.modifiers.remove(m)
|
|
||||||
break
|
|
||||||
ob_count += 1
|
|
||||||
ob_prop_name = "object_"+str(ob_count)
|
|
||||||
|
|
||||||
# Remove hook Action if exists.
|
# Remove hook Action if exists.
|
||||||
if hook.animation_data and hook.animation_data.action:
|
if hook.animation_data and hook.animation_data.action:
|
||||||
@ -275,6 +316,57 @@ class TWEAKLAT_OT_Delete(bpy.types.Operator):
|
|||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class TWEAKLAT_OT_Add_Objects(bpy.types.Operator):
|
||||||
|
"""Add selected objects to this tweak lattice"""
|
||||||
|
bl_idname = "lattice.add_selected_objects"
|
||||||
|
bl_label = "Add Selected Objects"
|
||||||
|
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
ob = context.object
|
||||||
|
if ob.type!='EMPTY' or 'Tweak Lattice' not in ob: return False
|
||||||
|
values = ob.values()
|
||||||
|
for sel_o in context.selected_objects:
|
||||||
|
if sel_o==ob or sel_o.type!='MESH': continue
|
||||||
|
if sel_o not in values:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
hook = context.object
|
||||||
|
|
||||||
|
# Add Lattice modifier to the selected objects
|
||||||
|
add_objects_to_lattice(hook, context.selected_objects)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class TWEAKLAT_OT_Remove_Objects(bpy.types.Operator):
|
||||||
|
"""Remove selected objects from this tweak lattice"""
|
||||||
|
bl_idname = "lattice.remove_selected_objects"
|
||||||
|
bl_label = "Remove Selected Objects"
|
||||||
|
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
ob = context.object
|
||||||
|
if ob.type!='EMPTY' or 'Tweak Lattice' not in ob: return False
|
||||||
|
values = ob.values()
|
||||||
|
for sel_o in context.selected_objects:
|
||||||
|
if sel_o==ob or sel_o.type!='MESH': continue
|
||||||
|
if sel_o in values:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
hook = context.object
|
||||||
|
|
||||||
|
# Add Lattice modifier to the selected objects
|
||||||
|
remove_objects_from_lattice(hook, context.selected_objects)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class TWEAKLAT_PT_Main(bpy.types.Panel):
|
class TWEAKLAT_PT_Main(bpy.types.Panel):
|
||||||
bl_space_type = 'VIEW_3D'
|
bl_space_type = 'VIEW_3D'
|
||||||
bl_region_type = 'UI'
|
bl_region_type = 'UI'
|
||||||
@ -321,6 +413,22 @@ class TWEAKLAT_PT_Main(bpy.types.Panel):
|
|||||||
root_row.prop(hook, '["Root"]', text="Root")
|
root_row.prop(hook, '["Root"]', text="Root")
|
||||||
root_row.prop(hook['Root'], 'hide_viewport', text="", emboss=False)
|
root_row.prop(hook['Root'], 'hide_viewport', text="", emboss=False)
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Add Objects")
|
||||||
|
for o in context.selected_objects:
|
||||||
|
if o == hook or o.type!='MESH': continue
|
||||||
|
if o in hook.values(): continue
|
||||||
|
layout.label(text=o.name, icon='ADD')
|
||||||
|
layout.operator(TWEAKLAT_OT_Add_Objects.bl_idname, icon='ADD')
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Remove Objects")
|
||||||
|
for o in context.selected_objects:
|
||||||
|
if o == hook or o.type!='MESH': continue
|
||||||
|
if o not in hook.values(): continue
|
||||||
|
layout.label(text=o.name, icon='REMOVE')
|
||||||
|
layout.operator(TWEAKLAT_OT_Remove_Objects.bl_idname, icon='REMOVE')
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.label(text="Affected Objects")
|
layout.label(text="Affected Objects")
|
||||||
|
|
||||||
@ -335,6 +443,8 @@ classes = [
|
|||||||
TWEAKLAT_OT_Create
|
TWEAKLAT_OT_Create
|
||||||
,TWEAKLAT_OT_Delete
|
,TWEAKLAT_OT_Delete
|
||||||
,TWEAKLAT_OT_Falloff
|
,TWEAKLAT_OT_Falloff
|
||||||
|
,TWEAKLAT_OT_Add_Objects
|
||||||
|
,TWEAKLAT_OT_Remove_Objects
|
||||||
,TWEAKLAT_PT_Main
|
,TWEAKLAT_PT_Main
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user