1
$\begingroup$

how would I go about doing this?

Say I have a Vector(0, 0, 1) pointing upwards in world space.

How would I align a child bone of some other bone so that it would be aligned along this vector, pointing up?

Seems like pose bone rotations are stored relative to parent pose bones, and I haven't figured a way to make this happen.

For starters I'd be happy to be able to simply make the bone point in right direction, and even better if I could somehow make bone keep its x-axis aligned towards the same direction as its parent bone's x-axis (the axis pointing right from bone's side).

I've done some scripting with rotations, edit and pose bones but my Matrix and Quaternion rotations understanding (and Blender API knowledge on this department too) is very limited.

Like this. Of course, the direction vector could be any directional vector.

enter image description here

$\endgroup$

1 Answer 1

1
$\begingroup$

I think it's best to do this in two steps. In the first step, we rotate the bone to align its y-axis with your custom vector. Then, in the second step, we rotate the bone along its y-axis (or the custom vector) to align its x-axis with its parent's x-axis. To do this, we'll need to project the parent's x-axis to the xz-plane of the bone. The following screenshots illustrate the two steps.

Step 1

Step 2

I decided to do this in the local space of the armature, so I needed to add an additional step to account for the armature object rotation. If the armature object is aligned with the world, the extra step can be skipped.

The script is attached below with comments explaining how I did it.

import bpy, mathutils

# Define the target vector
v = mathutils.Vector((0.0, 0.0, 1.0))

# Get the armature object by object name
arm = bpy.data.objects['Armature']
# Get the active bone. If you want to operator on a specific bone, use it's name instead. arm.pose.bones['Bone']
bone = arm.pose.bones[arm.data.bones.active.name]
# Get the components of the matrix of the bone. matrix is in the armature object space. matrix_basis is relative to the parent. decompose() returns the location, rotation, and scale from the matrix.
l, r, s = bone.matrix.decompose()

# The first step:
# rotation_difference returns a quaternion representing the rotation between two vectors. In our case, we need a quaternion that represent a rotation to our target vector.
r1 = bone.y_axis.rotation_difference(v)
# Apply the rotation to the bone's rotation
r.rotate(r1)

# The extra step:
# To cancel out the armature object rotation, we need to extract the rotation from its matrix_world and apply its inverse to the bone's rotation.
r_extra = arm.matrix_world.decompose()[1]
r.rotate(r_extra.inverted())

# The second step depends on our work so far being applied. We do that as follows.
# Update the matrix of the bone by building and assigning a new matrix from the old location and scale, and the modified rotation
bone.matrix = mathutils.Matrix.LocRotScale(l, r, s)
# Trigger an update.
bpy.context.view_layer.update()

# The second step:
# Repeating the first step with the parent's x-axis and the bone's x-axis will rotate the whole bone. This is not what we want. We want the bone to rotate about its y-axis. This means we need to use the parent's x-axis projection on the bone's xz-plane instead.
# I couldn't find a direct way to achieve this in the API, so I constructed the projection with the next best thing I could find.
parent_x = bone.parent.x_axis
x_proj = parent_x.project(bone.x_axis)
z_proj = parent_x.project(bone.z_axis)
parent_x_proj = x_proj + z_proj
# The remaining steps were explained previously
r2 = bone.x_axis.rotation_difference(parent_x_proj)
r.rotate(r2)
bone.matrix = mathutils.Matrix.LocRotScale(l, r, s)
$\endgroup$
3
  • $\begingroup$ Hey, thanks! I'll check this out when I have time! I already got something working last night, after testing various things and after banging my head on the wall for maybe 4 hours. I definitely want to compare my "solution" to this and see how this works. Anyway - what I did: Calculate upper arm dir, calculate forearm dir, then using cross product calculate sideways and bone up/roll directions. I then built a rotations using these vectors by filling an identity matrix with these vectors. Moved armature ain't problem but my solution doesn't work yet if armature object itself has been rotated. $\endgroup$
    – ezez
    Nov 24, 2023 at 15:59
  • $\begingroup$ Thanks again for the reply - just had time to test it a few times. It is a different approach that what I could get done, and there are several useful functions used that I have to learn to use. I still have to find more info on "rotation_difference" and another thing I tried to use (to_track_quat) - seems like there is very little info available. Overall I feel API descriptions are really just that, mostly lacking any helpful / insightful info for those who are not familiar with those specific features or concepts. $\endgroup$
    – ezez
    Nov 25, 2023 at 16:46
  • $\begingroup$ You're welcome! I am glad I was able to help. $\endgroup$
    – Mr A
    Nov 25, 2023 at 16:53

You must log in to answer this question.

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