Standard Align tool vs Max Script! Score is 2 : 1 ))


#1

Hello folks!
Want to talk about of align animation result between align tool and maxscript metod.
Look at the attached file and you will see that the standard alignment tool always gives a more accurate result in animation. Getting a similar result using the max script command like

A.transform = B.transform

does not always work.

Of course, before creating a topic, I did a lot of searching on this issue, and found this topic with an example from PEN - Paul Neale

b=Box pos:[-20,0,0]
b.transform=(matrix3 [0.904582,-0.426096,0.0132061] [0.35901,0.744726,-0.562579] [0.229877,0.51364,0.826638] [-20,0,0])
s=sphere segments:4 pos:[20,0,0]

–Rotate the spheres matrix around it’s own position.
s.transform=(b.transform.rotationPart as matrix3*transMatrix s.transform.pos)

But alas, code written based on PEN example

A.transform = (B.transform.rotationPart as matrix3*transMatrix B.transform.pos)

gives the same result as this code

A.transform = B.transform

Look animation in attached max file.

In general, of course, we have a method for getting the same result as when using the align tool. This method is based on obtaining an angle of three points for each axis (XYZ) How do I find the angle between 3 vertices? And after we get all the angles, we will have to do some calculations between the old and new angles to get the angular difference and determine the rotation vector based on the near angle. So in my head, this code takes at least 42 lines and I thought that it is very much for such a task.

So, I am sure that experts and transform matrix gurus know the shortest path for this task solution :smiley: And I hope they have some free time to share their examples and tips with us :pray: :sunglasses:
Scene_For_CGS.max (460 KB)


#2

This is an interpolation issue between keys when using that math.

It is interesting because if you delete the X and Z rotation keys at frame 60 it works as it should .Some how it is interpolating the Euler into a Gimbal Lock situation. (http://paulneale.com/gimbal-lock/)

I wish I had time to play with this and have a look at how they are doing the conversion from Matrix to Euler. There must be something different happening there as you can see by looking at the function curves for the Max Align Tool solution as it is pushing it into Gimbal but not spinning it around while doing so.


#3

You absolutely right Paul

It would be great :smiley:

Yes, they obviously use a more cunning algorithm. :smiley:

In the next few days I will try to write my terrible code in 42 lines and if it works, I’ll post it here. :grin:

By the way thanks for this useful link :+1:
http://paulneale.com/gimbal-lock/


#4

Would the not be effectively using a euler filter algorithm to unravel the rotations?


#5

Thanks for the link, I will learn it.
If the actions described there have the ability to run and done via a max script, then this may be one of the solutions, otherwise this method does not suit us.


#6

ok … i finally found some free time to take a look into the issue and ready to give some explanations.

you have to understand that matrix3 rotation part in represented as quat value. Which can give many (actually 12 as i remember) different combinations of euler angles.

the animation interpolation is easy for linear values… and taking into account our Euler XYZ controllers the best situation for us will be to interpolate every float rotation part as a linear value separately.

When the Align tool sets corresponding values it uses (if possible) the every component of rotation controller. Which means in our case it sets X,Y, and Z rotation angles separately.

So we can do the same because our rotation controllers are Euler XYZ

with animate on
(
	at time 0
	(
		$Point005.rotation = eulerangles 0 0 0
	)
	at time 20
	(
		x = $Tar_Point_015.rotation.controller[1].value
		y = $Tar_Point_015.rotation.controller[2].value
		z = $Tar_Point_015.rotation.controller[3].value
		p = $Tar_Point_015.position

		$Point005.rotation.controller[1].value = x
		$Point005.rotation.controller[2].value = y
		$Point005.rotation.controller[3].value = z
		$Point005.position = p
	)
	at time 40
	(
		x = $Tar_Point_014.rotation.controller[1].value
		y = $Tar_Point_014.rotation.controller[2].value
		z = $Tar_Point_014.rotation.controller[3].value
		p = $Tar_Point_014.position

		$Point005.rotation.controller[1].value = x
		$Point005.rotation.controller[2].value = y
		$Point005.rotation.controller[3].value = z
		$Point005.position = p
	)
	at time 60
	(
		x = $Tar_Point_013.rotation.controller[1].value
		y = $Tar_Point_013.rotation.controller[2].value
		z = $Tar_Point_013.rotation.controller[3].value
		p = $Tar_Point_013.position

		$Point005.rotation.controller[1].value = x
		$Point005.rotation.controller[2].value = y
		$Point005.rotation.controller[3].value = z
		$Point005.position = p
	)
)

yeh… it looks pretty ugly from code point of view. Let’s clean it up:

fn getNodeTransformData n = 
(
	x = n.rotation.controller[1].value
	y = n.rotation.controller[2].value
	z = n.rotation.controller[3].value
	p = n.position	
	#(p, eulerangles x y z)
)
fn setNodeTransformData n d = 
(
	n.rotation.controller[1].value = d[2].x
	n.rotation.controller[2].value = d[2].y
	n.rotation.controller[3].value = d[2].z
	n.position = d[1]
)

fn alignNodeTransforms source target = 
(
	d = getNodeTransformData target
	setNodeTransformData source d
)

with animate on
(
	at time 0
	(
		$Point005.rotation = eulerangles 0 0 0
	)
	at time 20
	(
		alignNodeTransforms $Point005 $Tar_Point_015
	)
	at time 40
	(
		alignNodeTransforms $Point005 $Tar_Point_014
	)
	at time 60
	(
		alignNodeTransforms $Point005 $Tar_Point_013
	)
)

now the question is - What can we do if our transform controllers are Transforms Scripts for example and we can’t interpolate their float components separately?

Honestly, we can’t do much with it to make it always right. The Transform controller in this case has to implement its own “matrix3 interpolation” method. Which is in case of Besier Rotation is …
oh… i don’t want to talk too much about it. Just can recommend to read the explanation of quatArrayToEulerArray mxs method in the Help.

the MXS provides another methods to make the sequence of quat values “interpolatable” as euler angles.
see:
GetEulerQuatAngleRatio
GetEulerMatAngleRatio

that’s how the max animation system works.

Good luck!


#7

Thank you very much DenisT I agree with everything said, and i tried your script.
So much better than “A.transform = B.transform” but the alignment tool is still better ))


Scene_For_CGS_02.max (288 KB)

By the way, thanks for the hint about “GetEulerQuatAngleRatio” but now I’m not quite clear how to use the returned result? :thinking:

> A = $Tar_Point_014 
> B = $Tar_Point_013
> 
> getEulerQuatAngleRatio A.rotation B.rotation (A.rotation as eulerAngles) (B.rotation as eulerAngles) 
> 
> -- returned 2.3806

And I also found 2 topics with similar themes . :smiley:



#8

There is no such thing as “better or worse.” All is useful for its own job. If the Align Tool gives a different result, it only means that we are doing something wrong.


#9

… deleted for review


#10

You absolutely right :+1:


#11

I know that this is not the solution but may rings any bell to find it
The scene

--your Scene
delete objects
AnimPt =	Point  size:15 cross:off axistripod:on cross:off Box:on centermarker:off wirecolor:yellow
AnimPt2 =	Point  size:15 cross:off axistripod:on cross:off Box:on centermarker:off wirecolor:yellow
AnimPt2[3][2].Controller.AxisOrder=5	
TransPt1 =	Point  size:10 cross:off axistripod:on cross:off Box:on centermarker:off wirecolor:green transform:(matrix3 [-1,0,0] [0,-1,0] [0,0,1] [0,0,30])
TransPt2 =	Point  size:10 cross:off axistripod:on cross:off Box:on centermarker:off wirecolor:green transform:(matrix3 [-0.707107,0,0.707107] [0,-1,0] [0.707107,0,0.707107] [20,0,20])
TransPt3 =	Point  size:10 cross:off axistripod:on cross:off Box:on centermarker:off wirecolor:green transform:(matrix3 [0,0,1] [0,-1,0] [1,0,0] [30,0,0])

And now try to change the axis order to sth and align it then back to the initial axis order

with animate on
(
	at time 0
	(
		AnimPt.transform = AnimPt.transform
		AnimPt2.transform = AnimPt2.transform		
	)
	at time 20
	(
		AnimPt[3][2].Controller.AxisOrder=4	
		AnimPt.transform= TransPt1.transform
		AnimPt[3][2].Controller.AxisOrder=1
--
		AnimPt2[3][2].Controller.AxisOrder=4	
		AnimPt2.transform= TransPt1.transform
		AnimPt2[3][2].Controller.AxisOrder=5		
	)
	at time 40
	(
		AnimPt[3][2].Controller.AxisOrder=4	
		AnimPt.transform= TransPt2.transform
		AnimPt[3][2].Controller.AxisOrder=1
--
		AnimPt2[3][2].Controller.AxisOrder=4	
		AnimPt2.transform= TransPt2.transform
		AnimPt2[3][2].Controller.AxisOrder=5	
	)
	at time 60
	(
		AnimPt[3][2].Controller.AxisOrder=4	
		AnimPt.transform= TransPt3.transform
		AnimPt[3][2].Controller.AxisOrder=1
--
		AnimPt2[3][2].Controller.AxisOrder=4	
		AnimPt2.transform= TransPt3.transform
		AnimPt2[3][2].Controller.AxisOrder=5	
	)
)

The point with axisorder of 1 it works just like the max default align tools
but
for the pt with axisorder of 5 again max align works better


#12

Any way thanks for your example.
I learn something new of maxscript from all comments )

As I understand your example fits, if we initially know which axis will rotate, but as you know, we need a method that can automatically determine the angles and the axes corresponding to these angles.

Actually at the moment I already have a draft script, which in most situations gives the same result as the standard alignment tool, but sometimes it still gives incorrect results. I think I know the reason for these errors, and if I can solve them, I will of course post the script here.


#13

Or maybe you could make cylinder, rotate it +/-90° on Y and than try to rotate it like wheel…
Than create it in left or right view an try again…

After all that examine all yours points in gimbal or parent coordinate system, see what method give more accurate result…
Than open track view and look at the values there…
While you are there copy some PRS controllers and paste them on other objects, or just copy yours object an paste it to another (target) object…
That would be the same as Denis method