Pose Shape Keys: Major Update #321

Merged
Demeter Dzadik merged 10 commits from pose-shape-keys-extension into main 2024-07-03 14:50:58 +02:00
3 changed files with 134 additions and 123 deletions
Showing only changes of commit e7cf029f5a - Show all commits

View File

@ -13,14 +13,17 @@ bl_info = {
import importlib
import bpy
from . import ui
from . import pose_key
from . import ui_list
from . import symmetrize_shape_key
from . import prefs
from . import (
props,
ui,
ops,
ui_list,
symmetrize_shape_key,
prefs,
)
# Each module can have register() and unregister() functions and a list of classes to register called "registry".
modules = [prefs, ui, pose_key, ui_list, symmetrize_shape_key]
modules = [props, prefs, ui, ops, ui_list, symmetrize_shape_key]
def register_unregister_modules(modules, register: bool):

View File

@ -1,6 +1,6 @@
import bpy
from bpy.types import PropertyGroup, Object, Operator, Action, ShapeKey, VertexGroup, MeshVertex
from bpy.props import PointerProperty, IntProperty, CollectionProperty, StringProperty, BoolProperty
from bpy.types import Object, Operator
from bpy.props import StringProperty
from mathutils import Vector
from math import sqrt
from .symmetrize_shape_key import mirror_mesh
@ -29,99 +29,6 @@ DEFORM_MODIFIERS = [
GOOD_MODIFIERS = ['ARMATURE']
class PoseShapeKeyTarget(PropertyGroup):
def update_name(self, context):
if self.block_name_update:
return
obj = context.object
if not obj.data.shape_keys:
return
sk = obj.data.shape_keys.key_blocks.get(self.shape_key_name)
if sk:
sk.name = self.name
self.shape_key_name = self.name
def update_shape_key_name(self, context):
self.block_name_update = True
self.name = self.shape_key_name
self.block_name_update = False
name: StringProperty(
name="Shape Key Target",
description="Name of this shape key target. Should stay in sync with the displayed name and the shape key name, unless the shape key is renamed outside of our UI",
update=update_name,
)
mirror_x: BoolProperty(
name="Mirror X",
description="Mirror the shape key on the X axis when applying the stored shape to this shape key",
default=False,
)
block_name_update: BoolProperty(
description="Flag to help keep shape key names in sync", default=False
)
shape_key_name: StringProperty(
name="Shape Key",
description="Name of the shape key to push data to",
update=update_shape_key_name,
)
@property
def key_block(self) -> list[ShapeKey]:
mesh = self.id_data
if not mesh.shape_keys:
return
return mesh.shape_keys.key_blocks.get(self.name)
class PoseShapeKey(PropertyGroup):
target_shapes: CollectionProperty(type=PoseShapeKeyTarget)
def update_active_sk_index(self, context):
obj = context.object
if not obj.data.shape_keys:
return
try:
sk_name = self.target_shapes[self.active_target_shape_index].shape_key_name
except IndexError:
obj.active_shape_key_index = len(obj.data.shape_keys.key_blocks) - 1
return
key_block_idx = obj.data.shape_keys.key_blocks.find(sk_name)
if key_block_idx > -1:
obj.active_shape_key_index = key_block_idx
# If in weight paint mode and there is a mask vertex group,
# also set that vertex group as active.
if context.mode == 'PAINT_WEIGHT':
key_block = obj.data.shape_keys.key_blocks[key_block_idx]
vg_idx = obj.vertex_groups.find(key_block.vertex_group)
if vg_idx > -1:
obj.vertex_groups.active_index = vg_idx
active_target_shape_index: IntProperty(update=update_active_sk_index)
def update_name(self, context):
if self.name == "":
self.name = "Pose Key"
name: StringProperty(name="Name", update=update_name)
action: PointerProperty(
name="Action",
type=Action,
description="Action that contains the frame that should be used when applying the stored shape as a shape key",
)
frame: IntProperty(
name="Frame",
description="Frame that should be used within the selected action when applying the stored shape as a shape key",
default=0,
)
storage_object: PointerProperty(
type=Object,
name="Storage Object",
description="Specify an object that stores the vertex position data",
)
def get_deforming_armature(mesh_ob: Object) -> Object:
for mod in mesh_ob.modifiers:
@ -956,8 +863,6 @@ class OBJECT_OT_pose_key_copy_data(Operator):
registry = [
PoseShapeKeyTarget,
PoseShapeKey,
OBJECT_OT_pose_key_add,
OBJECT_OT_pose_key_save,
OBJECT_OT_pose_key_set_pose,
@ -969,23 +874,3 @@ registry = [
OBJECT_OT_pose_key_jump_to_shape,
OBJECT_OT_pose_key_copy_data,
]
def update_posekey_index(self, context):
# Want to piggyback on update_active_sk_index() to also update the active
# shape key index when switching pose keys.
mesh = context.object.data
if mesh.pose_keys:
pk = mesh.pose_keys[mesh.active_pose_key_index]
# We just want to fire the update func.
pk.active_target_shape_index = pk.active_target_shape_index
def register():
bpy.types.Mesh.pose_keys = CollectionProperty(type=PoseShapeKey)
bpy.types.Mesh.active_pose_key_index = IntProperty(update=update_posekey_index)
def unregister():
del bpy.types.Mesh.pose_keys
del bpy.types.Mesh.active_pose_key_index

View File

@ -0,0 +1,123 @@
import bpy
from bpy.types import PropertyGroup, Object, Action, ShapeKey
from bpy.props import PointerProperty, IntProperty, CollectionProperty, StringProperty, BoolProperty
class PoseShapeKeyTarget(PropertyGroup):
def update_name(self, context):
if self.block_name_update:
return
obj = context.object
if not obj.data.shape_keys:
return
sk = obj.data.shape_keys.key_blocks.get(self.shape_key_name)
if sk:
sk.name = self.name
self.shape_key_name = self.name
def update_shape_key_name(self, context):
self.block_name_update = True
self.name = self.shape_key_name
self.block_name_update = False
name: StringProperty(
name="Shape Key Target",
description="Name of this shape key target. Should stay in sync with the displayed name and the shape key name, unless the shape key is renamed outside of our UI",
update=update_name,
)
mirror_x: BoolProperty(
name="Mirror X",
description="Mirror the shape key on the X axis when applying the stored shape to this shape key",
default=False,
)
block_name_update: BoolProperty(
description="Flag to help keep shape key names in sync", default=False
)
shape_key_name: StringProperty(
name="Shape Key",
description="Name of the shape key to push data to",
update=update_shape_key_name,
)
@property
def key_block(self) -> list[ShapeKey]:
mesh = self.id_data
if not mesh.shape_keys:
return
return mesh.shape_keys.key_blocks.get(self.name)
class PoseShapeKey(PropertyGroup):
target_shapes: CollectionProperty(type=PoseShapeKeyTarget)
def update_active_sk_index(self, context):
obj = context.object
if not obj.data.shape_keys:
return
try:
sk_name = self.target_shapes[self.active_target_shape_index].shape_key_name
except IndexError:
obj.active_shape_key_index = len(obj.data.shape_keys.key_blocks) - 1
return
key_block_idx = obj.data.shape_keys.key_blocks.find(sk_name)
if key_block_idx > -1:
obj.active_shape_key_index = key_block_idx
# If in weight paint mode and there is a mask vertex group,
# also set that vertex group as active.
if context.mode == 'PAINT_WEIGHT':
key_block = obj.data.shape_keys.key_blocks[key_block_idx]
vg_idx = obj.vertex_groups.find(key_block.vertex_group)
if vg_idx > -1:
obj.vertex_groups.active_index = vg_idx
active_target_shape_index: IntProperty(update=update_active_sk_index)
def update_name(self, context):
if self.name == "":
self.name = "Pose Key"
name: StringProperty(name="Name", update=update_name)
action: PointerProperty(
name="Action",
type=Action,
description="Action that contains the frame that should be used when applying the stored shape as a shape key",
)
frame: IntProperty(
name="Frame",
description="Frame that should be used within the selected action when applying the stored shape as a shape key",
default=0,
)
storage_object: PointerProperty(
type=Object,
name="Storage Object",
description="Specify an object that stores the vertex position data",
)
registry = [
PoseShapeKeyTarget,
PoseShapeKey,
]
def update_posekey_index(self, context):
# Want to piggyback on update_active_sk_index() to also update the active
# shape key index when switching pose keys.
mesh = context.object.data
if mesh.pose_keys:
pk = mesh.pose_keys[mesh.active_pose_key_index]
# We just want to fire the update func.
pk.active_target_shape_index = pk.active_target_shape_index
def register():
bpy.types.Mesh.pose_keys = CollectionProperty(type=PoseShapeKey)
bpy.types.Mesh.active_pose_key_index = IntProperty(update=update_posekey_index)
def unregister():
del bpy.types.Mesh.pose_keys
del bpy.types.Mesh.active_pose_key_index