Notifications
Clear all

[Closed] Deriving direction vector from rotation keyframes

Hi Everyone,

I have a sphere with a rotation key on frames 0 and 1. I need to find the direction in which the sphere should roll based on these two values (assuming it’s sitting on a flat plane – no Z translation). I’m using max 9 with Productivity Booster.

I’m currently using the following code with limited success – it seems to work as long as the rotation is confined to a single world space axis (1,0,0) or (0,1,0). I get unpredictable results when the rotation is along more than 1 axis.

–code–
animate on
obj = $sphere01
rad = obj.radius*2
r0 = at time 0 obj.rotation
r1 = at time 1 obj.rotation
rotDif = r1 – r0
moveAxis = normalize (cross r1.axis [0,0,1]) – I think this may be where the problem is
toMove = (rad/360)rotDif.angle
at time 1 $.pos += moveAxis
toMove

Any help/suggestions would be greatly appreciated.

Thanks in advance

9 Replies

You can get the direction with a single rotation value. From there you can calculate the X and Y position of where the sphere should be in the next frame given a certain speed.

I'll use the Z rotation only to steer the sphere. I'll use the X rotation to roll the ball, with the default setting this will avoid gimbal lock if you leave the Y rotation alone.

Say the sphere has a fixed Z rotation value of 30°. If we leave it at that it will move in that direction forever. So, with the degrees set we have a direction vector with no coordinates, just an angle.
To get the coordinates you can use cos and sin:

posX = cos Z_Angle = cos 30  = 0.87
posY = sin Z_Angle = sin 30  = 0.5

These are the normalised vector coordinates. To figure out by how much you should translate your sphere you need to know the speed. Say we have a speed of 10 units per frame. Now just multiply the vector coords with the speed to get the translation:

transX = posX * 10  = 8.7
transY = posY * 10  = 5

Just add these values to the current position and apply it on the next frame. If your sphere was at (3 , 2 , 0) it should be at (11.7 , 7 , 0) the next frame.

Now we still need to add 90 degrees to the Z_Angle, otherwise it'll head off in the wrong direction. Anyway this gives us:

transX = ( cos (Z_Angle + 90) ) * Speed
transX = ( sin (Z_Angle + 90) ) * Speed


Here's a little script to demostrate this, it'll roll the ball as well. Just key in some rotations on the Z rotation beforehand and run the script.
I've assumed you have a sphere called Sphere01 with a radius of 30.

     myObj = $Sphere01
     
     tStart = 1
     tEnd = 99
  
     -- X&Y Pos.
     at time tStart oPosX = myObj.pos.x
     at time tStart oPosY = myObj.pos.y
     
     -- X axis rotation.
     at time tStart oRotX = myObj.rotation.x_rotation
     
     -- Radius/Circumference/Speed
     oRds = 30
     oCrc = 2 * pi * oRds
     oSpd = 10
     
     -- Add new keys.
     rotXCtrl = myobj.rotation.x_rotation.controller
     posXCtrl = myobj.position.x_position.controller
     posYCtrl = myobj.position.y_position.controller
     
     for t = tStart to (tEnd+2) do
     (
     	addNewKey rotXCtrl t
     	addNewKey posXCtrl t
     	addNewKey posYCtrl t
     )
     
     
     -- Roll speed is always the same with fixed translation speed.
     oRotXDelta = -(oSpd/oCrc) * 360
     
     -- For loop start.
     for t = tStart to tEnd do
     (
     	-- Z axis rotation. Z adjusted to fit coord sys. 
     	at time t oRotZ = myObj.rotation.z_rotation + 90
     	
     	-- Get the XY direction vector with cos and sin.
     	-- Multiply with speed to get the translation for this frame.
     	at time t oTransX = oSpd * (cos oRotZ)
     	at time t oTransY = oSpd * (sin oRotZ)
     	
     	-- Update values.
     	oPosX += oTransX
     	oPosY += oTransY
     	oRotX += oRotXDelta
     	
     	-- Insert your curve normalisation here. Optional.
     	--if oRotX < -360 do 
     	--(
     	--	oRotX += 360
     	--)
     
     	-- Set keys. t+1 for array compensation and t+1 because
     	-- it should be applied at the next frame.
     	t2 = t + 2
     	myobj.position.x_position.controller.keys[t2].value = oPosX
     	myobj.position.y_position.controller.keys[t2].value = oPosY
     	myobj.rotation.x_rotation.controller.keys[t2].value = oRotX
     )
     -- For loop end.
     
Good luck.

I read your post again, are you trying to do this with two or three axes then? I think that with two you could use a similar method to calculate a 3D vector for each axis, add them, and then take the X and Y values of the resulting vector. Not sure though.

 rdg

Do you want to create a rollingball and instead of moving the ball and deriving its rotation, you want to rotate theball and calculate the position?

Just a quick guess:
You should treat the rotations a quaternions – It should be possible to eytract the axis the ball is rotating about and the direction is perpendicular to this axis.

Maybe this helps.

Georg

You could get the axis around which the object is rotating with angleAxis. The direction vector would be perpendicular but I’m not sure how to calculate it from the rotation axis.

 rdg

direction = cross rotationAxis [0, 1, 0]
the cross product of the (normalised) rotationAxis and the up-vector gives you the direction.

Georg

Guys,

Firstly, many thanks for taking the time to respond – it’s really appreciated and apologies for not answering your questions sooner – I started this thread before leaving work on Friday and have only just been able to check back.

Let me give you some more detail on what I want to achieve. I have a spherical character and the tool I described in my first post is just one of the options I want to implement to animate the character – the other tools are working already and are as follows:-

I can keyframe the position of the sphere’s parent and then generate keyframes to roll the sphere.

I can enter a number of revolutions the I want the sphere to roll, pick an axis and generate keyframes for the parent’s position as well as the sphere’s rotation

I can set a rotation key on the same frame as the parent’s final position key and then generate rotation keyframes for the sphere going back to the parent’s first position keyframe.

The final piece of the puzzle is the one I’m having trouble with. It needs to be able to rotate along 3 axis if required so I think quats are definately the way forward.

Do you want to create a rollingball and instead of moving the ball and deriving its rotation, you want to rotate theball and calculate the position?

Just a quick guess:
You should treat the rotations a quaternions – It should be possible to eytract the axis the ball is rotating about and the direction is perpendicular to this axis.

This is exactly what I’m after and I seem to be doing as you suggest. I think the problem may be that I need to move the sphere’s parent rather than the sphere itself – this works fine if the sphere is aligned to its parent along the Z axis. For example, If the sphere starts out aligned to its parent on frame zero and I set a keyframe at frame 30 rotating the sphere 90 degrees in Y, the parent translates correctly along its X axis. However if both the sphere’s rotation is offset along the Z axis by 45 degrees the translation of the parent stays the same – along its X axis. This may be to do with the coordinate system I’m applying the transforms in? I’ve attached a simple scene to demonstrate the problem.

roll_demo.zip

Thanks again for all your time and input…

OK I think I’ve got it working… finally!

This is the code I now using – $sphere01 is the child of $Dummy01

parent = $Dummy01
obj = $Sphere01
fStart = 0
fEnd = 30
rangeLength = fEnd – fStart
rad = 10
circ = (2pirad)
deg = circ/360 – distance travelled during a 1 degree rotation

for i = 0 to rangeLength do
(
r0 = at time (fStart + (i-1)) in coordsys world obj.rotation
r1 = at time (fStart + i) in coordsys world obj.rotation
rotDif = r1 – r0 – angle from previous frame to current one
objMatrix = at time (fStart + (i-1)) obj.transform – local coordinate system of obj on previous frame
x = normalize (cross objMatrix[2] [0,0,1]) – *
y = normalize (cross x [0,0,1])————- constructing coordinate space
z = [0,0,1] ——————————— for final transform
moveMatrix = matrix3 x y z [0,0,0] ———-
*
moveDist = degrotDif.angle – distance to move
moveAxis = cross rotDif.axis [0,0,1] – multiplier for distance along each axis
moveAxis[1] = moveAxis[1]
-1 – flips x value (not sure why this is needed!)
at time (fStart + i) in coordsys moveMatrix parent.pos -= moveDist*moveAxis – Applies final transform
at time (fEnd) parent.pos = at time (fStart + i) parent.pos
) – end for

I’d still be interested if anyone has a more elegant solution to this – Thanks again to Rens and rdg for your input

Duncs

Cuneyt Ozdas wrote a scripting tutorial for rolling a ball which can be found here. Hopefully that will help you with some of your questions, if you have any left.

-Eric

Cheers Eric, I must admit, in the early stages of this project the rolling ball tutorial was a lifesaver. That along with the tutorials on Paul Neale’s site really helped me get this far.

Thanks for posting

Duncs