Character Animation Goodie: "Whole Character" Builtin Keying Set

This commit introduces a new Keying Set: "Whole Character", which is
specially designed for character animators blocking out their
animation. It should make animating with rigs such as the Sintel rigs
(and other "mainstream" setups, though others may also work with a few
modifications) much easier.

It automatically determines which properties on every bone in the
active rig should be keyframed, avoiding an initial set up step where
properties may be missed, or non-animatable properties are also
needlessly keyframed. To do this, it relies on several rules:
1) All bones in the armature, regardless of visibility status are
considered, so that hiding some layers on some keyframes then
keyframing them later won't create problems with earlier poses
changing
2) Bones starting with certain prefixes, i.e. DEF, MCH, VIS, etc. (the
full list is available in the code for this, and can be/is meant to be
modified by riggers in their own versions as they see fit), so that
some bones on hidden layers which shouldn't be seen by animators are
not keyframed
3) Locked transforms AREN'T keyframed
4) All custom properties ARE keyframed - currently this is the best we
can do, as it's hard to tell if they're needed or not, or even if
they're already driven.
This commit is contained in:
Joshua Leung 2011-01-29 03:01:51 +00:00
parent f11424d644
commit 1439ebb6b1
Notes: blender-bot 2023-09-13 13:07:04 +02:00
Referenced by issue #97886, Keying sets other than Whole Character (or keyframing in general) do not respect channel locks
1 changed files with 126 additions and 0 deletions

View File

@ -190,6 +190,130 @@ class BUILTIN_KSI_Available(bpy.types.KeyingSetInfo):
###############################
# All properties that are likely to get animated in a character rig
class BUILTIN_KSI_WholeCharacter(bpy.types.KeyingSetInfo):
bl_label = "Whole Character"
# these prefixes should be avoided, as they are not really bones
# that animators should be touching (or need to touch)
badBonePrefixes = (
'DEF',
'GEO',
'MCH',
'ORG',
'COR',
'VIS',
# ... more can be added here as you need in your own rigs ...
)
# poll - pose-mode on active object only
def poll(ksi, context):
return ((context.active_object) and (context.active_object.pose) and
(context.active_object.mode == 'POSE'))
# iterator - all bones regardless of selection
def iterator(ksi, context, ks):
for bone in context.active_object.pose.bones:
if not bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
ksi.generate(context, ks, bone)
# generator - all unlocked bone transforms + custom properties
def generate(ksi, context, ks, bone):
# loc, rot, scale - only include unlocked ones
ksi.doLoc(ks, bone)
if bone.rotation_mode in ('QUATERNION', 'AXIS_ANGLE'):
ksi.doRot4d(ks, bone)
else:
ksi.doRot3d(ks, bone)
ksi.doScale(ks, bone)
# custom props?
ksi.doCustomProps(ks, bone)
# ----------------
# helper to add some bone's property to the Keying Set
def addProp(ksi, ks, bone, prop, index=-1, use_groups=True):
# add the property name to the base path
path = path_add_property(bone.path_from_id(), prop)
# add Keying Set entry for this...
id_block = bone.id_data
if use_groups:
ks.paths.add(id_block, path, index, group_method='NAMED', group_name=bone.name)
else:
ks.paths.add(id_block, path, index)
# ----------------
# location properties
def doLoc(ksi, ks, bone):
if bone.lock_location == (False, False, False):
ksi.addProp(ks, bone, "location")
else:
for i in range(3):
if not bone.lock_location[i]:
ksi.addProp(ks, bone, "location", i)
# rotation properties
def doRot4d(ksi, ks, bone):
# rotation mode affects the property used
if bone.rotation_mode == 'QUATERNION':
prop = "rotation_quaternion"
elif bone.rotation_mode == 'AXIS_ANGLE':
prop = "rotation_axis_angle"
# add rotation properties if they will
if bone.lock_rotations_4d:
# can check individually
if (bone.lock_rotation == (False, False, False)) and (bone.lock_rotation_w == False):
ksi.addProp(ks, bone, prop)
else:
if bone.lock_rotation_w == False:
ksi.addProp(ks, bone, prop, 0) # w = 0
for i in range(3):
if not bone.lock_rotation[i]:
ksi.addProp(ks, bone, prop, i+1) # i+1, since here x,y,z = 1,2,3, and w=0
elif True not in bone.lock_rotation:
# if axis-angle rotations get locked as eulers, then it's too messy to allow anything
# other than all open unless we keyframe the whole lot
ksi.addProp(ks, bone, prop)
def doRot3d(ksi, ks, bone):
if bone.lock_rotation == (False, False, False):
ksi.addProp(ks, bone, "rotation_euler")
else:
for i in range(3):
if not bone.lock_rotation[i]:
ksi.addProp(ks, bone, "rotation_euler", i)
# scale properties
def doScale(ksi, ks, bone):
if bone.lock_scale == (0,0,0):
ksi.addProp(ks, bone, "scale")
else:
for i in range(3):
if not bone.lock_scale[i]:
ksi.addProp(ks, bone, "scale", i)
# ----------------
# custom properties
def doCustomProps(ksi, ks, bone):
# go over all custom properties for bone
for prop,val in bone.items():
# ignore special "_RNA_UI" used for UI editing
if prop == "_RNA_UI":
continue
# for now, just add all of 'em
ksi.addProp(ks, bone, '["%s"]' % (prop))
###############################
classes = [
BUILTIN_KSI_Location,
BUILTIN_KSI_Rotation,
@ -199,6 +323,8 @@ classes = [
BUILTIN_KSI_LocScale,
BUILTIN_KSI_LocRotScale,
BUILTIN_KSI_RotScale,
BUILTIN_KSI_WholeCharacter,
BUILTIN_KSI_VisualLoc,
BUILTIN_KSI_VisualRot,