Skip to main content
françois leroy
Inspiring
March 27, 2013
Answered

Full auto-orient via expression

  • March 27, 2013
  • 2 replies
  • 13806 views

Hi!

I'd like a 3D Null to get the orientation of 3D a layer. (without parenting, it would be too easy )

I've tried a lot of solutions, through orientation, rotations, both combined... some results are close, but never perfect.

If I'm right, orientation is computed before rotations...

I think the simplest way would be with auto-orientation, or the lookAt() expression on orientation, wich gives me correct X and Y orientations, and then get an expression on zRotation to get a "full" orientation.

But what would it be?

How can I get the Z orientation in layer space?

Any idea will be appreciated!

François

Correct answer Dan Ebberts

Try this oreientation expression:

L = thisComp.layer("other layer");

u = fromWorldVec(L.toWorldVec([1,0,0]));

v = fromWorldVec(L.toWorldVec([0,1,0]));

w = normalize(fromWorldVec(L.toWorldVec([0,0,1])));

sinb = clamp(w[0],-1,1);

b = Math.asin(sinb);

cosb = Math.cos(b);

if (Math.abs(cosb) > .0005){

  c = -Math.atan2(v[0],u[0]);

  a = -Math.atan2(w[1],w[2]);

}else{

  a = (sinb < 0 ? -1 : 1)*Math.atan2(u[1],v[1]);

  c = 0;

}

[radiansToDegrees(a),radiansToDegrees(b),radiansToDegrees(c)]

Dan

2 replies

synthetick
Community Expert
Community Expert
January 19, 2025

This thread has helped me a lot but I'm stuck on something. I'm trying to set up expressions for an animation of an office chair. I have a 3D null called "null - direction" which controls the path of the chair, and it is set to auto-orient along the path. But I wanted the wheels to have a slight delay so that when the chair turns a corner the wheels take a little bit of extra time to rotate into that orientation.

In order to do this I made a null called "null - getOrientation" and I used the expressions in this thread to get the orientation of the chair, and then I used another null called "null - delay" to delay the orientation by half a second. The orientation of the wheels is pick-whipped to the orientation of "null - delay"

It all seems to work ok at the first corner but when we get to the second corner the chair is turning clockwise but "null - getOrientation" and "null - delay" start turning anticlockwise and we end up with wheels not oriented to the path. I'm attaching a video which I hope shows what I mean. On further investigation it seems to be occurring when the value of sinb reaches the top or bottom of the sine wave curve ie. at 1 or -1.

françois leroy
Inspiring
March 27, 2013

It seems I asked a bit quickly...

Here's the solution in case somebody needs it:

for orientation:

a = thisComp.layer("master").toWorld([0,0,0]);

b = thisComp.layer("master").toWorld([0,0,100]);

lookAt(a,b)

then for zRotation:

a = fromWorld(thisComp.layer("master").toWorld([0,0,0]));

b = fromWorld(thisComp.layer("master").toWorld([100,0,0]));

c = a-b;

radiansToDegrees(Math.atan2(c[1],c[0]))+180;

Hope it can help!

François

françois leroy
Inspiring
March 28, 2013

Aaaaargh !

Nope, it's not perfect, even if it's close to the result...

Does anyone have a solution?

Dan?

Thanx.

Inspiring
May 9, 2013

I agree with the loop, I can't find any other way.

But I still don't think what you're doing will work: what if the scale is [100, 100, -100] for example? It's still going to use a wrong XYZ basis for the calculation and your workaround for the X/Y scales won't do anything. I may be wrong though, tell me if I'm completely mistaken.

What I'll try is "prévenir plutôt que guérir" and recalculate the scale using a while loop, then multiply the u, v and w vectors I send to the getRotation functions by the computed scales' signs. This way 1) the parented object will have the same scale as it's parent and 2) the getRotation function should get a proper XYZ basis.


So here's what I tried:

- scale expression

{

  var layer = thisComp.layer(  /* name of the parent layer */  );

  var scl = [1, 1, 1];

  while (layer != null)

  {

    for (var j=0; j<3; j++)

    {

      scl *= layer.scale/100;

    }

    try

    {

      layer = layer.parent;

    }

    catch (err)

    {

      layer = null;

    }

  }

  scl*100;

}

- orientation expression

{

  function sign(x)

  {

    return x>=0  ?  1 : -1;

  }

  function getOrientation(u, v, w)

  {

      // still the same thing

  }

  layer = thisComp.layer(  /* name of the parent layer */  );

  u = layer.toWorldVec([1,0,0])*sign(scale[0]);

  v = layer.toWorldVec([0,1,0])*sign(scale[1]);

  w = layer.toWorldVec([0,0,1])*sign(scale[2]);

  getOrientation(u, v, w);

}

I haven't tested it that much but the idea seems fine to me. In the scale expression note that I start the loop from the parent layer because in my case I want my parented layer to have the exact same scale.