# Limit amount of rotation

hi there, i've been making one of my object to start rotating upon key press.
when i press a key (once) the object start rotating.
so far so good, but, i wanted the object to stop rotation when i decide it.
is there's any way to setup something like:  " rotate until reaching 90deg from original state"
or a different angle too, i could need my object to rotate 60deg, or 120deg .. or any other target angle..

looking at jme-test's TestTube.java
i've found the following very interesting:

``` [.....]     private Quaternion rotQuat = new Quaternion();     private float angle = 0;     private Vector3f axis = new Vector3f(1, 1, 0).normalizeLocal();     private Tube t; [.....]     protected void simpleUpdate() {         if (timer.getTimePerFrame() < 1) {             angle = angle + (timer.getTimePerFrame() * 1);             if (angle > 360) {                 angle = 0;             }         }         rotQuat.fromAngleNormalAxis(angle, axis);         t.setLocalRotation(rotQuat);     } [.....] ```

looking at TestTube.java example, i've done the same in my own project, but, i've found the float "angle" misleading.
unless i'm wrong "angle is just a timer. here the rotation will not perform until it reaches 360deg
but instead, the rotation will continue until the timer reach 360.
if i set: if (angle > 2) { ...} the rotation will happen until "angle timer = 2, which isn't a 2deg angle..

how can i setup a real angle based limit for my rotation ? (keep rotating until 90deg or 120deg whatever is reached for example)

tnx

There is a bug in the test by the looks of it.

fromAngleNormalAxis takes an angle in radians not degrees.

If using degrees (as it is obviously meant to), multiply by FastMath.DEG_TO_RAD

This will make the rotation really slow so probably make that 1 into a 10 to speed it up.

I think this is roughly what it should be:

``` [.....]     private Quaternion rotQuat = new Quaternion();     private float angle = 0;     private Vector3f axis = new Vector3f(1, 1, 0).normalizeLocal();     private Tube t; [.....]     protected void simpleUpdate() {         if (timer.getTimePerFrame() < 1) {             angle = angle + (timer.getTimePerFrame() * 10);             if (angle > 360) {                 angle = 0;             }         }         rotQuat.fromAngleNormalAxis(angle * FastMath.DEG_TO_RAD, axis);         t.setLocalRotation(rotQuat);     } [.....] ```

The basic idea is fine - multiplying by the timer is just to make sure the rotation speed is not tied to your framerate.

wow that seems to work real fine,

i've just had a quick test but thanks !

now i can decide the target rotation angle and have it stops when it reaches it…

great !  :mrgreen:

any idea how to overcome the slowliness though?

i did that:

angle = angle + (timer.getTimePerFrame() * 20);

but i'm nore sure i should do it like this ?

or it's ok?

btw,  *20 is still a bill slow, *40 would be fine i guess…

Yes you can use any number like that.

It works like this:

If you do angle = angle + 10, you are saying every frame rotate the object by 10 degrees.

But that means that on a slow computer the object will rotate slowly, and on a fast computer it could rotate really fast.

timer.getTimePerFrame() gives you the time per frame as a fraction of one second.

So if you multiply by that you are now doing degrees per second instead of degrees per frame. You get the same speed of movement even with different frame rates.

ie.

`angle = angle + (timer.getTimePerFrame() * degreesPerSecond);`

@alric

tnx for the explanations

i have it working now except for 1 thing, the rotation doesn't use the object current rotation status,

when i press my key, first the object rotation status reset to default then rotation starts and when it reaches the target angle,

instead of staying there, it finaly revert back to rotation status before rotation started.

to try to make myself clearer:

when i start my project, the object is facing west, so in init() i've put that:

b.getLocalRotation().set(new Quaternion().fromAngleAxis( - FastMath.PI/2, new Vector3f(0,1,0)) );

so the object faces now north, which is what i wanted.

i can move and all , it works fine. then i press key to start rotation and the following happens:

oject reset to facing west (original rotation status before b.getLocalRotation().set(…)),

starts a 90deg rotation towards south then when this is done,

the object revert back to rotation status before rotation began: facing north ( b.getLocalRotation().set(…) ).

how can i make my rotation to begin depending current rotation status ?

i though i could put instead of "axis" b.getLocalRotation() like:

but because, b.getLocalRotation() return Quaternion

and Quaternion want vector as arguments.

( rotQuat.fromAngleNormalAxis(angle* FastMath.DEG_TO_RAD, axis);    axis is vector )

i cannot seem to be allowed to put b.getLocalrotation() here instead of axis

or am i completely wrong, this wouldn't work anyway?

how could i make my rotation starts from object's current rotation status?

tnx

There's a few ways you could do that. Probably the simplest for many games is just to store the current rotation of your <whatever>. So then you just add that to the angle you are rotating by.

b.getLocalRotation().set(rotQuat);

use

``` b.getLocalRotation().multLocal(rotQuat); ```

This will rotate it from where it already is, so you would want to use the same angle every time or it will get faster and faster. The problem with this way is that you don't know what the angle is right now.

Another option is slerp. This lets you specify a start and end rotation, and how far between them you are.

``` b.getLocalRotation().slerp(startQuat, endQuat, 0.5f); ```

Would put you half way between the start and end.

thanks!

i tryed the b.getLocalRotation().multLocal(rotQuat);

but couldn't find a way to make the rotation stop… it kept rotating again and again.

and for reducing the speed i had to use:

angle = angle + (timer.getTimePerFrame() * 0.001f);

if there was a way to make it stop at a defined target angle that would be cool

b.getLocalRotation().slerp(startQuat, endQuat, 0.5f);

how would you setup startQuat and endQuat to, in order to make a 90deg rotation ? (or other angles for example?)

i tryed this: (among other failled attempts)

Quaternion startQuat=b.getLocalRotation();

Quaternion endQuat=b.getLocalRotation().set(new Quaternion().fromAngleAxis( - FastMath.PI/2, new Vector3f(0,1,0)) );

b is my box object. i tryed to put it's current rotation status in startQuat

and then in endQuat an attempt at doing  90deg  …

it didn't seemed to work, it fact it did doing at all… no rotation

could you advice some way of setting up thoses 2 quaternion? (startQuat and endQuat) … what should i put in there?

tnx

I think you just need to change that third parameter. It is how far through the movement you are. 0 is just started, 1 is finished, 0.5 is half way etc. With slerp you update that instead of keeping track of the angle.

Try something like this to get the initial values (only done once, at the start of the turn):

``` initialRotation = b.getLocalRotation(); turn90.fromAngleAxis(90 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y); eventualRotation = initialRotation.mult(turn90); float turnAmount = 0; ```

Then something like this each update:

``` if(timer.getTimePerFrame() < 1f) turnAmount += timer.getTimePerFrame(); else turnAmount = 0; b.getLocalRotation().slerp(initialRotation, eventualRotation, turnAmount); ```

I haven't tried that but it should be about right.

i tryed like this:

```     private Quaternion initialRotation = new Quaternion();     private Quaternion turn90 = new Quaternion();     private Quaternion eventualRotation = new Quaternion();     private float turnAmount = 0; [.....]             if (KeyBindingManager.getKeyBindingManager().isValidCommand("rotateRight", false))  {                 rotateRight=true;                 initialRotation = b.getLocalRotation();                 turn90.fromAngleAxis(90 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);                 eventualRotation = initialRotation.mult(turn90);                            }             if (rotateRight){                 rotateLeft=false;                 if(timer.getTimePerFrame() < 1f) turnAmount += timer.getTimePerFrame(); else turnAmount = 0;                 b.getLocalRotation().slerp(initialRotation, eventualRotation, turnAmount);             } ```

it works well but only 3 or 4 times.. the 5th time i press the key it goes bad.
it starts spinning like mad, or just go outside the boundaries of the level..

how i can tell when a rotation has finished so i can turn my flag off ?

i need to set rotateRight=false; at some point…

also i see speed increase each time i rotate…

maybe it's because i still have the rotateRight flag which is never turned off?

tnx

i tried a few things and this worked for me:

• when you press A or D, set a variable toRotate to 90 and isRotationg to true
• in the update cycle, check if you are rotating, if yes, calculate the amount of degree you rotate in this update cycle
• subtract this from the toRotate value
• create a new quaternion with this amount negative or positive depending on the direction and apply it to your char

```             // left rotation code             if (KeyBindingManager.getKeyBindingManager().isValidCommand("rotateLeft", false)) {                 if (isRotating) {                     System.out.println("ignoring keypress while rotating");                 } else {                     isRotating = true;                     toRotate = ROTATION_STEP;  // 90 or 45                     dir = Direction.LEFT;                 }             } ```
```             if (isRotating) {                 float value = 0;                 // look in which direction we turn, and calculate                 // how much we should turn in this update cycle                 if (dir == Direction.RIGHT) {                     value = rotationSpeed*tpf*-1;                     toRotate += value;                 } else {                     value = rotationSpeed*tpf;                     toRotate -= value;                 }                 System.out.println("Degrees toRotate: " +toRotate);                                 // have we finished rotating ?                 if (toRotate <= 0) {                     // rotation has reached its end                     isRotating = false;                 } else {                     // not yet reached the end position, continue rotation                     // create a Quaternion which represents the Rotation                     // for this Cycle                     tmpAngles[0] = 0;                            // X                     tmpAngles[1] = value * FastMath.DEG_TO_RAD;  // Y                     tmpAngles[2] = 0;                            // Z                     tmpRotationQuat.fromAngles(tmpAngles);                                         // multiply the players current rotation with the rotation                     // we want to do in this update cycle                     playerNode.getLocalRotation().multLocal(tmpRotationQuat);                 }             } ```
```import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; import com.jme.app.SimpleGame; import com.jme.bounding.BoundingBox; import com.jme.bounding.CollisionTree; import com.jme.bounding.CollisionTreeManager; import com.jme.input.KeyBindingManager; import com.jme.input.KeyInput; import com.jme.intersection.CollisionData; import com.jme.intersection.CollisionResults; import com.jme.intersection.TriangleCollisionResults; import com.jme.math.FastMath; import com.jme.math.Quaternion; import com.jme.math.Vector3f; import com.jme.scene.CameraNode; import com.jme.scene.Node; import com.jme.scene.Spatial; import com.jme.scene.shape.Box; import com.jme.util.export.binary.BinaryImporter; import com.jmex.model.converters.FormatConverter; import com.jmex.model.converters.ObjToJme; public class HelloModelLoading extends SimpleGame {     private final float ROTATION_STEP = 45;         private Node playerNode = new Node("player Node");     private Spatial map;     private CollisionResults results;     private CollisionData oldData;     // player run speed     private float playerSpeed = 3f;     // player rotation speed     private float rotationSpeed = 100f;         private Vector3f lastPosition;     // temporary float array to store rotation angles     private float[] tmpAngles = new float[3];     // temporary quaternion used to rotate     private Quaternion tmpRotationQuat = new Quaternion();     // are we rotationg currently ?     private boolean isRotating = false;     // degrees to rotate     private float toRotate = 0;         private enum Direction {         RIGHT,         LEFT;     }     Direction dir;         public static void main(String[] args) {         HelloModelLoading app = new HelloModelLoading();         app.setConfigShowMode(ConfigShowMode.AlwaysShow);         Logger.getLogger("").setLevel(Level.SEVERE);         app.start();     }         protected void simpleInitGame() {          display.setTitle("test dungeon");         CollisionTreeManager.getInstance().setTreeType(CollisionTree.Type.AABB);         results = new TriangleCollisionResults();              URL folder= HelloModelLoading.class.getClassLoader().getResource("data/");         URL model = HelloModelLoading.class.getClassLoader().getResource("data/test01.obj");         FormatConverter converter=new ObjToJme();         converter.setProperty("mtllib", folder);         converter.setProperty("texdir", folder);         ByteArrayOutputStream BO=new ByteArrayOutputStream();                 try {             //mapNode = new Node("map node");             converter.convert(model.openStream(), BO);             map=(Spatial) BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));             map.setLocalScale(.1f);             map.setModelBound(new BoundingBox());             map.updateModelBound();             //player = new Cylinder("box", 10, 25, 1, 1,true);             //player.setLocalRotation( new Quaternion().fromAngleAxis( - FastMath.PI/2, new Vector3f(1,0,0)) );             Spatial player = new Box("box", new Vector3f(0,5,0), 1, 1 ,1);             player.setLocalScale(0.1f);             player.setModelBound(new BoundingBox());             player.updateModelBound();                         playerNode.getLocalRotation().set(new Quaternion().fromAngleAxis( - FastMath.PI/2, new Vector3f(0,1,0)) );             playerNode.setLocalTranslation(0, 0.1f, 0);             playerNode.attachChild(player);                         // attach player and map to the scene             rootNode.attachChild(playerNode);             rootNode.attachChild(map);         } catch (Exception e) {   // Just in case anything happens             System.out.println("Damn exceptions! O_o n" + e);             e.printStackTrace();             System.exit(0);         }         KeyBindingManager.getKeyBindingManager().set("moveFwd", KeyInput.KEY_W);         KeyBindingManager.getKeyBindingManager().set("moveBwd", KeyInput.KEY_S);         KeyBindingManager.getKeyBindingManager().set("rotateLeft", KeyInput.KEY_A);         KeyBindingManager.getKeyBindingManager().set("rotateRight", KeyInput.KEY_D);         KeyBindingManager.getKeyBindingManager().set("strafeLeft", KeyInput.KEY_Q);         KeyBindingManager.getKeyBindingManager().set("strafeRight", KeyInput.KEY_E);         KeyBindingManager.getKeyBindingManager().set("return", KeyInput.KEY_RETURN);         // create a Camera Node and attach it to the player node         // move the camera a bit behind and above the player         CameraNode camNode = new CameraNode("", cam);         playerNode.attachChild(camNode);         camNode.setLocalTranslation(0, 0.7f, -0.5f);                 lastPosition = new Vector3f();         //disable mouselook:         //input = new InputHandler();                 //prevent "look through walls glitch         cam.setFrustumPerspective(45.0f, (float) display.getWidth() / (float) display.getHeight(), 0.002f, 350);     }     protected void simpleUpdate() {         //make collision box invisible         //player.setCullHint(CullHint.Always);         //collision stuff         results.clear();                    playerNode.findCollisions(rootNode, results);         if (results.getNumber() <= 0) {             System.out.printf("don't cross the dungeon limits dammitn");             System.exit(0);         }         oldData = results.getCollisionData(0);         if (oldData.getTargetTris().size() == 0) {             //store player last known working location             lastPosition.set(playerNode.getLocalTranslation());             // the boolean at the end is for keypress repeat             if (KeyBindingManager.getKeyBindingManager().isValidCommand("moveFwd", true)) {                 playerNode.getLocalTranslation().x += cam.getDirection().x * playerSpeed * timer.getTimePerFrame();                 playerNode.getLocalTranslation().z += cam.getDirection().z * playerSpeed * timer.getTimePerFrame();             }                         if (KeyBindingManager.getKeyBindingManager().isValidCommand("moveBwd", true)) {                 playerNode.getLocalTranslation().x -= cam.getDirection().x * playerSpeed * timer.getTimePerFrame();                 playerNode.getLocalTranslation().z -= cam.getDirection().z * playerSpeed * timer.getTimePerFrame();             }                        if (KeyBindingManager.getKeyBindingManager().isValidCommand("strafeLeft", true)) {                      Vector3f sideways = cam.getLeft();                 playerNode.getLocalTranslation().x += sideways.x * playerSpeed * timer.getTimePerFrame();                 playerNode.getLocalTranslation().z += sideways.z * playerSpeed * timer.getTimePerFrame();             }             if (KeyBindingManager.getKeyBindingManager().isValidCommand("strafeRight", true))  {                 Vector3f sideways = cam.getLeft();                 playerNode.getLocalTranslation().x -= sideways.x * playerSpeed * timer.getTimePerFrame();                 playerNode.getLocalTranslation().z -= sideways.z * playerSpeed * timer.getTimePerFrame();             }                             // left rotation code             if (KeyBindingManager.getKeyBindingManager().isValidCommand("rotateLeft", false)) {                 if (isRotating) {                     System.out.println("ignoring keypress while rotating");                 } else {                     isRotating = true;                     toRotate = ROTATION_STEP;                     dir = Direction.LEFT;                     System.out.println("Target Direction set to: " +dir                                        +"toRoate set to" +ROTATION_STEP);                 }             }                         // right rotation code             if (KeyBindingManager.getKeyBindingManager().isValidCommand("rotateRight", false))  {                 if (isRotating) {                     System.out.println("ignoring keypress while rotating");                 } else {                     isRotating = true;                     toRotate = ROTATION_STEP;                     dir = Direction.RIGHT;                     System.out.println("Target Direction set to: " +dir                                        +"toRoate set to" +ROTATION_STEP);                 }             }             // check if we should turn             if (isRotating) {                 float value = 0;                 // look in which direction we turn, and calculate                 // how much we should turn in this update cycle                 if (dir == Direction.RIGHT) {                     value = rotationSpeed*tpf*-1;                     toRotate += value;                 } else {                     value = rotationSpeed*tpf;                     toRotate -= value;                 }                 System.out.println("Degrees toRotate: " +toRotate);                                 // have we finished rotating ?                 if (toRotate <= 0) {                     // rotation has reached its end                     isRotating = false;                 } else {                     // not yet reached the end position, continue rotation                     // create a Quaternion which represents the Rotation                     tmpAngles[0] = 0;                            // X                     tmpAngles[1] = value * FastMath.DEG_TO_RAD;  // Y                     tmpAngles[2] = 0;                            // Z                     tmpRotationQuat.fromAngles(tmpAngles);                                         // multiply the players current rotation with the rotation                     // we want to do in this update cycle                     playerNode.getLocalRotation().multLocal(tmpRotationQuat);                 }             }                         if (KeyBindingManager.getKeyBindingManager().isValidCommand("return", true)) {                 //report current angle                 playerNode.getLocalRotation().toAngles(tmpAngles);                 float currentAngle = tmpAngles[1] * FastMath.RAD_TO_DEG;                 System.out.println("current angle: " +currentAngle);             }         }                 if (oldData.getTargetTris().size() > 0) {             System.out.println("Collission - reset position");             playerNode.getLocalTranslation().set(lastPosition);         }     } } ```