The time has come where I need to ask how to do this. I experimented for about 5 hours last night trying to get this to work correctly. I was trying to use (OrthographicCamera) camera.rotate() and camera.rotateAround() and I just can’t seem to get the camera to rotate how I want it to. Although it does rotate every other which way I don’t want it to. Currently it is just top down, and I want to tilt it backwards. So basically two questions. 1: Are these the correct methods to be using in order to achieve top down oblique view? Can this be achieved by a 2d camera or should I switch it to 3d? And when I tilt it backwards, I’m tilting along the Y axis, correct? Sorry I’m such a noob and can’t get this to work.
I am not entirely sure whether this is achievable via libGDX’s OrthographicCamera alone, since it only seems to provide a rotate(angles) method which rotates around the view (i.e. Z) axis. But you need to rotate around the X axis. And you definitely need an orthographic/2D projection, not a perspective/3D one.
For this I would rather revert to the raw matrix operations you need.
You basically need this: O * R * T
where:
- O is a scale and translate transformation to transform coordinates in a “view extents” volume down to the unit cube. This is usually called some form of “ortho”. In libGDX’s Matrix4 class it is called ‘setToOrtho’.
- R is a rotation of 45 degrees around the X axis. In libGDX this can be done via setToRotation(Vector3(1,0,0), 45).
- T is a translation of the negated camera position. There is a post-multiplying method in libGDX for that: translate().
Sadly, libGDX’s Matrix4 class does not have post-multiplying methods for O and R, so you gonna need to use mul() for that.
So you create O and R using the mentioned methods and can post-multiply the translation onto R using Matrix4.translate() on R. Then, you need to multiply R to O with O.mul®.
If you prefer, here are the necessary transformations expressed in “JOML language”:
float ex = 4.0f; // <- 4 units in each direction
float ar = (float)width/height;
Matrix4f m = new Matrix4f()
// account for aspect ratio and view extents
. ortho(-ar*ex, ar*ex, -ex, +ex, 0, +10)
// rotate 45° down (i.e. rotate around the X axis)
. rotateX((float)Math.PI * 0.25f)
// translate by negated camera position
. translate(0, -1, -1);
Thanks a lot for the deep explanation on how to do this. I definitely wasn’t / didn’t think I had to go that in depth. I’ll mess around with this info and see if I can get it working. Thanks man!
Btw. if you want to rather specify your camera in terms of a “look at” point/position and a distance to that point (along the view direction of the camera), you need the following transformations:
float ex = 4.0f; // <- 4 units in each direction
float ar = (float)width/height;
float dist = 5.0f; // <- distance from camera to lookat position
Matrix4f m = new Matrix4f()
// account for aspect ratio and view extents
. ortho(-ar*ex, ar*ex, -ex, +ex, 0, +10)
// translate camera "back" by the distance 'dist'
. translate(0, 0, -dist)
// rotate 45° down (i.e. rotate around the X axis)
. rotateX((float)Math.PI * 0.25f)
// translate by negated target/lookat position
. translate(-2, 0, 0); // <- look at (2, 0, 0)
This is essentially the basis for an “arcball” camera. You can also add a rotation around the Y axis after the rotation around the X axis.
It should be straight forward to translate that to libGDX Matrix4 methods.
Ok cool, I can’t wait to get home and try this stuff out!
Just to be clear, do I put this stuff in the constructor for Matrix4? Or do I create a Matrix4 and use matrix.setToRotation?
You create individual Matrix4 objects and call the respective methods on them.
Then you multiply them together with mul(), like so:
[icode]o.mul(t1).mul®.mul(t2)[/icode]
The result will be stored in ‘o’. Read the JavaDoc of libGDX’s Matrix4.mul(Matrix4).
After all that, I am still a bit surprised that Google’ing for “libGDX oblique camera” or “libGDX oblique projection” does not yield any reasonable results… There is only one single question on stackexchange without any answer. Of course there are tons of resources on how to do that generally with linear algebra and matrices, but none using libGDX’s camera abstractions.
Yeah thanks for taking the time and having the patience to explain all of that. I googled so much on how to do that with LibGDX and couldn’t really find any solid examples of it (and I’ll be the first to admit I’m not the best with math and how angles work when you have a z axis). There are some good ones for isometric, but like I said in the other post, I probably shoudn’t be trying to do iso, esp if I can’t even figure out the top down oblique lol. Hopefully this thread will help other people looking for this same question. I’m working on animations right now and will try for the camera either later today or tomorrow. Thanks again @KaiHH you were a super big help with all this!
You can also look at this very small LWJGL3/JOML demo to see how other oblique projections (such as cavalier) can be achieved easily with a custom matrix.
LibGDX’s Matrix4 also has a constructor and a set method that you can give the 16 matrix element values in the form of a float[] array.
Am I anywhere close with this? How it is now will kind of just cut off the bottom of the viewport and flips the y axis over.
private void initializeCamera() {
camera = new OrthographicCamera(cameraWidth, cameraHeight);
camera.position.x = myGame.gameObjectLoader.player.getX();
camera.position.y = myGame.gameObjectLoader.player.getY();
camera.setToOrtho(true, cameraWidth, cameraHeight);
float ex = 10f; // <- 4 units in each direction
float ar = (float) cameraWidth / cameraHeight;
matrixO.setToOrtho(-ar*ex, ar*ex, -ex, ex, 0, 10);
matrixR.setToRotation(new Vector3(1, 0, 0), 45);
matrixR.translate(0, -1, -1);
matrixT = matrixO.mul(matrixR);
camera.rotate(matrixT);
}
One thing I’m confused about is ar: is that the cameraWidth/cameraHeight? Or is it supposed to be Gdx.graphics.getWidth()/height (I tried both). CameraWidth = 10 at the moment and I don’t specifiy a size for the window in the desktop launcher so it’s size is whatever the default is. So that code above is camera init(). Here is the camera update method:
@Override
protected void updateCamera() {
myGame.renderer.batch.setProjectionMatrix(camera.combined);
myGame.renderer.shapeRenderer.setProjectionMatrix(camera.combined);
if (!ScreenShake.screenIsShaking) {
camera.position.x = myGame.gameObjectLoader.player.getX();
camera.position.y = myGame.gameObjectLoader.player.getY();
}
camera.update();
}
Sorry this is so confusing, and thanks again.