Maya - match rotation of a single axis despite rotate order

I have a camera and a light. I want the light to match the camera’s Y rotation value.

So if the camera’s rotation is (45,23,-12), my light should rotate to (0,23,0) and point in the same direction than the camera on the Y rotate axis.

Problem: the camera’s rotation order is set to xyz, I can’t change this order.

With most rotation values there is no problem using a simple orient constraint, but with some orientations the light direction is completely wonky.

My solution for now is to create a locator with rotation order yxz, parent it to the camera, zero it out and unparent it and set the X and Z rotations to 0, which gives me the correct Y euler value… But Ideally I don’t want to create new nodes to get this value.

Is there a way to convert euler rotation with a certain rotation order to another one ?

I tried playing with quaternions and openMaya but I can’t seem to find a solution.

Thanks

This high-performance library is part of the “VFX Reference Platform” standard and is included in almost all modern CG software.

Thanks !

Not that easy without a python documentation, but I tried this:

import imath
import math
r_euler_degree =  (45,23,-15)

e_xyz = imath.Eulerf(
    math.radians(r_euler_degree[0]),
    math.radians(r_euler_degree[1]),
    math.radians(r_euler_degree[2]),
    imath.Eulerf.Order.XYZ
)
# convert to a rotation matrix
m = e_xyz.toMatrix44()

# extract Euler angles in YXZ order
e_yxz = imath.Eulerf(0,0,0, imath.Eulerf.Order.YXZ)
e_yxz.extract(m)

print(r_euler_degree)
print(math.degrees(e_yxz.x), math.degrees(e_yxz.y), math.degrees(e_yxz.z))

And it does not seem to match the rotations when reusing the values in maya.

Any help would be greatly appreciated!

The order of the returned values ​​corresponds to the assigned order!
In your case: e_yxz.x - 0, e_yxz.y - 1, e_yxz.z - 2
ORDER = YXZ1,0,2

tr = 'rsPhisicalLight2'
cmds.setAttr('{}.rotateOrder'.format(tr), 4)
cmds.setAttr('{}.rotateX'.format(tr), math.degrees(e_yxz.y))
cmds.setAttr('{}.rotateY'.format(tr), math.degrees(e_yxz.x))
cmds.setAttr('{}.rotateZ'.format(tr), math.degrees(e_yxz.z))

Litle Demo:

import maya.mel as mel
import maya.cmds as cmds
import imath
import math
from time import sleep as time_sleep

cancell_message = ('\n\t\t Ups... the user interrupted the execution' +
              ' of the Demo Script... \n')
pns = mel.eval('saveChanges("file -force -new");')
if not pns:
    cmds.error(cancell_message)

g_model_panel = 'modelPanel4'
cmds.setFocus(g_model_panel)
cmds.modelEditor(g_model_panel,
    edit = True,
    da = 'wireframe',    #'smoothShaded'
    grid = True,
    swf = True,
    udm = False,
    wos = True)
mel.eval('setNamedPanelLayout "Four View";')
mel.eval('updateToolbox();')
mel.eval('setNamedPanelLayout "Single Perspective View";')
mel.eval('updateToolbox();')

def view_fit_for_cam():
    cmds.setFocus(g_model_panel)
    time_sleep(0.5)
    cmds.refresh(force = True)
    cmds.viewFit(animate = True)
    cmds.refresh(force = True)

# create poly cubes for all Maya baze rotate order variants
p_cubes = []
for i in range(6):
    p_cubes.append(cmds.polyCube(
        w = 100, h = 100, d = 100,
        sx = 10, sy = 10, sz = 10,
        ax = (0, 1, 0), cuv = 4, ch = 0)[0])
cmds.select(clear = True)
view_fit_for_cam()
# set euler angle (degrees) for demo
r_euler_degree_xyz = (45.0, 23.0, -15.0)

# set rotate order and rotate values for poly cube 1
cmds.select(p_cubes[0])
view_fit_for_cam()
cmds.setAttr('{}.rotateOrder'.format(p_cubes[0]), 0)
cmds.setAttr('{}.rotateX'.format(p_cubes[0]), r_euler_degree_xyz[0])
cmds.setAttr('{}.rotateY'.format(p_cubes[0]), r_euler_degree_xyz[1])
cmds.setAttr('{}.rotateZ'.format(p_cubes[0]), r_euler_degree_xyz[2])
view_fit_for_cam()

# create Euler Angle from xyz order
e_xyz_d = imath.Eulerd(
    math.radians(r_euler_degree_xyz[0]),
    math.radians(r_euler_degree_xyz[1]),
    math.radians(r_euler_degree_xyz[2]),
    imath.Eulerd.XYZ)
# create matrix from Euler Angle
m44_d = e_xyz_d.toMatrix44()

# optional, for other input matrix,
# with scale and/or shear values
scale_v = imath.V3d()
shear_v = imath.V3d()
m44_d.extractAndRemoveScalingAndShear(scale_v, shear_v)

# get Euler Angle from matrix for yzx order
e_yzx_f = imath.Eulerf(0,0,0, imath.EULER_YZX)
e_yzx_f.extract(m44_f)
r_euler_degree_yzx = (
    math.degrees(e_yzx_f.x),
    math.degrees(e_yzx_f.y),
    math.degrees(e_yzx_f.z))
# set rotate order and rotate values for poly cube 2
cmds.select(p_cubes[1])
view_fit_for_cam()
cmds.setAttr('{}.rotateOrder'.format(p_cubes[1]), 1)
cmds.setAttr('{}.rotateX'.format(p_cubes[1]), r_euler_degree_yzx[2])
cmds.setAttr('{}.rotateY'.format(p_cubes[1]), r_euler_degree_yzx[0])
cmds.setAttr('{}.rotateZ'.format(p_cubes[1]), r_euler_degree_yzx[1])
view_fit_for_cam()

# get Euler Angle from matrix for zxy order
e_zxy_f = imath.Eulerf(0,0,0, imath.EULER_ZXY)
e_zxy_f.extract(m44_f)
r_euler_degree_zxy = (
    math.degrees(e_zxy_f.x),
    math.degrees(e_zxy_f.y),
    math.degrees(e_zxy_f.z))
# set rotate order and rotate values for poly cube 3
cmds.select(p_cubes[2])
view_fit_for_cam()
cmds.setAttr('{}.rotateOrder'.format(p_cubes[2]), 2)
cmds.setAttr('{}.rotateX'.format(p_cubes[2]), r_euler_degree_zxy[1])
cmds.setAttr('{}.rotateY'.format(p_cubes[2]), r_euler_degree_zxy[2])
cmds.setAttr('{}.rotateZ'.format(p_cubes[2]), r_euler_degree_zxy[0])
view_fit_for_cam()

# get Euler Angle from matrix for xzy order
e_xzy_f = imath.Eulerf(0,0,0, imath.EULER_XZY)
e_xzy_f.extract(m44_f)
r_euler_degree_xzy = (
    math.degrees(e_xzy_f.x),
    math.degrees(e_xzy_f.y),
    math.degrees(e_xzy_f.z))
# set rotate order and rotate values for poly cube 4
cmds.select(p_cubes[3])
view_fit_for_cam()
cmds.setAttr('{}.rotateOrder'.format(p_cubes[3]), 3)
cmds.setAttr('{}.rotateX'.format(p_cubes[3]), r_euler_degree_xzy[0])
cmds.setAttr('{}.rotateY'.format(p_cubes[3]), r_euler_degree_xzy[2])
cmds.setAttr('{}.rotateZ'.format(p_cubes[3]), r_euler_degree_xzy[1])
view_fit_for_cam()

# get Euler Angle from matrix for yxz order
e_yxz_f = imath.Eulerf(0,0,0, imath.EULER_YXZ)
e_yxz_f.extract(m44_f)
r_euler_degree_yxz = (
    math.degrees(e_yxz_f.x),
    math.degrees(e_yxz_f.y),
    math.degrees(e_yxz_f.z))
# set rotate order and rotate values for poly cube 5
cmds.select(p_cubes[4])
view_fit_for_cam()
cmds.setAttr('{}.rotateOrder'.format(p_cubes[4]), 4)
cmds.setAttr('{}.rotateX'.format(p_cubes[4]), r_euler_degree_yxz[1])
cmds.setAttr('{}.rotateY'.format(p_cubes[4]), r_euler_degree_yxz[0])
cmds.setAttr('{}.rotateZ'.format(p_cubes[4]), r_euler_degree_yxz[2])
view_fit_for_cam()

# get Euler Angle from matrix for zyx order
e_zyx_f = imath.Eulerf(0,0,0, imath.EULER_ZYX)
e_zyx_f.extract(m44_f)
r_euler_degree_zyx = (
    math.degrees(e_zyx_f.x),
    math.degrees(e_zyx_f.y),
    math.degrees(e_zyx_f.z))
# set rotate order and rotate values for poly cube 6
cmds.select(p_cubes[5])
view_fit_for_cam()
cmds.setAttr('{}.rotateOrder'.format(p_cubes[5]), 5)
cmds.setAttr('{}.rotateX'.format(p_cubes[5]), r_euler_degree_zyx[2])
cmds.setAttr('{}.rotateY'.format(p_cubes[5]), r_euler_degree_zyx[1])
cmds.setAttr('{}.rotateZ'.format(p_cubes[5]), r_euler_degree_zyx[0])
view_fit_for_cam()

Good luck!

Here’s how I would do it using OpenMaya2.

from maya import cmds
from maya.api import OpenMaya as om2
from math import degrees

camera = 'locator1'  # or whatever your camera's name is

# Get the camera matrix
cameraMat = cmds.xform(camera, matrix=True, query=True)

# build an MEulerRotation from the camera's matrix
# The second argument lets you choose the rotation order
rot = om2.MEulerRotation.decompose(om2.MMatrix(cameraMat), om2.MEulerRotation.kYXZ)

# build the output in degrees
output_rotation = (degrees(rot.x), degrees(rot.y), degrees(rot.z))
1 Like

you could use a simple trick with the aimConstraint if it needs to be a live setup:

  • make a locator in the Yaxis of the light
  • use the locator as the driver, the light as the driven object
  • use an aim constraint to make the light aim in the yaxis to the locator
  • use the z axis as your up vector
  • make the aimconstraint use object rotation up as your uptype method
  • use your camera as the up object and set its vector to negative Z