Add Lattice Magic
to Addons
#48
26
__init__.py
26
__init__.py
@ -14,23 +14,33 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "Camera Lattice",
|
"name": "Lattice Magic",
|
||||||
"author": "Demeter Dzadik",
|
"author": "Demeter Dzadik",
|
||||||
"version": (1,0),
|
"version": (1,0),
|
||||||
"blender": (2, 90, 0),
|
"blender": (2, 90, 0),
|
||||||
"location": "View3D > Sidebar > Camera Lattice",
|
"location": "View3D > Sidebar > Lattice Magic",
|
||||||
"description": "Create a lattice in the camera view to smear geometry.",
|
"description": "Various Lattice-based tools to smear or adjust geometry.",
|
||||||
"category": "Rigging",
|
"category": "Rigging",
|
||||||
"doc_url": "",
|
"doc_url": "https://gitlab.com/blender/lattice_magic/-/wikis/home",
|
||||||
"tracker_url": "",
|
"tracker_url": "https://gitlab.com/blender/lattice_magic/-/issues/new",
|
||||||
}
|
}
|
||||||
|
|
||||||
from . import camera_lattice
|
from . import camera_lattice
|
||||||
|
from . import tweak_lattice
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
|
modules = [
|
||||||
|
camera_lattice
|
||||||
|
,tweak_lattice
|
||||||
|
]
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
importlib.reload(camera_lattice)
|
for m in modules:
|
||||||
camera_lattice.register()
|
importlib.reload(m)
|
||||||
|
if hasattr(m, 'register'):
|
||||||
|
m.register()
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
camera_lattice.unregister()
|
for m in modules:
|
||||||
|
if hasattr(m, 'unregister'):
|
||||||
|
m.unregister()
|
@ -103,7 +103,7 @@ class LatticeSlot(bpy.types.PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class CAMLAT_OT_Remove(bpy.types.Operator):
|
class CAMLAT_OT_Remove(bpy.types.Operator):
|
||||||
"""Remove Lattice Slot along with its Lattice object, animation and modifiers."""
|
"""Remove Lattice Slot along with its Lattice object, animation and modifiers"""
|
||||||
bl_idname = "lattice.remove_slot"
|
bl_idname = "lattice.remove_slot"
|
||||||
bl_label = "Remove Lattice Slot"
|
bl_label = "Remove Lattice Slot"
|
||||||
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||||
@ -133,6 +133,7 @@ class CAMLAT_OT_Remove(bpy.types.Operator):
|
|||||||
return { 'FINISHED' }
|
return { 'FINISHED' }
|
||||||
|
|
||||||
class CAMLAT_OT_Add(bpy.types.Operator):
|
class CAMLAT_OT_Add(bpy.types.Operator):
|
||||||
|
"""Add a Camera Lattice Slot"""
|
||||||
bl_idname = "lattice.add_slot"
|
bl_idname = "lattice.add_slot"
|
||||||
bl_label = "Add Lattice Slot"
|
bl_label = "Add Lattice Slot"
|
||||||
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||||
@ -152,6 +153,7 @@ class CAMLAT_OT_Add(bpy.types.Operator):
|
|||||||
return { 'FINISHED' }
|
return { 'FINISHED' }
|
||||||
|
|
||||||
class CAMLAT_OT_Move(bpy.types.Operator):
|
class CAMLAT_OT_Move(bpy.types.Operator):
|
||||||
|
"""Move Lattice Slot"""
|
||||||
bl_idname = "lattice.move_slot"
|
bl_idname = "lattice.move_slot"
|
||||||
bl_label = "Move Lattice Slot"
|
bl_label = "Move Lattice Slot"
|
||||||
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||||
@ -188,7 +190,7 @@ class CAMLAT_OT_Move(bpy.types.Operator):
|
|||||||
|
|
||||||
|
|
||||||
class CAMLAT_OT_Generate(bpy.types.Operator):
|
class CAMLAT_OT_Generate(bpy.types.Operator):
|
||||||
"""Generate a lattice to smear the selected collection from the selected camera."""
|
"""Generate a lattice to smear the selected collection from the selected camera"""
|
||||||
bl_idname = "lattice.generate_lattice_for_slot"
|
bl_idname = "lattice.generate_lattice_for_slot"
|
||||||
bl_label = "Generate Lattice"
|
bl_label = "Generate Lattice"
|
||||||
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||||
@ -423,8 +425,8 @@ class CAMLAT_OT_ShapeKey_Add(bpy.types.Operator):
|
|||||||
class CAMLAT_PT_main(bpy.types.Panel):
|
class CAMLAT_PT_main(bpy.types.Panel):
|
||||||
bl_space_type = 'VIEW_3D'
|
bl_space_type = 'VIEW_3D'
|
||||||
bl_region_type = 'UI'
|
bl_region_type = 'UI'
|
||||||
bl_category = 'Camera Lattice'
|
bl_category = 'Lattice Magic'
|
||||||
bl_label = "Lattice Slots"
|
bl_label = "Camera Lattice Slots"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
|
398
tweak_lattice.py
Normal file
398
tweak_lattice.py
Normal file
@ -0,0 +1,398 @@
|
|||||||
|
# Another lattice addon, this time inspired by https://twitter.com/soyposmoderno/status/1307222594047758337
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
# Merge this and Camera Lattice into one addon
|
||||||
|
# Re-organize the code
|
||||||
|
|
||||||
|
# Automagically determining the initial radius based on the selected objects' bounding box would be interesting
|
||||||
|
|
||||||
|
# More things:
|
||||||
|
# Mirror selected positions(along any axis, with pickable left/right up/down front/back preference)
|
||||||
|
# Grow selection
|
||||||
|
|
||||||
|
# Add operators to the UI wherever possible.
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
from bpy.props import FloatProperty, IntVectorProperty
|
||||||
|
from typing import List
|
||||||
|
from mathutils import Vector
|
||||||
|
from rna_prop_ui import rna_idprop_ui_create
|
||||||
|
|
||||||
|
coll_name = 'Tweak Lattices'
|
||||||
|
|
||||||
|
def clamp(val, _min=0, _max=1) -> float or int:
|
||||||
|
if val < _min:
|
||||||
|
return _min
|
||||||
|
if val > _max:
|
||||||
|
return _max
|
||||||
|
return val
|
||||||
|
|
||||||
|
def get_lattice_vertex_index(lattice: bpy.types.Lattice, xyz: List[int], do_clamp=True) -> int:
|
||||||
|
"""Get the index of a lattice vertex based on its position on the XYZ axes."""
|
||||||
|
|
||||||
|
# The lattice vertex indicies start in the -Y, -X, -Z corner,
|
||||||
|
# increase on X+, then moves to the next row on Y+, then moves up on Z+.
|
||||||
|
res_x, res_y, res_z = lattice.points_u, lattice.points_v, lattice.points_w
|
||||||
|
x, y, z = xyz[:]
|
||||||
|
if do_clamp:
|
||||||
|
x = clamp(x, 0, res_x)
|
||||||
|
y = clamp(y, 0, res_y)
|
||||||
|
z = clamp(z, 0, res_z)
|
||||||
|
|
||||||
|
assert x < res_x and y < res_y and z < res_z, "Error: Lattice vertex xyz index out of bounds"
|
||||||
|
|
||||||
|
index = (z * res_y*res_x) + (y * res_x) + x
|
||||||
|
return index
|
||||||
|
|
||||||
|
def get_lattice_vertex_xyz_position(lattice: bpy.types.Lattice, index: int) -> (int, int, int):
|
||||||
|
res_x, res_y, res_z = lattice.points_u, lattice.points_v, lattice.points_w
|
||||||
|
|
||||||
|
x = 0
|
||||||
|
remaining = index
|
||||||
|
z = int(remaining / (res_x*res_y))
|
||||||
|
remaining -= z*(res_x*res_y)
|
||||||
|
y = int(remaining / res_x)
|
||||||
|
remaining -= y*res_x
|
||||||
|
x = remaining # Maybe need to add or subtract 1 here?
|
||||||
|
|
||||||
|
return (x, y, z)
|
||||||
|
|
||||||
|
def get_lattice_point_original_position(lattice: bpy.types.Lattice, index: int) -> Vector:
|
||||||
|
"""Reset a lattice vertex to its original position."""
|
||||||
|
start_vec = Vector((-0.5, -0.5, -0.5))
|
||||||
|
if lattice.points_u == 1:
|
||||||
|
start_vec[0] = 0
|
||||||
|
if lattice.points_v == 1:
|
||||||
|
start_vec[1] = 0
|
||||||
|
if lattice.points_w == 1:
|
||||||
|
start_vec[2] = 0
|
||||||
|
|
||||||
|
unit_u = 1/(lattice.points_u-1)
|
||||||
|
unit_v = 1/(lattice.points_v-1)
|
||||||
|
unit_w = 1/(lattice.points_w-1)
|
||||||
|
|
||||||
|
unit_vec = Vector((unit_u, unit_v, unit_w))
|
||||||
|
xyz_vec = Vector(get_lattice_vertex_xyz_position(lattice, index))
|
||||||
|
|
||||||
|
return start_vec + xyz_vec*unit_vec
|
||||||
|
|
||||||
|
def simple_driver(owner: bpy.types.ID, driver_path: str, target_ob: bpy.types.Object, data_path: str, array_index=-1) -> bpy.types.Driver:
|
||||||
|
if array_index > -1:
|
||||||
|
owner.driver_remove(driver_path, array_index)
|
||||||
|
driver = owner.driver_add(driver_path, array_index).driver
|
||||||
|
else:
|
||||||
|
owner.driver_remove(driver_path)
|
||||||
|
driver = owner.driver_add(driver_path).driver
|
||||||
|
|
||||||
|
driver.expression = 'var'
|
||||||
|
var = driver.variables.new()
|
||||||
|
var.targets[0].id = target_ob
|
||||||
|
var.targets[0].data_path = data_path
|
||||||
|
|
||||||
|
return driver
|
||||||
|
|
||||||
|
def ensure_tweak_lattice_collection(scene):
|
||||||
|
coll = bpy.data.collections.get(coll_name)
|
||||||
|
if not coll:
|
||||||
|
coll = bpy.data.collections.new(coll_name)
|
||||||
|
scene.collection.children.link(coll)
|
||||||
|
|
||||||
|
return coll
|
||||||
|
|
||||||
|
class LATTICE_OT_Reset(bpy.types.Operator):
|
||||||
|
"""Reset selected lattice points to their default position."""
|
||||||
|
bl_idname = "lattice.reset_points"
|
||||||
|
bl_label = "Reset Lattice Points"
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
factor: FloatProperty(name="Factor", min=0, max=1, default=1)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return len(context.selected_objects)>0 and context.mode=='EDIT_LATTICE'
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.use_property_split = True
|
||||||
|
layout.prop(self, 'factor', slider=True)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
for ob in context.selected_objects:
|
||||||
|
if ob.type!='LATTICE':
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Resetting shape key or Basis shape
|
||||||
|
if ob.data.shape_keys:
|
||||||
|
active_index = ob.active_shape_key_index
|
||||||
|
key_blocks = ob.data.shape_keys.key_blocks
|
||||||
|
active_block = key_blocks[active_index]
|
||||||
|
basis_block = key_blocks[0]
|
||||||
|
if active_index > 0:
|
||||||
|
for i, skp in enumerate(active_block.data):
|
||||||
|
point = ob.data.points[i]
|
||||||
|
if not point.select: continue
|
||||||
|
mix = skp.co.lerp(basis_block.data[i].co, self.factor)
|
||||||
|
skp.co = mix
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
for i, skp in enumerate(active_block.data):
|
||||||
|
base = get_lattice_point_original_position(ob.data, i)
|
||||||
|
# Resetting the Basis shape
|
||||||
|
mix = basis_block.data[i].co.lerp(base, self.factor)
|
||||||
|
basis_block.data[i].co = mix
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Otherwise, reset the actual points.
|
||||||
|
for i in range(len(ob.data.points)):
|
||||||
|
point = ob.data.points[i]
|
||||||
|
if not point.select:
|
||||||
|
continue
|
||||||
|
base = get_lattice_point_original_position(ob.data, i)
|
||||||
|
mix = point.co_deform.lerp(base, self.factor)
|
||||||
|
point.co_deform = base
|
||||||
|
|
||||||
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class TWEAKLAT_OT_Create(bpy.types.Operator):
|
||||||
|
"""Create a lattice setup at the 3D cursor 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.5)
|
||||||
|
resolution: IntVectorProperty(name="Resolution", default=(12, 12, 12), min=6, max=64)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
for ob in context.selected_objects:
|
||||||
|
if ob.type=='MESH':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
# Ensure a collection to organize all our objects in.
|
||||||
|
coll = ensure_tweak_lattice_collection(context.scene)
|
||||||
|
|
||||||
|
# Create a lattice object at the 3D cursor.
|
||||||
|
lattice_name = "LTC-Tweak"
|
||||||
|
lattice = bpy.data.lattices.new(lattice_name)
|
||||||
|
lattice_ob = bpy.data.objects.new(lattice_name, lattice)
|
||||||
|
coll.objects.link(lattice_ob)
|
||||||
|
lattice_ob.location = context.scene.cursor.location
|
||||||
|
lattice_ob.hide_viewport = True
|
||||||
|
|
||||||
|
# Set resolution
|
||||||
|
lattice.points_u, lattice.points_v, lattice.points_w, = self.resolution
|
||||||
|
lattice.points_u = clamp(lattice.points_u, 6, 64)
|
||||||
|
lattice.points_v = clamp(lattice.points_v, 6, 64)
|
||||||
|
lattice.points_w = clamp(lattice.points_w, 6, 64)
|
||||||
|
|
||||||
|
# Create a vertex group.
|
||||||
|
vg = lattice_ob.vertex_groups.new(name="Hook")
|
||||||
|
|
||||||
|
indices = []
|
||||||
|
for x in range(lattice.points_u-4):
|
||||||
|
for y in range(lattice.points_v-4):
|
||||||
|
for z in range(lattice.points_w-4):
|
||||||
|
indices.append( get_lattice_vertex_index(lattice, (x+2, y+2, z+2)) )
|
||||||
|
|
||||||
|
# Assign weights to the group.
|
||||||
|
vg.add(indices, 1, 'REPLACE')
|
||||||
|
|
||||||
|
# Create an Empty at the 3D cursor
|
||||||
|
hook_name = "Hook_"+lattice_ob.name
|
||||||
|
hook = bpy.data.objects.new(hook_name, None)
|
||||||
|
hook.empty_display_type = 'SPHERE'
|
||||||
|
hook.empty_display_size = 0.5
|
||||||
|
hook.location = context.scene.cursor.location
|
||||||
|
coll.objects.link(hook)
|
||||||
|
|
||||||
|
# Create some custom properties
|
||||||
|
hook['Lattice'] = lattice_ob
|
||||||
|
|
||||||
|
rna_idprop_ui_create(
|
||||||
|
hook, "Tweak Lattice", default = 1.0,
|
||||||
|
min = 0, max = 1,
|
||||||
|
description = "Influence of this lattice on all of its target objects",
|
||||||
|
)
|
||||||
|
rna_idprop_ui_create(
|
||||||
|
hook, "Radius", default = self.radius,
|
||||||
|
min = 0, soft_max = 1, max=100,
|
||||||
|
description = "Size of the influenced area",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a Root Empty to parent both the hook and the lattice to.
|
||||||
|
# This will allow pressing Ctrl+G/R/S on the hook to reset its transforms.
|
||||||
|
root_name = "Root_" + hook.name
|
||||||
|
root = bpy.data.objects.new(root_name, None)
|
||||||
|
root.empty_display_type = 'CUBE'
|
||||||
|
root.empty_display_size = 0.5
|
||||||
|
root.location = context.scene.cursor.location
|
||||||
|
coll.objects.link(root)
|
||||||
|
root.hide_viewport = True
|
||||||
|
hook['Root'] = root
|
||||||
|
|
||||||
|
# Parent lattice and hook to root
|
||||||
|
lattice_ob.parent = root
|
||||||
|
lattice_ob.matrix_parent_inverse = root.matrix_world.inverted()
|
||||||
|
lattice_ob.location = Vector()
|
||||||
|
|
||||||
|
hook.parent = root
|
||||||
|
hook.matrix_parent_inverse = root.matrix_world.inverted()
|
||||||
|
hook.location = Vector()
|
||||||
|
|
||||||
|
# Add Hook modifier to the lattice
|
||||||
|
hook_mod = lattice_ob.modifiers.new(name="Hook", type='HOOK')
|
||||||
|
hook_mod.object = hook
|
||||||
|
hook_mod.vertex_group = vg.name
|
||||||
|
|
||||||
|
# Add Lattice modifier to the selected objects
|
||||||
|
for i, o in enumerate(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.
|
||||||
|
trans_con = hook.constraints.new(type='TRANSFORM')
|
||||||
|
trans_con.target = root
|
||||||
|
trans_con.map_to = 'SCALE'
|
||||||
|
trans_con.mix_mode_scale = 'MULTIPLY'
|
||||||
|
for prop in ['to_min_x_scale', 'to_min_y_scale', 'to_min_z_scale']:
|
||||||
|
simple_driver(trans_con, prop, hook, '["Radius"]')
|
||||||
|
|
||||||
|
for i in range(3):
|
||||||
|
simple_driver(lattice_ob, 'scale', hook, '["Radius"]', i)
|
||||||
|
|
||||||
|
root_drv = simple_driver(root, 'empty_display_size', hook, '["Radius"]')
|
||||||
|
root_drv.expression = 'var/2'
|
||||||
|
|
||||||
|
# Deselect everything, select the hook and make it active
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
hook.select_set(True)
|
||||||
|
context.view_layer.objects.active = hook
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class TWEAKLAT_OT_Delete(bpy.types.Operator):
|
||||||
|
"""Delete a tweak lattice setup with all its helper objects, drivers, etc."""
|
||||||
|
bl_idname = "lattice.delete_tweak_lattice"
|
||||||
|
bl_label = "Delete Tweak Lattice"
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
ob = context.object
|
||||||
|
return ob.type=='EMPTY' and 'Tweak Lattice' in ob
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
hook = context.object
|
||||||
|
lattice = hook['Lattice']
|
||||||
|
root = hook['Root']
|
||||||
|
|
||||||
|
# Remove Lattice modifiers and their drivers.
|
||||||
|
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:
|
||||||
|
m.driver_remove('strength')
|
||||||
|
ob.modifiers.remove(m)
|
||||||
|
break
|
||||||
|
ob_count += 1
|
||||||
|
ob_prop_name = "object_"+str(ob_count)
|
||||||
|
|
||||||
|
# Remove hook Action if exists.
|
||||||
|
if hook.animation_data and hook.animation_data.action:
|
||||||
|
bpy.data.actions.remove(hook.animation_data.action)
|
||||||
|
|
||||||
|
# Remove objects and Lattice datablock.
|
||||||
|
bpy.data.objects.remove(hook)
|
||||||
|
lattice_data = lattice.data
|
||||||
|
bpy.data.objects.remove(lattice)
|
||||||
|
bpy.data.lattices.remove(lattice_data)
|
||||||
|
bpy.data.objects.remove(root)
|
||||||
|
|
||||||
|
# Remove the collection if it's empty.
|
||||||
|
coll = bpy.data.collections.get(coll_name)
|
||||||
|
if coll and len(coll.all_objects)==0:
|
||||||
|
bpy.data.collections.remove(coll)
|
||||||
|
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class TWEAKLAT_PT_main(bpy.types.Panel):
|
||||||
|
bl_space_type = 'VIEW_3D'
|
||||||
|
bl_region_type = 'UI'
|
||||||
|
bl_category = 'Lattice Magic'
|
||||||
|
bl_label = "Tweak Lattice"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.object
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.use_property_split = True
|
||||||
|
layout.use_property_decorate = False
|
||||||
|
|
||||||
|
hook = context.object
|
||||||
|
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")
|
||||||
|
layout.prop(hook, '["Radius"]', slider=True)
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.operator(TWEAKLAT_OT_Delete.bl_idname, text='Delete Tweak Lattice', icon='TRASH')
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Helper Objects")
|
||||||
|
lattice_row = layout.row()
|
||||||
|
lattice_row.prop(hook, '["Lattice"]', text="Lattice")
|
||||||
|
lattice_row.prop(hook['Lattice'], 'hide_viewport', text="", emboss=False)
|
||||||
|
|
||||||
|
root_row = layout.row()
|
||||||
|
root_row.prop(hook, '["Root"]', text="Root")
|
||||||
|
root_row.prop(hook['Root'], 'hide_viewport', text="", emboss=False)
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Affected Objects")
|
||||||
|
|
||||||
|
ob_count = 0
|
||||||
|
ob_prop_name = "object_"+str(ob_count)
|
||||||
|
while ob_prop_name in hook:
|
||||||
|
layout.prop(hook, f'["{ob_prop_name}"]', text="")
|
||||||
|
ob_count += 1
|
||||||
|
ob_prop_name = "object_"+str(ob_count)
|
||||||
|
|
||||||
|
classes = [
|
||||||
|
LATTICE_OT_Reset
|
||||||
|
,TWEAKLAT_OT_Create
|
||||||
|
,TWEAKLAT_OT_Delete
|
||||||
|
,TWEAKLAT_PT_main
|
||||||
|
]
|
||||||
|
|
||||||
|
def register():
|
||||||
|
from bpy.utils import register_class
|
||||||
|
for c in classes:
|
||||||
|
register_class(c)
|
||||||
|
|
||||||
|
# bpy.types.MESH_MT_shape_key_context_menu.append(draw_shape_key_reset)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
from bpy.utils import unregister_class
|
||||||
|
for c in reversed(classes):
|
||||||
|
unregister_class(c)
|
||||||
|
|
||||||
|
# bpy.types.MESH_MT_shape_key_context_menu.remove(draw_shape_key_reset)
|
Loading…
Reference in New Issue
Block a user