[SOLVED] Rotation issue being normalized in Jme

I am trying to let my car sping 2.5 revolutions (meaning 900 degrees). I am using Cinematics and the function is:



[java]



public void rotateCars(String id, float trigger_time, float theta, float duration)

{

final Spatial spatialToMove = rootNode.getChild( id);

float drive_rotation = theta; // theta rotation in degrees

float rot_duration = duration; // duration of the rotation

float rot_in_rad = FastMath.PI * drive_rotation / 180; // convert from rad to degrees

Quaternion rotation_angle = new Quaternion().fromAngleAxis(rot_in_rad, Vector3f.UNIT_Z);



// RotationTrack is a pre-existing Cinematic event that rotates the car and its platter

// since the rotation-angle is a rotation in world space we should never rotate the parent (spatialToMove)

// but only the children to avoid animation errors

cinematic.addCinematicEvent(trigger_time, new RotationTrack(((Node) spatialToMove).getChild(0), rotation_angle, rot_duration / (getAnimationSpeed())));

cinematic.addCinematicEvent(trigger_time, new RotationTrack(((Node) spatialToMove).getChild(1), rotation_angle, rot_duration / (getAnimationSpeed())));

}

[/java]



This works fine up to 180 degrees and after that I cannot rotate accurately b/c it seems that the angle is being normalized by the RotationTrack. Is there a work around around that (without having to change the RotationTrack source code)? or having to create an abstractEvent for this?

Apparently that’s a Slerp weirdness, you cannot interpolate over a 180° angle.

A work around would be to split your rotation in pieces of less than 180 (in your example 6 “slices” of 150 for example) and create a cinematic event for each slice.

i tried with 180 but at some point the spatial may rotate the other way round…



I tried it in the cinematic test case.

[java]

Quaternion rotation2 = new Quaternion().fromAngleAxis(150 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);

cinematic.addCinematicEvent(1, new RotationTrack(teapot, rotation2, 2));

Quaternion rotation3 = new Quaternion().fromAngleAxis(300 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);

cinematic.addCinematicEvent(3, new RotationTrack(teapot, rotation3, 2));

Quaternion rotation4 = new Quaternion().fromAngleAxis(450 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);

cinematic.addCinematicEvent(5f, new RotationTrack(teapot, rotation4, 2));

Quaternion rotation5 = new Quaternion().fromAngleAxis(600 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);

cinematic.addCinematicEvent(7f, new RotationTrack(teapot, rotation5, 2));

Quaternion rotation6 = new Quaternion().fromAngleAxis(750 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);

cinematic.addCinematicEvent(9f, new RotationTrack(teapot, rotation6, 2));

Quaternion rotation7 = new Quaternion().fromAngleAxis(900 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);

cinematic.addCinematicEvent(11f, new RotationTrack(teapot, rotation6, 2));

[/java]



that’s quite an ugly solution though…

I’ll look into the slerp method, but the math it involves are a bit over my head…

1 Like

But even that doesn’t fix it. Here’s my code:



[java]

public void rotateCars(String id, float trigger_time, float theta, float duration)

{

final Spatial spatialToMove = rootNode.getChild( id);

float drive_rotation = theta; // theta rotation in degrees

float rot_duration = duration; // duration of the rotation

float rot_in_rad = FastMath.PI * drive_rotation / 180; // convert from rad to degrees

Quaternion rotation_angle = new Quaternion().fromAngleAxis(rot_in_rad, Vector3f.UNIT_Z);

float slerp_duration = 1.14f;



if(Math.abs(drive_rotation - 900f) < epsilon)

{

Quaternion rotation2 = new Quaternion().fromAngleAxis(150 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);

cinematic.addCinematicEvent(trigger_time, new RotationTrack(((Node) spatialToMove).getChild(0), rotation_angle, slerp_duration / (getAnimationSpeed())));

cinematic.addCinematicEvent(trigger_time, new RotationTrack(((Node) spatialToMove).getChild(1), rotation_angle, slerp_duration / (getAnimationSpeed())));



Quaternion rotation3 = new Quaternion().fromAngleAxis(300 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);

cinematic.addCinematicEvent(trigger_time + 1.14f, new RotationTrack(((Node) spatialToMove).getChild(0), rotation_angle, slerp_duration / (getAnimationSpeed())));

cinematic.addCinematicEvent(trigger_time + 1.14f, new RotationTrack(((Node) spatialToMove).getChild(1), rotation_angle, slerp_duration / (getAnimationSpeed())));



Quaternion rotation4 = new Quaternion().fromAngleAxis(450 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);

cinematic.addCinematicEvent(trigger_time + 2.28f, new RotationTrack(((Node) spatialToMove).getChild(0), rotation_angle, slerp_duration / (getAnimationSpeed())));

cinematic.addCinematicEvent(trigger_time + 2.28f, new RotationTrack(((Node) spatialToMove).getChild(1), rotation_angle, slerp_duration / (getAnimationSpeed())));



Quaternion rotation5 = new Quaternion().fromAngleAxis(600 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);

cinematic.addCinematicEvent(trigger_time + 3.42f, new RotationTrack(((Node) spatialToMove).getChild(0), rotation_angle, slerp_duration / (getAnimationSpeed())));

cinematic.addCinematicEvent(trigger_time + 3.42f, new RotationTrack(((Node) spatialToMove).getChild(1), rotation_angle, slerp_duration / (getAnimationSpeed())));



Quaternion rotation6 = new Quaternion().fromAngleAxis(750 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);

cinematic.addCinematicEvent(trigger_time + 4.56f, new RotationTrack(((Node) spatialToMove).getChild(0), rotation_angle, slerp_duration / (getAnimationSpeed())));

cinematic.addCinematicEvent(trigger_time + 4.56f, new RotationTrack(((Node) spatialToMove).getChild(1), rotation_angle, slerp_duration / (getAnimationSpeed())));



Quaternion rotation7 = new Quaternion().fromAngleAxis(900 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);

cinematic.addCinematicEvent(trigger_time + 5.7f, new RotationTrack(((Node) spatialToMove).getChild(0), rotation_angle, slerp_duration / (getAnimationSpeed())));

cinematic.addCinematicEvent(trigger_time + 5.7f, new RotationTrack(((Node) spatialToMove).getChild(1), rotation_angle, slerp_duration / (getAnimationSpeed())));



}

else{

cinematic.addCinematicEvent(trigger_time, new RotationTrack(((Node) spatialToMove).getChild(0), rotation_angle, rot_duration / (getAnimationSpeed())));

cinematic.addCinematicEvent(trigger_time, new RotationTrack(((Node) spatialToMove).getChild(1), rotation_angle, rot_duration / (getAnimationSpeed())));}

}[/java]

it only does a 180

A quaternion is an orientation… not an arbitrary rotation. It can’t keep track of how many revolutions around the axis you say so it effectively normalizes rotations above 180. Since a rotation of 190 is really a rotation of -170.



If RotationTrack is using Quaternions as its start and end then it will always be limited.



In your second case you are still giving it absolute rotations… you need to work out what the smaller step relative rotations would be and use those instead. So the first step could take it up to 180 and the next step could take it from -180 to 0 or something.



Actually, your second case is doing that but you aren’t using your intermediate values. You are passing the one main values from up in the class fields. rotation_angle versus rotation2 for example.

1 Like
@pspeed said:
A quaternion is an orientation... not an arbitrary rotation. It can't keep track of how many revolutions around the axis you say so it effectively normalizes rotations above 180. Since a rotation of 190 is really a rotation of -170.

If RotationTrack is using Quaternions as its start and end then it will always be limited.

In your second case you are still giving it absolute rotations... you need to work out what the smaller step relative rotations would be and use those instead. So the first step could take it up to 180 and the next step could take it from -180 to 0 or something.

Actually, your second case is doing that but you aren't using your intermediate values. You are passing the one main values from up in the class fields. rotation_angle versus rotation2 for example.

Yeah Paul's right you don't use the good variable when you schedule the events.

RotationTrack is deprecated, the preferred way would be to use SpatiaAnimation for this kind of things. I'll work on an AnimationHelper because the API of spatial animation is horrible...

Thanks guys,



before I call a rotationTrack event I have a MotionHelper function that calculates which rotation is the smallest and chooses it. So whenever the angle is less than 180 I’m good. Also that function does the necessary calculations to make a drive with current angle say 180 rotate another 180 and does the normalization. This is all working fine.



the issue is that I need the spatial to rotate 2.5 revolutions for a certain motion state. I don’t care if it is ugly as long as it does the job using maybe a simple [java]if (theta == 900) { //do ugly code here}[/java]



Is there any work around without having to use SpatialAnimation?

your code should work if you use rotation2…7 instead of rotation_angle

but I need the rotation_angle for cases that is not a 900 degrees rotation. I need both cases.

Dude…do you realize what you did there?

Code:
Quaternion rotation2 = new Quaternion().fromAngleAxis(150 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y); cinematic.addCinematicEvent(trigger_time, new RotationTrack(((Node) spatialToMove).getChild(0), rotation_angle, slerp_duration / (getAnimationSpeed()))); cinematic.addCinematicEvent(trigger_time, new RotationTrack(((Node) spatialToMove).getChild(1), rotation_angle, slerp_duration / (getAnimationSpeed())));
you create a rotation2 variable....but you never use it...
My point,(and Paul's) is that in the fist part of your if statement you have to use the rotations you create for it to work.
The second part of the if statement is the fall back when the rotation is below 180°

you have your drive_rotation variable that is in degrees, if it over 180 do the first part of your if statement by slicing the rotation in pieces of 150°.
else...do with the rotation_angle
1 Like

oh man!! how did I miss that??



it works fine now… and as I said for the 2nd part I have a function that takes care of that before passing it to Cinematics so I should be fine.



thanks a lot guys

@nehon btw I still get the same weird behavior when I try to increase the speed, I think like you said in the other post it’s because of my AccerelateTrack… Did you have time to take a look at it?



Thanks

I just used the function provided and it works, so it’s definitely something else.