Reputation: 229
I've come to a situation in my application, built using LibGDX, where I need the camera to be able to rotate, and also be moveable by the user, as well as being able to be zoomed. My camera controls for moving and zooming the camera work beautifully, however, when the camera is rotated, the camera is then moved based on that rotation. For example, if the camera is turned 45 degrees to the left, and the user drags to the right to move the camera, it will move towards the upper right, because that is where the right side of the camera is now pointing. Instead, I need the camera to always move relative to the screen/user input, regardless of its rotation.
I do have one solution for this problem, and that is to simply use "world coordinates" to position the camera, rather than basing it on the "window coordinates" I am trying to use now. This creates a very unpleasant user experience though, as it makes the camera fairly choppy and causes problems with my zooming system. I assume I could patch those problems up with a lot of tweaking, however, because it is so much smoother already using the window coordinates, I would really prefer to go that route.
I suspect there is an elegant solution to this seemingly simple problem, where I could do a simple calculation on a matrix or something along those lines, however my knowledge in OpenGL is still pretty lacking and I cannot seem to figure out what exactly needs to happen to fix it. Any ideas from someone more adept with OpenGL and all of this matrices would be greatly appreciated!
===EDIT===
The objects used to hold the values here are essentially just my own version of Vector2, which was required in a different part of the application. I can't quite remember the exact reasoning for the 0.7f in there, but presumably it was just to lower the sensitivity of the movement, and the problem still persists if I remove it.
camera.position.set(cameraStartPosition.getX() - ((zoomCurrentPosition.getX() - zoomStartPosition.getX()) * 0.7f), cameraStartPosition.getY() + ((zoomCurrentPosition.getY() - zoomStartPosition.getY()) * 0.7f), 0);
cameraStartPosition - The X and Y coordinates of the camera when the user starts to move it.
zoomStartPosition - The X and Y coordinates of the initial touch that starts the zoom
zoomCurrentPosition - The X and Y coordinates where the touch that controls the zoom currently is
The x and y values in those are taken directly from the touchDown and touchDragged methods. If I instead place the code that sets those after the following code, it creates the problem I mentioned in the original post, where it moves the way it should, but very choppy.
Vector3 vector = new Vector3(x, y, 0);
camera.unproject(vector);
x = (int) vector.x;
y = CanvasAnywhereMain.HEIGHT - (int) vector.y;
Upvotes: 4
Views: 3946
Reputation: 2884
I was dealing with similar problem today when I was programming orbit camera. I will try to describe you how I dealt with this issue.
I wanted my orbit camera to be able to rotate around X and Y axis in the same time, but it was not working. I was able to rotate around this axes only respectively.
The trick for me was:
Vector3 target = Vector3.zero
Vector3 right = new Vector().set(camera.direction).crs(camera.up).nor();
// This angles usualy comes from touchDragged event in your input processor
// class implementing the InputProcessor, where you do your calculations
deltaAngleX = 1.1f;
deltaAngleY = 1.9f;
// Rotate around X axis
camera.rotateAround(target, right, deltaAngleX);
// Rotate around Y
camera.updateRotation(Vector3.Y, deltaAngleY);
When you are rotating around Y axis everything is fine because Y axis does not change depending on your X rotation. You always want to rotate around the world Y axis, not any local one.
But when you are rotating around X you can not use Vector3.X because this axis will not be relative to your camera position and direction. So we need to calculate "local Y" axis of your camera. To do that we need to know what Cross product of two vectors is. Please see Cross product on wiki and crs() on libgdx class reference
Following code fragment will return new Vector3, which will be pointing to the right axis relative to the current camera. Note the nor() call is present because we want to normalize that vector.
Long story short:
Vector3 right = new Vector().set(direction).crs(up).nor();
Crs creates cross product vector from two vectors (in our case camera.direction and camera.up).
I don't understand why in Vector3 the right member variable is not exposed or why right vector calculation method is not present, but this will do the trick anyway
With few code edits you can move your player, camera or anything in the world around the axis you need. You just need to understand the basic Vector operations so I recommend you going trough them
Last note:
You must rotate around X axis first because rotating around Y changes your local X axis which will need to be recalculated (the right vector).
Upvotes: 2