3D Game Engine, Entity Rotation Problem

Started by Spytrycer, April 21, 2017, 04:14:05

Previous topic - Next topic

Spytrycer

Hey Guys,

Im currently working on my own Java 3D Game Engine.
But im having some issues with my Entity/Player Rotation and im not sure where my Mistake lies.

I already posted my Problem and how far i got on Stack Overflow and Cant find any help there.
http://stackoverflow.com/questions/43433107/java-lwjgl-3-3d-entity-rotation-x-axis

My Problem is the following:

The Game im writing the Engine for will be a 3D 'Space-Game' and because of that i want the player to be able to Move and turn in all directions.
The Moving part, i think i have figured out.
But im just plain struggeling with the Rotation for Months now.

By now i have figured out, that in my createTransformationMatrix() function, the Axis i rotate last will be the Axis working with no problem and Corresponding to the ENTITY'S_Axis and not to the World's-Axis.
The other Rotations will then be rotating to the World axis, meaning that when for example the Y Rotation, in this case the turning left and right, is last, then all the other axis will rotate corresponding to the World and not the player and by that, pitching with 90Degrees of a Y-Rotation becomes the Rolling, since its corresponding to the World-Axis.

If all of the Rotations would be Player OR World related, that could then be fixed to what i want, but its so odd that the last one Called is always the Right one.
I also checked if my Math Classes are False. I used to use Math classes from LWJGL2 that have been changed to fit into 3.
But i switched to Joml and no change.

So im really clueless.

Here is my Code. And i have Example GIfs on Stack Overflow, just follow the link Above
public void move(double delta)
{
    if(this.rotX >= 360) this.rotX = 0;

    float distance = (float) (currentSpeed * delta);
    float dx = (float) (distance * (Math.sin(Math.toRadians(super.getRotY()))));
    float dz = (float) (distance * (Math.cos(Math.toRadians(super.getRotY())) * (Math.cos(Math.toRadians(-super.getRotX())))));
    float dy = (float) (distance * (Math.sin(Math.toRadians(-super.getRotX()))));


    this.increasePosition(dx, dy, dz);


    float rotation_pitch = (float) ((currentTurnSpeed_pitch * delta) );
    float rotation_yaw = (float) ((currentTurnSpeed_yaw * delta));
    float rotation_roll = (float) (((currentTurnSpeed_roll * delta))); 

    float rx = (float) (rotation_roll  ); //X C
    float ry = (float) (rotation_yaw    ); //A D
    float rz = (float) (rotation_pitch   ); //Q E 



    this.increaseRotation(rx, ry, rz);


TransformationMatrixcreation Math Class

public static Matrix4f createTransformationMatrix(Vector3f translation, 
        float rx, float ry, float rz, float scale)
{
    Matrix4f matrix = new Matrix4f();

    matrix.translate(translation.x, translation.y, translation.z)
    .rotate((float) Math.toRadians(rx), 1, 0, 0)  
    .rotate( (float) Math.toRadians(rz), 0, 0, 1)
    .rotate((float) Math.toRadians(ry), 0, 1, 0) 
    .scale(scale);


    return matrix;   
}

Matrix4f Rotate

public static Matrix4f rotate(float angle, Vector3f axis, Matrix4f src, Matrix4f dest) {
    if (dest == null)
        dest = new Matrix4f();
    float c = (float) Math.cos(angle);
    float s = (float) Math.sin(angle);
    float oneminusc = 1.0f - c;
    float xy = axis.x*axis.y;
    float yz = axis.y*axis.z;
    float xz = axis.x*axis.z;
    float xs = axis.x*s;
    float ys = axis.y*s;
    float zs = axis.z*s;

    float f00 = axis.x*axis.x*oneminusc+c;
    float f01 = xy*oneminusc+zs;
    float f02 = xz*oneminusc-ys;
    // n[3] not used
    float f10 = xy*oneminusc-zs;
    float f11 = axis.y*axis.y*oneminusc+c;
    float f12 = yz*oneminusc+xs;
    // n[7] not used
    float f20 = xz*oneminusc+ys;
    float f21 = yz*oneminusc-xs;
    float f22 = axis.z*axis.z*oneminusc+c;

    float t00 = src.m00 * f00 + src.m10 * f01 + src.m20 * f02;
    float t01 = src.m01 * f00 + src.m11 * f01 + src.m21 * f02;
    float t02 = src.m02 * f00 + src.m12 * f01 + src.m22 * f02;
    float t03 = src.m03 * f00 + src.m13 * f01 + src.m23 * f02;
    float t10 = src.m00 * f10 + src.m10 * f11 + src.m20 * f12;
    float t11 = src.m01 * f10 + src.m11 * f11 + src.m21 * f12;
    float t12 = src.m02 * f10 + src.m12 * f11 + src.m22 * f12;
    float t13 = src.m03 * f10 + src.m13 * f11 + src.m23 * f12;
    dest.m20 = src.m00 * f20 + src.m10 * f21 + src.m20 * f22;
    dest.m21 = src.m01 * f20 + src.m11 * f21 + src.m21 * f22;
    dest.m22 = src.m02 * f20 + src.m12 * f21 + src.m22 * f22;
    dest.m23 = src.m03 * f20 + src.m13 * f21 + src.m23 * f22;
    dest.m00 = t00;
    dest.m01 = t01;
    dest.m02 = t02;
    dest.m03 = t03;
    dest.m10 = t10;
    dest.m11 = t11;
    dest.m12 = t12;
    dest.m13 = t13;
    return dest;
}


I am going crazy and i think about to go bold if i keep staring at those classes.
I would really appreciate ANY help

Kai

When you want to implement such a "space/free camera" the problem is that you cannot represent all rotations with just three Euler angles and manipulate those angles in an intuitive way, because the rotations depend on each other and you always start either with the rotations being relative to the world or relative to the _initial_ view/camera/eye frame.

The solution is really really simple:
1. you need to accumulate the rotations (and translations)
2. you need to do it via pre-multiplying to remain in the camera's local frame of reference

So instead of modifying the Euler angles and building the rotation transformation from scratch each time, you need to apply differentials to the rotation (and translations).

The easiest way to do it using JOML is this:
// Initially, once declare your matrix:
private Matrix4f view = new Matrix4f();
// ...
// then each time you rotate left/right (around the Y axis), do:
view.rotateLocal(+/-dAngle, 0, 1, 0);
// for up/down (around the X axis), do:
view.rotateLocal(+/-dAngle, 1, 0, 0);
// each time you want to move forward/backward, left/right, up/down do:
view.translateLocal(+/-dxDist, +/-dyDist, +/-dzDist);

The rotations/translations can ideally be applied in your game loop also taking the time differential as a factor.
There is nothing more to it.

Spytrycer

Thank you for the Answer!

I am actually trying to Rotate the Entity though, because funny enough, the Camera ViewMatrix works fine around all Axis with pretty much the Same Code:

public static Matrix4f createViewMatrix(Camera camera) 
	{
		
		
        Matrix4f viewMatrix = new Matrix4f();
        viewMatrix.identity();
        Vector3f cameraPos = camera.getPosition();

        
        viewMatrix
		  .scale(1f)
		  .rotate( (float) Math.toRadians(camera.getRoll()), 0, 0, 1)
		 
		  .rotate( (float) Math.toRadians(camera.getPitch()), 1, 0, 0)
		  .rotate( (float) Math.toRadians(camera.getYaw()), 0, 1, 0)
		  
		  .translate(-cameraPos.x, -cameraPos.y, -cameraPos.z);
        
        
        return viewMatrix;
    }



But even with the Code you proposed, the problem remains.
I tried it and the Y-Rotation was fine, it rotated by the Entitys Axis.
But the X-Rotation, and when i added the Z-Rotation, it did the same, again just rotated along the Worlds Axis.
Here is my Implementation:

public static Matrix4f createTransformationMatrix(Vector3f translation, 
			float rx, float ry, float rz, float scale)
	{
		Matrix4f matrix = new Matrix4f();
		
		rx = (float) Math.toRadians(rx);
		ry = (float) Math.toRadians(ry);
		rz = (float) Math.toRadians(rz);
		
	   matrix.rotateLocal(ry, 0, 1, 0);
	   
	   matrix.rotateLocal(rx, 1, 0, 0);

	   matrix.rotateLocal(rz, 0, 0, 1);
	   
	   matrix.translateLocal(translation.x, translation.y, translation.z);
	   
		  
		return matrix;	 
	}

Kai

Quotebecause funny enough, the Camera ViewMatrix works fine around all Axis with pretty much the Same Code:
...code snip...
No, it will not work like this. Have you actually tried rotating the camera around all three angles?

Please re-read my first post. The important things you have to consider for a working solution:
- rebuilding your matrix based on three Euler angles _will not_ work
  (in addition, when using three Euler angles as absolute rotation angles, you'll run into Gimbal Lock eventually.)
- instead you need to apply/integrate little differential/delta rotations over time

It is the same for transforming an entity in your world. Just that here you should use post-multiplication methods (rotate/translate) instead of pre-multiplication methods (rotateLocal/translateLocal).

Again: Storing and manipulating three Euler angles and rebuilding the transformation matrix based on them will not work.

Spytrycer

*Sound of understanding*AHHHHHHHHHHHH........(continues for eternity)

Now i know what you mean!
I feared that i had to look closer at the whole calculating thing.
I understand what you mean now.

Could you point me towards an article or tutorial that fits my Idea so i can work myself into it?
And thank you so much, this is the closest im to a solution i've come in about 5 Months!

Kai

I'm sorry. I cannot tell you any references that describe exactly your problem at hand.
But there is a working demo for a "Free fly camera" here: https://github.com/JOML-CI/joml-lwjgl3-demos/blob/master/src/org/joml/lwjgl/FreeCameraDemo.java
It uses this FreeCamera class of "joml-camera": https://github.com/JOML-CI/joml-camera/blob/master/src/org/joml/camera/FreeCamera.java
and is only compatible with the latest JOML 1.9.3-SNAPSHOT (deployed as Maven artifact to oss.sonatype.org)
The relevant parts of the demo are:
- https://github.com/JOML-CI/joml-lwjgl3-demos/blob/master/src/org/joml/lwjgl/FreeCameraDemo.java#L195-L206
- https://github.com/JOML-CI/joml-lwjgl3-demos/blob/master/src/org/joml/lwjgl/FreeCameraDemo.java#L221

You should be able to adapt that code to transforming a model in your world (such as an airplane).
Just remember to use post-multiplying matrix methods (the ones without the 'Local' suffix).

Spytrycer

Thank you alot!
I understand my problem now way more.

I implemented the Code into mine and changed it a little bit, because i didnt like the Entity to move continuously and faster and faster when accelerated, or i implemented the code wrong at first :).

But i did keep the integration of differentials with delta that solves the Rotation problem i had, thanks alot for that!

Im now struggeling with the Coordinates of an Entity.
I want an Entity to have specified coordinates in the World, that i can refer to and change in order to chase it with the Camera(which right now doesnt work) and to set to a specific value if nessesary.

Here is my implementation, maybe you can help me getting to a solution and look over my Code to see if i implemented correctly:

(And Yes, i know casting a Double into a Float is horribly inaccurate and can be terrible, i intend to go over these castings later on, now i want to get this to work)

public void move(double delta)
	{
		float dt = (float)delta;
		
		//linearVel.fma(dt, linearAcc);
		
	    //angularVel.fma(dt, angularAcc);

	    this.position.x += dt*linearVel.x;
	    this.position.y += dt*linearVel.y;
	    this.position.z += dt*linearVel.z;
		
	    transformationMatrix.rotateX(dt*angularVel.x)
	          .rotateY(dt*angularVel.y)
	          .rotateZ(dt*angularVel.z);
	    
	    transformationMatrix.translate( dt*linearVel.x, dt*linearVel.y, dt*linearVel.z);
        }


Thats the changed Move Method. Im now not calculation in it, instead im just changing the transformationMatrix which i have made part of the Entity Class in general, terrible solution, but right now i want to get it to work.

//CAMERA MOVE
public void move(double delta)
	{
		
		
		float dt = (float)delta;
		
	    //linearVel.fma(dt, linearAcc);
		
	    //angularVel.fma(dt, angularAcc);
		
	    this.angularVel.x = player.angularVel.x;
	    this.angularVel.y = -player.angularVel.y;
	    this.angularVel.z = player.angularVel.z;
		
	    this.pitch += dt*angularVel.x;
	    this.yaw += dt*angularVel.y;
	    this.roll += dt*angularVel.z;
	    

	    
	    linearVel.x = player.linearVel.x;
	    linearVel.y = player.linearVel.y;
	    linearVel.z = player.linearVel.z;
	    
	    position.x += dt*linearVel.x;
	    position.y += dt*linearVel.y;
	    position.z += dt*linearVel.z;
		
	    viewMatrix
	    .rotateLocalX((float)Math.toRadians(dt*angularVel.x))
            .rotateLocalY((float)Math.toRadians(dt*angularVel.y))
            .rotateLocalZ((float)Math.toRadians(dt*angularVel.z))
	    .translateLocal(-dt*linearVel.x, -dt*linearVel.y, -dt*linearVel.z);
}



The Camera is the Problem.
I want it to tag along the Player, i try that by giving it the players linearVelocity.
The Camera does take this and with my code the Position changes to, but the Cameras position doesnt, or at least not really. It is kind of vibrating in place.
I tried to set the translation to:
.translateLocal(1,1,1);

and it turns out that it DOES move it that way, but it seems to be returned to its original position everytime.

I also spawned in a referenceEntity that im not moving and that proves this theory. The Camera is trying to move but it seems like it Cant for some reason.
What am i doing wrong??







Kai

You can separate the translation of your entity from its rotation and compose both together when building the final model transformation matrix. However, when you still want to move an entity along its own local "forward"/"right"/"up" axes, then you need to compute these vectors in world-space first.
Instead, an easier way is to keep everything like it is now, and compute the origin of the entity in world-space via:
Matrix4f model = ...; // <- the model transformation matrix of the entity
Vector3f position = new Vector3f(); // <- position will be stored in here
model.getTranslation(position); // <- store entity's position


It sounds that you want to have an "arcball" camera. Is that right?
If that is the case, then have a look at: https://github.com/JOML-CI/joml-camera/blob/master/src/org/joml/camera/ArcBallCamera.java

The important part of it is the order of transformations in the viewMatrix() method:
https://github.com/JOML-CI/joml-camera/blob/master/src/org/joml/camera/ArcBallCamera.java#L67-L70

You can throw all that ArcRotor/ScalarMover stuff out, as it is merely used to "smoothen" the rotation and translation of the arcball camera over time.

Spytrycer

Ok, thats great help, I implemented the Code for the Positioning and it works perfectly!

And i dont think i want an Arcball Camera, im sorry, i might have spoken too wierd :)

What i want the Camera to do is to follow around the Player in every Movement.
The Player moves like a FreeCamera and the Camera follows behind.
What i dont want is to just move the Camera in the Function i call the Playermovement, i want the camera to move BASED on the players position and Rotation.

And that works just fine, at least for the Rotation. Just the Translation doesnt want to work.

GIF of Movement Example
blob:http://imgur.com/7ff0818b-3cef-4e1a-84a4-c6f8e7cdd6e6

As you see in the Gif, its all working, but the Camera class is not moving.
Or to be precise, it is moving but its being reset everytime, which makes this wobbling and glitching kinda thing.

As i mentioned earlier, i tried a translation of just 1,1,1 and it was doing this glitching back and forth the whole time.

I dont know why it is not translating the Cameras position.
I checked the Values i give into the translation, i even checked if that changes the position i get with your method and the Position doesnt seem to change.

For Position.Z for example it just takes the Value of the Differential Change.
The System.out.print for position.z looks like this:
0.13825268
0.11797666
0.17785572
0.15799287
0.13815312
0.11804388
0.17783828
0.1583886
0.13771991
0.11774414
0.17758986
0.15768081
0.13756388
0.117652275
0.17749798
0.15764795
0.13755158
0.11771437
0.17732398
0.15786403
0.13733652
0.11720933
0.18107897
0.15739237
0.1567626
0.13690487
0.11675202
0.17704684
0.15671691
0.13668469
0.11680386
0.1767394
0.16356169
0.16142154
0.11668632
0.17648327
0.15750836
0.13646758
0.11657392
0.17649148
0.15631811
0.13650659
0.116476916


Looks pretty much just like
dt * player.linearVel.x


Here is my viewMatrix:

public void move(double delta)
	{
		
		
		float dt = (float)delta;
		
		//linearVel.fma(dt, linearAcc);
		
	    //angularVel.fma(dt, angularAcc);
		this.angularVel.x = player.angularVel.x;
	    this.angularVel.y = -player.angularVel.y;
	    this.angularVel.z = player.angularVel.z;
		
		this.Rotation.x += dt*angularVel.x;
	    this.Rotation.y += dt*angularVel.y;
	    this.Rotation.z += dt*angularVel.z;
	    
	    linearVel.x = dt * player.linearVel.x;
	    linearVel.y = dt * player.linearVel.y;
	    linearVel.z = dt * player.linearVel.z;
	    
		
	    viewMatrix
	    .rotateLocalX((float)Math.toRadians(dt*angularVel.x))
        .rotateLocalY((float)Math.toRadians(dt*angularVel.y))
        .rotateLocalZ((float)Math.toRadians(dt*angularVel.z))
	    .translateLocal(linearVel.x,linearVel.y, linearVel.z);

	    
	    viewMatrix.getTranslation(position); // <- store entity's position
	
	}



And BTW, is there a way to do
model.getTranslation(position);


With the Entities or cameras Rotation too?



Kai

Okay, if you want a "third-person camera" which always follows the player by keeping a fixed distance to the player and always has the same position relative to the player, then do the following:
1. build the view matrix V as usual (like shown in the FreeCamera/FreeCameraDemo)
2. for rendering the player, you need to have: V x V^-1 x T = T
   where:
     - V is the camera matrix built in step 1
     - V^-1 is the inverse of V (can compute with Matrix4f.invert())
     - T is the translation that positions the camera relative to the player
     - 'L x R' means: multiplying 'L' by 'R' (for example via Matrix4f.mul())
3. for rendering everything else, you need to have: T x V x M
   where:
     - T is the same T as above (the position of the camera relative to the player)
     - V again is the camera matrix built in step 1
     - M is the respective object's model transformation

At this point, you need to have a more thorough understanding of coordinate systems and linear algebra (i.e. for transformation matrices).
If you like to start with the basics, you can have a look at Khan's Academy Course "Linear Algebra" (beware: it'll teach you really fundamental and essential stuff which is not focused on computer graphics alone, and is also university-grade, which can be hard to follow at times).
Other good resources are:
- http://www.songho.ca/opengl/gl_transform.html
- https://processing.org/tutorials/transform2d/ (even if it is for 2D, it can be generalized to 3D)
- http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/

Kai

I've augmented the FreeCameraDemo with a "third person" view. Toggle with 'T'. Please check, if that is what you want.
Change set: https://github.com/JOML-CI/joml-lwjgl3-demos/commit/c36beed60a3a2181c64fe754b741400703b393cc

Spytrycer

Sry, i have to redefine my Question:

For now, i want a First person Camera, just having the same Position and Rotation as the Player-Entity.
And the Player-Entity moves fine, but the Camera does not translate.

When I call
viewMatrix.translateLocal(dt* linearVel.x,dt * linearVel.y, dt * linearVel.z);

The Matrix-Position for the Z-Change switches to the Value of
dt * linearVel.z

Instead of increasing by it.
It looks like this:
-1.000E+0  0.000E+0 -8.742E-8  0.000E+0
 0.000E+0  1.000E+0  0.000E+0  0.000E+0
 8.742E-8  0.000E+0 -1.000E+0  1.348E-1
 0.000E+0  0.000E+0  0.000E+0  1.000E+0


I dont know why the Cameras Position is not translating.

And my sideQuestion.
Is there a function like:
matrix.getTranslation(position);

For the Rotation?

I posted my Code on the ViewMatrix on my previous Answer

Spytrycer

I really cant figure out why its not working.

I tried multiple other things but No translation whatsoever works, why could that be?