MVP matrix in JOML

Started by josephdoss, July 26, 2016, 20:49:11

Previous topic - Next topic

josephdoss

Hey Bros,

Yesterday I just got so sick of C++ dependency problems that I decided to move my video game to Java. It's actually been a pretty good move. Already the external library situation has gone from 12 to 1 and I have way less files to keep track of. And no more makefile! My problem though is I can't figure out how to do the MVP matrix the way I had it in C++. Apparently, lwjgl was overhauled and rereleased just last month, which is probably why I can't find any tutorials or examples for lwjgl 3.0 out here on the net.

So, I need some help. Below is the C++ code. The facing, eyeXYZ, character(), and other variables I can get already in Java. What I can't do is get the whole matrix situation acting right. I'm using Matrix4f and Vector3f and Matrix4f.mul/flip and FloatBuffers and .... I don't even know. Apparently Linear Algebra is the one area where C++ is actually simpler than Java.

What I need is to input the desired camera positiion ( eyeXYZ ) and the characterXYZ and get them MVP matrix back.
I'll then pass the MVP matrix as a uniform into the vertex shader and then be able to follow my character around, RPG style.

Can anyone help?

//C++ code that does exactly what I need
      
float facing = getCharacterFacing();
facing = (float) ( ( (int) (facing + 180) ) % 360 ) ;
float eyeX = getCharacterX() + CAMERA_DISTANCE_BEHIND * cos( facing * PI / 180.0);
float eyeY = getCharacterY() + CAMERA_DISTANCE_BEHIND * sin( facing * PI / 180.0 );
float eyeZ = getCharacterZ() + 4.0;
               
glm::mat4 Projection = glm::perspective(45.0f, (float)640/480, 0.1f, 100.0f);

glm::mat4 View       = glm::lookAt(
    glm::vec3(eyeX,eyeY,eyeZ),                          // Camera is at (4,3,3), in World Space
    glm::vec3(getCharacterX(),getCharacterY(),getCharacterZ()), // and looks at the character
    glm::vec3(0,0,getCharacterZ()+20.0)             // Up is positive Z, 20 units above character so it's guaranteed to be above character
);

glm::mat4 Model      =  glm::mat4(1.0f); 

glm::mat4 MVP        = Projection * View * Model;


Kai

QuoteApparently Linear Algebra is the one area where C++ is actually simpler than Java.
Nope. :) Not when done right. JOML's API is actually more "fluent" than GLM's. And using a lookat transformation when computing an "arcball" camera, like you did, is more complicated than doing it the direct way with translate/rotate. So your complete shown code becomes:
float CAMERA_DISTANCE_BEHIND = 8.0f;
float HEIGHT_ABOVE_PLAYER = 4.0f;
float facingInRad = (float)Math.toRadians(facing);
// Compute angle above player from "opposite leg" / "adjacent leg"
float angle = (float) Math.atan(HEIGHT_ABOVE_PLAYER / CAMERA_DISTANCE_BEHIND);
// Build transformation matrix (using +Z as up)
mvp.setPerspective((float)Math.toRadians(45), 640.0f/480.0f, 0.1f, 100.0f)
   .translate(0, 0, -CAMERA_DISTANCE_BEHIND) // <- move camera away from player
   .rotateX(angle - (float)Math.PI * 0.5f) // <- keep an angle 'above' the player and make +Z point upwards
   .rotateZ(facingInRad) // <- rotate to correct facing
   .translate(-player.x, -player.y, -player.z); // <- move camera target/center to player position

Though it is unusual to have +Z being up. Usually with OpenGL and Direct3D +Y is up. So if you choose to use +Y as up, then the rotations of above code become:
   .rotateX(angle) // <- keep an angle 'above' the player
   .rotateY(facingInRad) // <- rotate to correct facing

Additionally, when you use +Y as up, you can use the Matrix4f.arcball() method, which makes your code more readable. The above complete code with +Y up then becomes:
float CAMERA_DISTANCE_BEHIND = 8.0f;
float HEIGHT_ABOVE_PLAYER = 4.0f;
float facingInRad = (float)Math.toRadians(facing);
// Compute angle from "opposite leg" / "adjacent leg"
float angle = (float) Math.atan(HEIGHT_ABOVE_PLAYER / CAMERA_DISTANCE_BEHIND);
// Build transformation matrix (using +Y as up)
mvp.setPerspective((float)Math.toRadians(45), 640.0f/480.0f, 0.1f, 100.0f)
   .arcball(CAMERA_DISTANCE_BEHIND, player.x, player.y, player.z, angle, facingInRad);

If you still want to use Matrix4f.lookAt() and compute the camera position like you did, then have a look at: https://github.com/JOML-CI/JOML#building-a-camera-transformation
Mayor take-aways from this: Matrix multiplications are simple concatenations of transformation methods in JOML. See https://github.com/JOML-CI/JOML/wiki/Design#post-multiplied-transformations for more detail.
Also: All angles are in radians. This includes the field-of-view angle in Matrix4f.setPerspective(). This convention differs from GLU and GLM (if GLM_FORCE_RADIANS is not used).

josephdoss

Thanks man,
That would have taken me a million years to work out on my own.

Cheers,
JD

josephdoss

Hey Kai,

I know it's been a few months and you probably don't still have the code, but if you're still around, would you show me how you made the mvp object in your java code? Is that a joml array or something?

Kai

Hey JD,
I have plenty of code creating model-view-projection matrices around, that you can also have a look at. See all the demos in these repositories:
- https://github.com/JOML-CI/joml-lwjgl3-demos/tree/master/src/org/joml/lwjgl
- https://github.com/LWJGL/lwjgl3-demos/tree/master/src/org/lwjgl/demo/opengl

I'd start with the former, because it focuses more on the JOML-side of things and not so much on OpenGL.

Quote...you made the mvp object in your java code? Is that a joml array or something?
I don't quite know what you mean by 'mvp object' and by 'joml array'.

The code that you see in my posts in this thread create mvp matrices. They assume that there is a variable (field or local) called 'mvp' of the type Matrix4f.

josephdoss

Thanks man, that helped!

Now I just have to pass mvp to my shader and I think I'm in business.

Thanks for all your help,
JD