Hey!
So this is my first post here! I recently started as technical animator/artist and its quite difficult to get certain information just from the internet. I wanted to inquire from those more experienced how I would specifically, account for a mirror when matching from FK to IK (in this context, matching FK controls to IK joints).
To give some context, this is not a rig I made and is a Maya-based rig. This isn’t a work project, but something I’m kind of keen to know about as I can’t for the life of me solve it.
The rig setup:
The rig is something I haven’t seen before ( I did not create it). The Left and right limb joints are not mirrors of one another, instead they have the same orientations. Instead, to get that mirrored behavior (say, +45 on the Z axis rotates the controls in opposite directions), the controls themselves are mirrored, and the constraints from the FK controls to those un-mirrored FK joints have a constraint offset.
I’ve tried so many different ways to get the matching script to work, I just am unable to. I wanted to ask if there is anything specific I should be accounting for. I’ve supplied below a script that is one of the variations of a solution I tried to make work. (so you can see my approach).
CODE**********************************
hand_node = pm.PyNode(end_ctrl.partialPathName())
##############################################
# In IK Mode.
##############################################
hand_node.ikSwitch.set(1)
ik_joint_parent_offset_m_matrix = Ops.getParentOffsetMatrix(ik_joint)
constraint_matrix = Ops.get_worldMatrix(ik_joint) * ik_joint_parent_offset_m_matrix.inverse()
pre_constraint_matrix = (constraint_matrix.inverse() * ik_joint.inclusiveMatrix())
matrices = {
"cached_ik_joint": Ops.get_localMatrix(ik_joint),
"ik_joint_world_matrix": Ops.get_worldMatrix(ik_joint),
"ik_parent_offset_matrix": ik_joint_parent_offset_m_matrix,
"constraint_matrix": constraint_matrix,
"pre_constraint_matrix": pre_constraint_matrix
}
for name, matrix in matrices.items():
matrices[name] = Ops.get_ik_data(name, matrix)
# get our null
null = pm.PyNode(pm.group(em=True))
pm.matchTransform(null, fk_ctrl.partialPathName())
##############################################
# In FK Mode.
##############################################
hand_node.ikSwitch.set(0)
fk_ctrl_local_matrix = fk_ctrl.inclusiveMatrix() * fk_ctrl.exclusiveMatrixInverse()
# Get the inverse parent offset matrix. If I get this and then the IK local matrix, if its not an
# identity then we're good.
# MQuaternion of fk_ctrl.
fk_ctrl_quaternion = Ops.matrix_to_quaternion(fk_ctrl_local_matrix)
printEulerAnglesFromQuaternion(fk_ctrl_quaternion, name="fk_ctrl quat: ")
# MQuaternion of IK_joint.
ik_joint_quaternion = matrices["cached_ik_joint"]["MQuaternion"]
# r_side
if mirrored:
fk_ctrl_Euler = fk_ctrl_quaternion.asEulerRotation()
fk_ctrl_Euler.y = fk_ctrl_Euler.y + 180
newEuler = om.MEulerRotation(fk_ctrl_Euler.x, fk_ctrl_Euler.y, fk_ctrl_Euler.z)
fk_ctrl_quaternion.setValue(newEuler)
# Calculate the offset rotation
adjusted_rotation = (matrices["ik_joint_world_matrix"]["MQuaternion"]
* matrices["ik_parent_offset_matrix"]["MQuaternion"].inverse())
# Combine the adjusted rotation with the modified FK quaternion
final_rotation = adjusted_rotation.inverse() * fk_ctrl_quaternion.conjugate()
printEulerAnglesFromQuaternion(final_rotation, name="final_rotation_quat: ")
euler_offset = final_rotation.asEulerRotation()
degrees = [math.degrees(angle) for angle in (euler_offset.x, euler_offset.y, euler_offset.z)]
cmds.xform(fk_ctrl, objectSpace=True, ro=degrees, relative=True)
# l_side
else:
quat_offset = ik_joint_quaternion.inverse() * fk_ctrl_quaternion.conjugate()
printEulerAnglesFromQuaternion(quat_offset, name="quat offset: ")
euler_offset = quat_offset.asEulerRotation()
print("euler offset", euler_offset)
degrees = [math.degrees(angle) for angle in (euler_offset.x, euler_offset.y, euler_offset.z)]
print(degrees)
cmds.xform(fk_ctrl, objectSpace=True, ro=degrees, relative=True)
CODE END********
I’m not specifically looking for any code fixes as this code doesn’t really work, but basically suggestions for how to account for the type of mirror I stated earlier.
Thank you and appreciate the time!