Getting and Setting Rotation Values

Hi Guys - wondering if you can help with a maths question! I am saving a list of points that a player object is when a player moves through the world. Then i want to replay their path but I am having trouble reading the rotation values.



At the moment I am saving rotation values to a linked list using

player.getLocalRotation().y





Then when i want to replay the path the player took through the world, I am using :


Quaternion q = new Quaternion();
q.fromAngleAxis(FastMath.PI * x.getYaw(), new Vector3f(0,1,0));
player.setLocalRotation(q);



The player is rotating, but in the wrong direction and at the wrong angles. I am pretty sure it is something to do with the "FastMath.PI * x.getYaw()" bit. I have tried a lot of combinations mentioned on the wiki rotation pages but none of them seem to have worked! Please help!

Thanks!

Thanks for your reply!



Maybe I was a bit vague yeah. Basically, i use player.getLocalRotation().y to get the value of the current y rotation of my player (from what I can see it is a float between 1 and -1) every frame, and once the user exits it is exported to XML. I also export the translation values of the player.



Then when I read the XML back in, I create a linked list and iterate through it, positioning the player in the world, effectively replaying their path through the world.



That works fine, but when it comes to rotation, I am unable to get it working correctly. x is just an instance of an object called pathstat that I defined and stores the position and rotation values. So x.getYaw() simply returns the y rotation for that point along the path.

the y in the Quaternion does not represent the rotation around the y axis or something alike, at least you need to confort it to angles before. (if you want to use angles use toangles from angles) elses if not important i would say why not just saving the quaternion as it is?

Oh, now I think I understand what you want. You want to be able to store the players position when the player exits the program and then load it when the program is started up again?



I'm not quite sure how to do this in jME, but what you want to do is to get the Matrix that define both translation, rotation and scale, from the player and save it. Then you multiply this matrix with the matrix for the new player you create when you start a new game.



I know how to get the matrix, but I don't know if jME supports the multiplication later on. I can't see any function to set the WorldMatrix. Anyway, this is how you get the WorldMatrix:



Matrix4f worldMatrix = player.getLocalToWorldMatrix(null);



And then I'm guessing you might be able to get the translation and the rotation from these functions (but I don't know, havn't tested it):

inverseRotateVect(Vector3f vec)
inverseTranslateVect(float[] vec)

If you have scaled your player you should save your scale separatly,since I don't think you can get it out of the matrix. You will probably have to reset the matrix to 1 in scale before you take out the rotate and translate parts.

edit: if you get this working you won't have to save all the history of how the player have moved.

try this and see if you can get it working.

The wonderful world of quaternions.  My guess at what you want is that your moving your player and want to store like a top down angle - so which way its facing - as it moves.  If you are using fromAngleAxis to setup the quaternion, you just need to use toAngleAxis to get the value back :




import static org.junit.Assert.*;

import org.junit.Test;

import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;

public class AngleTest
{
    @Test public void test()
    {
        int degrees = 45;
       
        Quaternion q = new Quaternion();
        q.fromAngleAxis(degrees * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);
       
        Vector3f axis = new Vector3f();
        float angle = q.toAngleAxis(axis);
    
        assertEquals(degrees, angle * FastMath.RAD_TO_DEG, 0.0001f);
        assertEquals(Vector3f.UNIT_Y, axis);
    }
}



Assuming you aren't using the quaternion for rotating in any other axis, this should get out your correct heading - so save the


  float angle = q.toAngleAxis(axis);



bit in your linked list, and then replay it back as you are doing already (without the PI bit though if you are working in radians the whole time).  Maybe that will work?

Hi Guys,



Just to let you know I am not ignoring you! Been having some problems with my workspace but hopefully will get some time to sort them out soon. I really appreciate your feedback and will keep you updated as soon as I can!



Thanks!

Ok, so I am still struggling with this!



I have changed the way I am doing things now. My game has 2 modes. The first is where the player controls movement themselves and the second is where their actions are replayed. I have this working for their position, but rotation is still giving me a headache!





When the player is in control, I am using player.getLocalRotation() to store the current quaternion.



Once the player exits, I am saving all of the rotation information (along with the position information) using serialiazable.



Then, when the players actions are being replayed, I am using player.setLocalRotation() to set the quaternion for that frame.



This has no effect though. The player doesn't rotate at all, even though I can do system.out and see that the quaternions are not the same. Its worth noting too - when the player is in control and not moving at all, I can do system.out.println(player.getLocalRotation), and the quaternions do not appear to be the same. Not sure if this is normal or not!



Hope you can help! This is driving me insane!  :smiley:

exactly what information are you storing and how? I would like you to post some code so we can see exactly what you do when you save/restore the position (the relevant parts of course).

All I am doing is storing a big linked list of an object called frame. Frame just contains the frame number, the x y z position and a quaternion of the player for every frame.



Then I am just serializing that on cleanup, and reading it back in init when the game is in review mode.

Okay, when you want to restore a rotation for the replay I would recommend that you create a new quaterion, based on the degree and axis of the stored quaterion, instead of using the stored quaterion. If I remember correctly I think I had problems with reusing a quaterion, nothing happend… I was in my case fixed with creating a new quaterion (don't ask me why, I don't know). it doesn't hurt to try it anyway :slight_smile:



One more question, This replay, is it during the same run of the program or do you shut down the program and save this frame objekt to a file?

Ok, thanks - I will give that a try now and see what happens.



The program is shut down and then the frame objects are serialised and saved to a file. I have a little tool that lets you select the file to replay before the program runs.

can you make a small example to reproduce the problem using simeplegame ?

I just tried making an example, but I am going to have to change a lot of what I have done and it will take days for me to do all of it, as the code is closely integrated to a lot of other classes and the statistics export and serialization is very complex. So instead could you just have a look at the code? This is essentially what I am trying to do:


private LinkedList<Vector3f> posList = new LinkedList<Vector3f>();
   private LinkedList<Quaternion> rotList = new LinkedList<Quaternion>();
   
   public SimpleReplay() { }

   public void stateUpdate(float tpf) {
      physicsSpace.update(tpf * physicsSpeed);

      if (evaluationMode){
         
         posList.add(player.getLocalTranslation());
         rotList.add(player.getLocalRotation());
         
      } else {
         
         try {
            player.setLocalTranslation(posList.getFirst());
            player.setLocalRotation(rotList.getFirst());
            
            posList.removeFirst();
            rotList.removeFirst();
         }
         catch (NoSuchElementException nse){ }
      }
   }



Any help would be great! Thanks!

player.getLocalRotation() returns the reference to the players Quaternion, so you probably adding always the same reference over and over to the list. I think you want to make a copy of the current values and save that copy in the list.



Try to do a rotList.add(getLocalRotation().clone()) / player.getLocalRotation().set(rotList.getFirst()); .

although player.setLocalRotation(rotList.getFirst()) should work too if the made copies when adding them to the list.

Yes! That did it! Thanks a lot for your help! I have been working on this for ages and now it works perfectly!



Thanks!

glad you got it working, its one of those jme pitfalls,

always choose wisely when to use getLocalRotation().set(x) or setLocalRotation(x);