Register Latest Topics
 
 
 


Reply
  Author   Comment  
pshipkov

SOuP Jedi
Registered:
Posts: 4,711
Reply with quote  #1 

The other day i needed an aim constraint (a look-at functionality) in Nuke but couldn't find anything out there so i had to come up with something.

Figured this may be interesting and for the Maya guys.
Here it goes:

Code:

import maya.cmds as mc
import maya.OpenMaya as om

def lookAt(base, target, up=[0,1,0]):

    # check if the "base" object has parent
    parent = mc.listRelatives(base, pa=True, p=True)
    if parent != None: parent = parent[0]

    base = mc.getAttr(base+".worldMatrix")
    target= mc.getAttr(target+".worldMatrix")

    # build transformation matrix
    x = om.MVector(target[12]-base[12], target[13]-base[13], target[14]-base[14])
    x.normalize()
    z = x ^ om.MVector(-up[0], -up[1], -up[2])
    z.normalize()
    y = x ^ z
    y.normalize()
    m = om.MMatrix()
    l = [x.x, x.y, x.z, 0, y.x, y.y, y.z, 0, z.x, z.y, z.z, 0, 0, 0, 0, 1]
    om.MScriptUtil.createMatrixFromList(l, m)

    if parent:
       # transform the matrix in the local space of the parent object
        m2 = om.MMatrix()
        l = mc.getAttr(parent+".worldMatrix")
        om.MScriptUtil.createMatrixFromList(l, m2)
        m *= m2.inverse()

    # retrieve the desired rotation for "base" to aim at "target", in degrees
    r = om.MTransformationMatrix(m).eulerRotation() * 57.2958

    return r[0], r[1], r[2]

0
mapofemergence

Avatar / Picture

First taste
Registered:
Posts: 7
Reply with quote  #2 
thank you @pshipkov, that is useful indeed.

I guess this is not the ideal place to ask, but could you share how you achieved that in nuke?

cheers,
s

__________________
if you're the least noob in the room, you're in the wrong room
@mapofemergence on twitter
0
pshipkov

SOuP Jedi
Registered:
Posts: 4,711
Reply with quote  #3 
Forgot to answer this one.

Nuke has basic lookAt capability in its 3D nodes, but no up-vector and aim in place functionality.

Code:

import nuke

def nuke_aimConstraint(base, target, up=[0,1,0]):

  base = nuke.toNode(base)
  target = nuke.toNode(target)
  try: parent = base.input(0)
  except: parent = None

  m = nuke.math.Matrix4()
  l = base["matrix"].getValue()
  l2 = target["world_matrix"].getValue()

  if parent:
    # target's world matrix
    for i in range(16): m[i] = l2[i]
    # parent's inverse world matrix
    m2 = nuke.math.Matrix4()
    l2 = parent["world_matrix"].getValue()
    for i in range(16): m2[i] = l2[i]
    m2 = m2
    # target in the space of parent
    l2 = m * m2.inverse()
    # up vector in the space of parent
    up = m2.transform(nuke.math.Vector4(up[0], up[1], up[2], 1))

  # build transformation matrix
  x = nuke.math.Vector3(l[3]-l2[3], l[7]-l2[7], l[11]-l2[11])
  x.normalize()
  z = x.cross(nuke.math.Vector3(-up[0], -up[1], -up[2]))
  z.normalize()
  y = x.cross(z)
  y.normalize()
  m = nuke.math.Matrix4()
  m[0] = -x.x
  m[1] = -x.y
  m[2] = -x.z
  m[3] = 0
  m[4] = y.x
  m[5] = y.y
  m[6] = y.z
  m[7] = 0
  m[8] = z.x
  m[9] = z.y
  m[10] = z.z
  m[11] = 0
  m[12] = 0
  m[13] = 0
  m[14] = 0
  m[15] = 1

  # retrieve the desired rotation for "base" to aim at "target", in degrees
  r = m.rotationsZXY()
  base["rotate"].setValue([r[0]*57.2958, r[1]*57.2958, r[2]*57.2958])

0
jouak

First taste
Registered:
Posts: 7
Reply with quote  #4 
Woow thanks @pshipkov !!!
Really useful to animate something flying/swimming!
I tweaked a bit the original code to set a rotation key with a value which aim to the object's position 2 frames in the futur. Basically, it's orienting the object to follow the animation.

I guess the code is quite dirty, I'm only an animator, not a coder :D
I'm creating a temp locator, getting object's xform 2 frames later, applying to locator translate, going back on current frame, applying  'lookAt()', with the locator as aim
Code:


#######pshipkov's
import maya.cmds as mc
import maya.OpenMaya as om

def lookAt(base, target, up=[0,-1,0]):

# check if the "base" object has parent
parent = mc.listRelatives(base, pa=True, p=True)
if parent != None: parent = parent[0]

base = mc.getAttr(base+".worldMatrix")
target= mc.getAttr(target+".worldMatrix")

# build transformation matrix
z = om.MVector(target[12]-base[12], target[13]-base[13], target[14]-base[14])
z.normalize()
x = z ^ om.MVector(-up[0], -up[1], -up[2])
x.normalize()
y = x ^ z
y.normalize()
m = om.MMatrix()
l = [x.x, x.y, x.z, 0, y.x, y.y, y.z, 0, z.x, z.y, z.z, 0, 0, 0, 0, 1]
om.MScriptUtil.createMatrixFromList(l, m)

if parent:
# transform the matrix in the local space of the parent object
m2 = om.MMatrix()
l = mc.getAttr(parent+".worldMatrix")
om.MScriptUtil.createMatrixFromList(l, m2)
m *= m2.inverse()
# retrieve the desired rotation for "base" to aim at "target", in degrees
r = om.MTransformationMatrix(m).eulerRotation() * 57.2958

return r[0], r[1], r[2]

 #####

def aimFuturPosition(inNode, offset):
offsetTime=offset
tmpLoc = mc.spaceLocator(n=inNode+'_tmpLocator')
mc.currentTime((mc.currentTime(query=True))+offsetTime)
tmpLoc_translate = mc.xform( inNode, query=True, t=True, worldSpace=True)
mc.xform( tmpLoc[0], worldSpace=True, t=(tmpLoc_translate))
mc.currentTime((mc.currentTime(query=True))-offsetTime)

matrixRotation=lookAt( tmpLoc[0], inNode)
print matrixRotation
mc.xform(inNode, worldSpace=True, ro=(matrixRotation))

mc.select(inNode)
mc.filterCurve (inNode)
mc.delete(tmpLoc)
mc.currentTime ((mc.findKeyframe(timeSlider=True, which='next')),edit=True)

 

inNode=mc.ls(sl=True)
aimFuturPosition(inNode[0], 2)





PS: my controller was using Z as the front axis, so I tweaked a bit the up, an switched x and z

Hope it could be useful to some of you guys!
 

0
pshipkov

SOuP Jedi
Registered:
Posts: 4,711
Reply with quote  #5 
Thanks for the kind words and for sharing your code.
0
Previous Topic | Next Topic
Print
Reply

Quick Navigation: