LookAt Constraint – axis and twisting problems?

LookAt Constraint – axis and twisting problems?

peterjung
Enthusiast Enthusiast
4,515 Views
12 Replies
Message 1 of 13

LookAt Constraint – axis and twisting problems?

peterjung
Enthusiast
Enthusiast

I am trying to animate a nozzle type object which is moved by a dummy that rotates in an arc around the origin point.  The limit needs to be X rotation +/- 45 degrees and Z rotation +/-45 degrees.

 

The nozzle pivot point is fixed at the origin.  I am struggling to get this rigged so it behaves correctly. 

 

If the controlling dummy object is simply rotated in only the direct vertical (X rotation) or only in the direct horizontal direction (Z rotation) then everything works fine.  The issue is when X and Z rotations are combined

 

With a simple LookAt constraint (target = controlling dummy object) the nozzle object twists with a Y direction rotation I have tried two other methods as outlined below but none of these work properly:

 

Test 1:

 

Normal Look At constraint.

 

Tracks target OK but object twisting on Y-axis – you can see the red part – this should stay exactly horizontal.

 

Test 2:

 

Using the Expose Transform helper object expose the X rotation and the Z rotation values of the LookAt dummy object

 

Using Parameter Wiring wire each value to the X and Z rotation of the object

 

Does not track target properly and is off in both the X and Z rotations.

 

Test 3:

 

Using “single axis LookAt method” in X axis with dummy object and again on Z axis with a separate dummy object.  I used the method as described in this video:

 

https://youtu.be/k9VlncTujNA

 

I created two separate  dummy objects with LookAt constraints each to only track one axis each – i.e. X rotation and Z rotation respectively.

 

Using the Expose Transform helper object expose the X rotation and the Z rotation value of each separate dummy object

 

Using Parameter Wiring wire the X rotation of one dummy and the Z rotation of the other dummy to the X and Z rotation of the object.

 

This method sees to track the target correctly in Z rotation but X is way off.

 

Now I am all out of fresh ideas….. maybe a LookAt constraint is the wrong way to go about this?

 

Here is a YouTube clip showing what I have so far

 

https://youtu.be/3lFH6GhLRp4

 

I would appreciate any help in solving this issue!  Thanks in advance…..

0 Likes
Accepted solutions (1)
4,516 Views
12 Replies
Replies (12)
Message 2 of 13

domo.spaji
Advisor
Advisor

Nozzle is gray cylindrical object, wright?

What if you  just animate/rotate that obj.?

 

Or link it to that dummy at the origin? Same thing.

Too many dummies just unnecessary complicate things. Single dummy is one dummy to much!!!

Specially plain box dummy!!! At least use point helpers with  axis tripod display. And can change colors.

 

Some tips:

Forget Expose Transform helper, nothing is hidden, just have to look at the wright place.

Forget about "wire parameters", much simpler and quicker  is copying, pasting instancing controllers/tracks in track editor. Not too many videos on YouTube about that, though.

 

 

0 Likes
Message 3 of 13

leeminardi
Mentor
Mentor

Here's an alternative solution that uses a transform script for the inner and outer gimbal.

A base dummy is used to enable the reposition of the mechanism anywhere and at any orientation in space.

Since the outer gimbal does not pitch or roll but only has yaw (z axis rotation) the vector pointing towards dummy1 has the same z coordinate as the base dummy. A cross product of this vector with a z vector yields the x axis of the outer gimbal..  The inner gimbal uses a vector from the base dummy to the target dumm1 to define the y axis. The inner gimbals x axis is the same as the outer gimbal x axis while the z axis is the cross product of x and y.

If needed, if statements could be included to limit rotation to 45.

image.png

°   

lee.minardi
0 Likes
Message 4 of 13

peterjung
Enthusiast
Enthusiast

Hi domo.spaji

 

Thank you for taking the time to reply.  Yes the nozzle is the grey cylindrical object.

 

“What if you  just animate/rotate that obj.?”

 

You mean by using keyframes?  I guess that would work but ideally I was hoping to rig this as a proper functioning model so I could have it move in many different motion patterns without the need to animate any parts for each individual motion path.

 

“Or link it to that dummy at the origin? Same thing.”

 

If I understand you properly then that is basically what Test 1 in the video clip is – but this causes the unwanted “twisting action”

 

Thanks for your tips and I will certainly try and read up on that!

0 Likes
Message 5 of 13

peterjung
Enthusiast
Enthusiast

Hi leeminardi

 

Thank you very much for your help and suggested solution – this is very close to what I need but still not quite right – it may have helped if I had shown a little more of the model…. my bad!

 

There is actually another universal type joint at the end of the nozzle  - at the origin and the complete nozzle needs to pivot around that point.

Image6.jpg

 

The “driving” object needs to be what you have as “DummyBase” (i.e. the "frame") in your file and dum1 needs to be stationary and the pivot point for the nozzle – i.e. location of the other universal joint – a little reversed from what you have.

 

I am sure your file just needs some tweaking so I am currently trying to understand exactly what each line of script you posted actually does… I think I understand the basic concept but I still need to spend some more time to try and fully understand… the transform matrix concept is new to me!

 

Here is a short clip of the motion of the device where you can see how the frame drives the nozzle angle – here you can see the wrong motion of the nozzle… which is where I came in 😊

 

https://youtu.be/l6_7YB72NHg

0 Likes
Message 6 of 13

leeminardi
Mentor
Mentor

@peterjung   the rigging that you are trying to do is similar to a single piston in the hexapod rig of this tutorial I made. Note that the inner piston rotates relative to the outer piston.  The hexapod uses offset universal joints whereas your rig uses standard universal joints.  Otherwise the kinematics are identical.  Are you familiar with vector math?

 

Can you post a copy of the model?

lee.minardi
0 Likes
Message 7 of 13

peterjung
Enthusiast
Enthusiast

 

Hi  leeminardi

 

Firstly let me say your hexapod tutorial is fantastic – an absolute masterclass and thank you so much for sharing.  Your straightforward and concise explanation has helped my understanding enormously!  Way, way back in the day I was an Electrical Engineering major but we did have some fundamental mechanics classes so I had a hazy recollection of vector math – however this tutorial has really helped me understand much better how Max and vector math can go together… very clever stuff!

 

I have watched this a few times now and it is starting to sink in – I can understand what you are doing with your code – my trouble at the moment is working out what the correct vectors are for my rig but I am working on it….

 

Sorry for the late response – this is only a hobby for me so I can only really get some time to work on this at the weekends right now – the vector math is needing all my concentration to understand!

 

I can’t share the model on a public form but I have sent you a link via PM.

 

Thanks again… having a go at the vector math right now! 😊

 

Peterjung

0 Likes
Message 8 of 13

peterjung
Enthusiast
Enthusiast

So after a couple of hours (well a few more actually!)  of messing around I can get this to work after a fashion by following closely the hexapod tutorial.  I can get the basic motion of the nozzle correct by doing the following – note point B is linked to Top Plate:

 

1.jpg

 

Rev A Universal Joint Rear Transform Script:

ptbw = pointB * topPlateT

uAB = normalize(ptbw - pointAT.row4)

xdir = pointAT.row1

ydir = normalize (cross uAB xdir)

zdir = normalize (cross xdir ydir)

(matrix3 xdir ydir zdir pointAT.row4)

 

Where:

topPlateT = $'Top Plate'.Transform

pointAT = $PointA.Transform

pointB = $PointB.transform.controller.Position

 

Rev A Universal Joint Front Transform Script:

ptbw = pointBT.row4 * topPlateT

uAB = normalize(pointA - ptbw)

xdir = normalize ((pointBT * topPlateT).row1)

ydir = normalize (cross xdir uAB)

zdir = normalize (cross xdir ydir)

(matrix3 xdir ydir zdir ptbw)

 

Where:

topPlateT = $'Top Plate'.Transform

pointBT = $PointBT.Transform

pointA = $PointA.transform.controller.Position

 

Rev A Nozzle Transform Script

ptbw = pointB * topPlateT

uAB = normalize (ptbw - pointA)

xdir = ujFrontT.row2

ydir = uAB

zdir = normalize (cross ydir xdir)

(matrix3 xdir ydir zdir pointA)

 

Where:

pointA = $PointA.transform.controller.Position

pointB = $PointB.transform.controller.Position

topPlateT = $'Top Plate'.Transform

ujFrontT = $'Universal Joint Front'.Transform

 

Now the problem comes when I try to apply this same principle to my model where I want to replace the object Top Frame with the dummy object Dummy-Frame to drive the movement of the nozzle.  So pointB is now linked to Dummy-Frame:

2.jpg

 

Note that Dummy-Frame is at the end of a long chain of linked objects:

3.jpg

 

I think I understand that the problem is perhaps related to getting the correct absolute (world) position of linked objects.

 

So with some googling I found that if you reference the .pos (.row4) part of the transform of an object it should (?) always return the absolute (world) position.  So I tried to revise the first script as:

 

Rev B Universal Joint Rear Transform Script:

uAB = normalize (pointBT.row4 – pointAT.row4)

xdir = pointAT.row1

ydir = normalize (cross uAB xdir)

zdir = normalize (cross xdir ydir)

(matrix3 xdir ydir zdir pointAT.row4)

 

Where:

pointAT = $PointA.Transform

pointBT = $PointB.Transform

 

So instead of using the line:

ptbw = pointB * topPlateT

 

I used pointBT.row4 to get the absolute position of point B – this seems to work OK.

 

So emboldened by this success I pushed on…. I tried this next:

 

Rev B Universal Joint Front Transform Script:

ptbw = pointBT.row4 * frameT.row4

uAB = normalize(pointA - ptbw)

xdir = normalize ((pointBT * frameT).row1)

ydir = normalize (cross xdir uAB)

zdir = normalize (cross xdir ydir)

m = (matrix3 xdir ydir zdir ptbw)

 

Where:

frameT = $'Dummy-Frame'.Transform

pointBT = $PointB.Transform

 

This results in bad stuff happening…

4.jpg

☹️

  • the Nozzle object appears to be flattened on the X-Y plane
  • Universal Joint Front object moves to the same position as Universal Joint Rear (at 0,0,0)
  • Point B follows the movement of Dummy-Frame but the Nozzle does not move at all

 

So obviously I am getting something wrong when trying to use a linked object as the replacement for “Top Plate” but I am not sure why.  There are a couple of other things I do not quite understand in the script also:

 

1. Universal Joint Rear Transform Script:

ptbw = pointB * topPlateT

 

Can you explain a little on this line?  This is multiplying the relative position of pointB by the complete transform of it’s parent object topPlateT right?  Why does this result in the absolute position of point B?

 

2. Rev A Universal Joint Front Transform Script:

ptbw = pointBT.row4 * topPlateT

 

In this case I am confused what this line is doing?  My understanding is that pointBT.row4 should already be the world position of point B (?) – why do you multiple by the full topPlate transform here to get ptbw?

 

I really appreciate your help on this on I learned a lot from your tutorial – I feel I am getting close to understanding how to properly apply this methodology to solve the problem I have but I am not quite there yet….

 

Thanks!! 

0 Likes
Message 9 of 13

peterjung
Enthusiast
Enthusiast

Hmmm....

 

Update from previous post... on closer inspection this is not actually working as I thought.... there is still a twist happening along the nozzles own Y axis:

5.jpg

☹️

 

The proper movement for this nozzle, with the pivot point at the world origin, somehow needs to have rotate around Z axis in world coords but also rotate on X axis on local..... with zero Y rotation

0 Likes
Message 10 of 13

domo.spaji
Advisor
Advisor

Here's the gif showing how ingenious one have to be to rewriting existing, perfectly good, essential controllers...

 

Let's say it really don't work as it should, there's more transforms than we would like to have...

From Max animation and hierarchy basics tools:

All tpots on gif are children of pink half-torus obj. (one with look at animation).

 

Every Teapot get single different track - x,y or z pos. or rotation (as seen on motion paths).

Just as result of linking/hierarchy, nothing else.

 

Inheritance.gif

 

0 Likes
Message 11 of 13

peterjung
Enthusiast
Enthusiast
Accepted solution

So… I finally got this solved!!

 

After some initially promising results I ran into trouble with the vector and transform scripts – when I found I still had the annoying twist problem I got a bit disheartened….

 

So I went back to the beginning and was thinking about how I had tried to apply the “single axis LookAt” method as referenced under Test 3 in my first post on this thread… and I had a minor Euraka moment – here is what I did to get a solution:

 

  • Create Vertical Plane001 – align pivot with world, move to 0,0,0
  • Create dummy object LocalSpaceObjNoz1 - align pivot with world, move to 0,0,0 and Link to Plane001

6.jpg

 

Apply a script controller to the Nozzle object:

  • Select Nozzle  ► Movement panel ► X rotation ► assign controller ► Float Script
  • Create variable LocalSpaceObject and then Assign Node and select LocalSpaceObjNoz1
  • Create variable LookAtObject and then Assign Node and select object Dummy-Frame

Script:

localPos = in coordsys LocalSpaceObject LookAtObject.pos

((atan2 localPos.z -localPos.y) +180) /180 * -pi

 

This takes care of the nozzle rotation in the X direction.

 

For the Z rotation I applied the same technique but this time I assigned the controller to Plane001 and then linked the Nozzle to Plane001 – this was the Euruka moment… i.e.

 

  • Create Horizontal Plane002 – align pivot with world, move to 0,0,0
  • Create Dummy – LocalSpaceObjNoz2 - align pivot with world, move to 0,0,0 and Link to Plane002

7.jpg

This time apply a script controller to the Z rotation of the vertical plane001 created in the previous step

 

  • Select Plane001 ► Movement panel ► Z rotation ► assign controller ► Float Script
  • Create variable LocalSpaceObject and then Assign Node and select LocalSpaceObjNoz2
  • Create variable LookAtObject and then Assign Node and select Dummy-Frame

Script:

localPos = in coordsys LocalSpaceObject LookAtObject.pos

((atan2 localPos.x -localPos.y) +180) /180 * pi

 

Finally Link Nozzle object to Plane001…. and eureka – it works as intended!

 

https://youtu.be/k0vJPFPY3bM

 

For the universal ball joint at the end of the nozzle the inner gimbal (red) is just linked to the Nozzle and the outer ball part is linked to Plane001.

 

For the Frame universal joint the inner gimbal (red) is already linked to the Nozzle.  For the outer ring (green) this is linked to the Frame and then the Z rotation of Plane001 is wired to the Z rotation of the ring.

 

Sincere thanks to all who contributed to this thread and especially to leeminardi – although I couldn’t quite get the vectors and transforms to work quite how I needed I learned a hell of a lot in the process

 

Thanks again!

 

peterjung

0 Likes
Message 12 of 13

leeminardi
Mentor
Mentor

@peterjung I've been busy the past few days and did not have time to review your post.  Glad that you solved the problem on your own.  

lee.minardi
0 Likes
Message 13 of 13

peterjung
Enthusiast
Enthusiast

Thanks Lee !

0 Likes