When creating a rig, the bind pose of a character mesh is a complicated topic. Modelers and TDs have an opposing set of requirements, with valid arguments on both sides.

Modelers typically desire a character mesh created in A-Pose (arms down at an angle, legs spread slightly). Stance advantages include easier normal baking, better deformation at bony landmarks, easier to critique and gauge proportions, and the underlying premise to model for the stance seen most often in motion. However, a big disadvantage is a lack of attention to armpits, crotches, and sometimes even the throat due to areas being hidden by overlapping geometry.

TDs tend to prefer a character mesh in T-pose (everything at right angles, unnaturally parallel to the world), a stance based largely on the mathematics. It is great for rigging, animation, and motion transfer but tends to mush bony landmarks if a mesh is constructed and bound in this pose. T-Pose can also present issues with certain vertex shaders when geometry crashes into itself due to the stance at bind pose.

My solution to the tug-of-war is a workflow incorporating both poses. Create, bind, and export the game mesh in A-Pose; Rig and animate the model from T-Pose. With skeletal joints created in A-Pose (the one seen the most), animations will presumably cross through bind pose most often, meaning fewer opportunities for gimbal. And since the rig is generated in T-Pose, TDs and animators enjoy the full set of rigging benefits. To top it off, bony landmarks are retained, meaning both parties are happy.

To pull this off, a bound character needs transposed from A-Pose to T-Pose. Using the `OpenMaya`

API and
some basic linear algebra, my solution involves only math. You simply extract a rotation, convert it to axes,
compare those to the world to determine closest cardinal, reconstruct, and apply.

#### Cardinal Alignment Tool

_{tool development using OpenMaya and Maya 2017}

#### The Underlying Linear Algebra

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | ```
import maya.api.OpenMaya as api2
import math
from not_included import from_vectors
def closest_cardinal(in_rotation):
"""
Returns the euler rotation of the closest cardinal to the given input rotation. This method is used to calculate
the rotation required to precisely axis align objects to the world. Both the input and output rotations are in
worldspace coordinates.
:param in_rotation: The incoming [x, y, z] euler rotation, in worldspace
:type in_rotation: iterable
:return: The closest cardinal [x, y, z] euler rotation, in worldspace
:rtype: list
"""
# worldspace cardinal directions
cardinal_axes = [api2.MVector(1., 0., 0.), api2.MVector(-1., 0., 0.),
api2.MVector(0., 1., 0.), api2.MVector(0., -1., 0.),
api2.MVector(0., 0., 1.), api2.MVector(0., 0., -1.)]
# incoming rotation data
rotate_matrix = api2.MEulerRotation([math.radians(x) for x in in_rotation]).asMatrix()
rotate_axis_x = api2.MVector(tuple(rotate_matrix)[0:3])
rotate_axis_y = api2.MVector(tuple(rotate_matrix)[4:7])
# determine which cardinal axis is closest to the rotateX axis
dots_x = [rotate_axis_x * cardinal_axis for cardinal_axis in cardinal_axes]
closest_x = cardinal_axes.pop(dots_x.index(max(dots_x)))
# determine which cardinal axis is closest to the rotateY axis
dots_y = [rotate_axis_y * cardinal_axis for cardinal_axis in cardinal_axes]
closest_y = cardinal_axes[dots_y.index(max(dots_y))]
# then derive rotateZ via cross product
closest_z = (closest_x ^ closest_y).normalize()
cardinal_matrix = api2.MTransformationMatrix(from_vectors(closest_x, closest_y, closest_z))
out_rotation = [math.degrees(x) for x in cardinal_matrix.rotation()]
return out_rotation
``` |