2
$\begingroup$

I want to access the keyframes for a specific bone in animation_data.action, just so I can reset them to a default value. My main problem is I dont know how to just access a specific bone.

The general purpose of this is to automatically reset the hand positions of bvh animation loaded onto a manuelbastionilab model. The bvh animation loads it in fine, but because there isnt really any hand data, it sets the animation to a default splayed out position. And because it assigns it to the wrong bones, it comes in with a crooked little finger. So I just want a simple python script to reset any animations I load in to a good default.

I think I should be able to get to the animation data for the keyframes with

myaction = bpy.context.active_object.animation_data.action

But I dont know what comes next, I dont know how to access the fcurves for a specific bone like a joint / bone in my little finger called pinky02_L

This seems incredibly basic but Ive spent ages googling it and failed miserably.

I suspect I might have problems with setting the data too ! I am using quaternions and so I want to access say the z rotation quaternion. So how do I access the z rot channel, I think its some array offset for the fcurve or something ? So once I have found the fcurve for pinky02l (pinky02lfcurve = ?), is it something like curve = pinky02lfcurve[6] to access the z rotation channel ? Or have I got it all horribly wrong ?

keyframePoints = curve.keyframe_points
    for keyframe in keyframePoints:

And Once I have a keyframe how do I set it to a value like :

keyframe.value = math.radians(8.0)

. Is it that simple ? I bet its not!

Any answers to any parts of this would be great ! p.s. thank you to batFinger and Scott Milner for answering my question about it setting it to the wrong degrees the other day.

So from the post lemon refers too I if I am understanding it correctly if I use:

for curve in pinky02lfcurve 
       if curve.data_path.endswith('rotation_quaternion') 
        if curve.array_index == 2 # or is it 3 for z with quaternions ?
            keyframePoints = curve.keyframe_points
            for keyframe in keyframePoints:
                keyframe.co[1] = math.radians(8.0)

Ok so re writing to take into account batFingers post I think the following should do what I want !

myaction = bpy.context.active_object.animation_data.action
fcurves = myaction.fcurves
rot = Euler(0,0,8)
myquat = rot.to_quaternion()


pinkyb = bpy.data.objects['MyArmature'].pose.bones['pinky02_L']
dpath = pinkyb.path_from_id("rotation_quaternion")
fc = fcurves.find(dpath, index=0)
keyframePoints = fc.keyframe_points
for keyframe in keyframePoints:
    keyframe.co[1] = myquat.w 

fc = fcurves.find(dpath, index=1)
keyframePoints = fc.keyframe_points
for keyframe in keyframePoints:
    keyframe.co[1] = myquat.x 

fc = fcurves.find(dpath, index=2)
keyframePoints = fc.keyframe_points
for keyframe in keyframePoints:
    keyframe.co[1] = myquat.y 

fc = fcurves.find(dpath, index=3)
keyframePoints = fc.keyframe_points
for keyframe in keyframePoints:
    keyframe.co[1] = myquat.z 

So I hope this should be good apart from the curve control points which are still at their original positions I think. Though I also still need to work out what angle I actually want to set it too ! Thanks again batFinger and lemon.

$\endgroup$
4
  • $\begingroup$ related blender.stackexchange.com/questions/74773/… $\endgroup$
    – lemon
    Commented Jun 22, 2018 at 14:16
  • $\begingroup$ Thanks lemon, if I am understanding it correctlyif fc.data_path.endswith(('location','rotation_euler','rotation_quaternion' $\endgroup$
    – MajorTom
    Commented Jun 22, 2018 at 17:38
  • $\begingroup$ Didn't used it myself with quaternion (so you'll have to check how it is organized... 4 data rows??). But yes 'rotation_quaternion', it should be. To check that, simply print the curve names. $\endgroup$
    – lemon
    Commented Jun 22, 2018 at 17:56
  • $\begingroup$ Just to say my last comment didnt go quite as planned, I pressed return to add a line break and arrange some code and it submitted the comment, then I deleted all the characters and submitted that and it ignored me and left the comment there ! Anyway I amended the end of my original question, though I think lemon might have replied to the comment that went wrong. So Im still hazy on the usage of data_path.endswith (is it used with curves or fcurves or are they the same thing) and Im still after a way of finding the fcurve(s) associated with pinky02_L. Thanks $\endgroup$
    – MajorTom
    Commented Jun 22, 2018 at 18:47

1 Answer 1

3
$\begingroup$

Some py console code.

Below are some ways to find an fcurve for specific pose bone, and pose bone property. In this example the context pose bone, and rotation_quaternion.

>>> ob = C.object
>>> 
>>> action = ob.animation_data.action
>>> pb =  context.active_pose_bone
>>> pb.name
'Bone'

>>> # could query the pose bone  from collection via its name
>>> ob.pose.bones.get(pb.name)
bpy.data.objects['Armature'].pose.bones["Bone"]

>>> print(pb.path_from_id.__doc__)
.. method:: path_from_id(property="")

   Returns the data path from the ID to this object (string).

   :arg property: Optional property name which can be used if the path is
      to a property of this object.
   :type property: string
   :return: The path from :class:`bpy.types.bpy_struct.id_data`
      to this struct and property (when given).
   :rtype: str

# get the path from pb.id_data 
>>> dp = pb.path_from_id("rotation_quaternion")
>>> dp
'pose.bones["Bone"].rotation_quaternion'

>>> # could also build this from name
>>> 'pose.bones["%s"].rotation_quaternion' % pb.name
'pose.bones["Bone"].rotation_quaternion'

>>> # use find for one offs
>>> print(action.fcurves.find.__doc__)
ActionFCurves.find(data_path, index=0)
Find an F-Curve. Note that this function performs a linear scan of all F-Curves in the action.
# find rotquot[3] aka rotquot.z
>>> fc = action.fcurves.find(dp, index=3)
>>> fc
bpy.data.actions['ArmatureAction']...FCurve

>>> fc.array_index
3
# use list comp to find them all for for each index
>>> ["%s[%d]" % (dp, fc.array_index) for fc in action.fcurves if fc.data_path == dp]
['pose.bones["Bone"].rotation_quaternion[0]', 'pose.bones["Bone"].rotation_quaternion[1]', 'pose.bones["Bone"].rotation_quaternion[2]', 'pose.bones["Bone"].rotation_quaternion[3]']

>>> # get the value from ob using dp 
>>> ob.path_resolve("%s[%d]" % (dp, fc.array_index))
-0.43060973286628723
# ie ob.pose.bones["Bone"].rotation_quaternion[3] = -0.43
>>> pb.id_data.path_resolve("%s[%d]" % (dp, fc.array_index))
-0.43060973286628723

Links

Get bones associated with specific action in python

How can I tell if a bone property has a keyframe on the current frame using python?

How can I access bone's 'animation_data.action'?

On rotations, here is the quaternion for an xyz Euler rotation of degrees (8, 8, 8)

>>> rot = Euler((radians(8) for axis in "xyz"))
>>> rot.to_quaternion()
Quaternion((0.9930493831634521, 0.06456293165683746, 0.07427114993333817, 0.06456293165683746)) # wxyz

To set the defaults to above would set each keyframe.y of keyframes in fcurve fc with array_index=3 to 0.06.. etc.

$\endgroup$
1
  • $\begingroup$ Thanks batFinger I think that sort of works, though boy is it confusing. Ill amend my orig post with what I think is working code in a bit. The one prob I still appear to have is that the curve control points are maybe still at their original positions. So despite setting all the points to the same value I dont end up with a straight line ! Getting the data path from the pose bone and using it to find the fcurve in the action doesnt make a lot of sense to me, but it appears to work, so thank you. P.s. thanks for the tip about quaternions as Im not sure I ever did fully understand them :) $\endgroup$
    – MajorTom
    Commented Jun 22, 2018 at 20:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .