Generic Simple 3D Matrix Rotation Issue - java

I have a problem with my rotation on my 3D object (It's in java but it doesn't really matter)
The background:
I have a simple 3d model, and you are a first person player on it, you move your mouse up to look up (I.E Rotate by the 3D's x-axis) and move your mouse down to look down (Rotate in opposite direction)
But:
I also have the left and right arrow keys to turn left/right (Which rotates the 3D's y-axis)
Now the problem is, when I have turned, when I rotate by the x-axis it no longer turns as expected i.e if you turn 180 degrees, by moving your mouse down you actually look up and if you move your mouse up you actually look down.
What rotation can I perform on the x/y axis to fix this?
-So no matter how much/little I have turned, moving mouse up will look up and moving mouse down will look down.
Sorry I can't explain it better, but if you need any more info just make a comment.
Thanks alot,
It's probabaly a simple transformation, but i can't think :(
Some of Java rotation code:
Mouse Up/Down:
public void rotateXY(double radians, double Yradians)
{
vpTrans.getTransform(T3D);
Transform3D test = new Transform3D();
Transform3D testX = new Transform3D();
Transform3D testY = new Transform3D();
translate.set(lastXcord, lastYcord, lastZcord);
testX.rotX(radians);
testY.rotY(Yradians);
test.mul(testX, testY);
test.setTranslation(translate);
vpTrans.setTransform(test);
//System.out.println(test);
} // end of rotateXY()
Left: (right is just similar but with minus instead of plus on angle changes)
public void run() {
Transform3D test = new Transform3D();
Transform3D rotX = new Transform3D();
Transform3D rotY = new Transform3D();
vpTrans.getTransform(T3D);
translate.set(lastXcord, lastYcord, lastZcord);
angle += 0.05;
trueangle += 0.05;
rotX.rotX(angle);
rotY.rotY(Yangle);
test.mul(rotX, rotY);
test.setTranslation(translate);
vpTrans.setTransform(test);
}
What transformations/rotations I need to add to that so no matter my y-axis, camera will look up when mouse is moved up and down when mouse is moved down??

It appears you have a problem with the order of transformations. Unlike numerical multiplication, matrix multiplication is not commutative. 3D applications are very sensitive to the order of transformations.
In English:
if x and y are numbers, then x * y = y * x
However, if x and y are matrices, then x * y ≠ y * x!
With that in mind, let's look at what you are doing.
test.mul(rotX, rotY);
To visualize what is happening with this multiplication, imagine you have a straight line coming out of your head. We'll call this LineUp. Now look down at your keyboard, imagine LineUp tilting with you. Now turn your head 90 degrees on LineUp. That is what is happening to your rotation matrix. Crazy huh?
Now let's reverse the multiplication like so: test.mul(rotY, rotX);
So sit straight up and look forward, and turn your head/body/chair. Now look down a bit. You rotated on a true LineUp axis first, then applied the up/down transformation. Fun yeah?
A great way to think about matrix transformations is like an instruction set that has to be in order.
Turn head right. (rotY)
Turn head up. (rotX)
Move to computer (translate)
...etc
Changing the order of one operation changes all the subsequent instructions in funny and outrageous ways.
What you are building is a Camera, so I highly recommend reading how to build one.
A great exercise to understand matrix transformations is building a solar system, complete with orbiting moons and spinning planets. It will be eye opening and very informative.

Related

How to create a counter that updates via a mouse drag?

Lets say I have a circle, and if the user drags his mouse clockwise along the path of the circle the counter increases, and if he drags the mouse counter-clockwise it decreases. What is the best way to implement this in Java? I imagine trig will be needed so the program knows when the user is dragging clockwise or counter-clockwise, right? Not looking for code examples, just help with theory so I can begin with the right approach.
As you probably have access to the circle, get its center point coordinates.
Then get the coordinates of the mouse. After that you compute the angle between the vector and the x-axis.
You do so by first setting the circle point as the center of the imaginary coordinate system and then shifting the mouse coordinates to this system. After that you apply atan2 on the new mouse coordinates. The result is the desired angle.
Point center = ...;
Point mouse = ...;
Point shiftedMouse = new Point(mouse.x - center.x, mouse.y - center.y);
double angle = Math.atan2(shiftedMouse.y, shiftedMouse.x);
At this point you probably need to convert the result of angle to degrees or something like that, if you like. You may take a look at Wikipedia#atan2 for this.
Of course you can also leave it in the format (-pi, pi] and work with that, if you know what it means.
Now you track how this angle changes. If it increases, then the mouse is moving counter-clockwise; if it decreases, then clockwise and so on (or maybe the other way around, just try it). Take care of the bound where after 359° 0° and then 1° comes.

Java/LWJGL: Z axis in OpenGL behaving like left-handed coordinate system

Recently, I sought help regarding 3d camera rotations in OpenGL. This answer and the comments that followed helped me greatly, but there is still one major issue: when moving the camera, the motion is often, but not always, in exactly the opposite direction it should be. For instance, when the camera's orientation matrix is the identity, the camera moves perfectly. However, if it is rotated in any direction, its motion on the axis perpendicular to the axis of rotation will have the opposite sign of the intended motion.
With this said, I think I have an idea why this inconsistent behavior is happening:
As we all know, OpenGL uses a Right-Handed coordinate system:
If I understand this diagram correctly, when the camera is oriented at the identity the z axis should point INTO the camera, and z-values should decrease as one moves away from the camera (apparently affirmed here). (coordinates measured in world space).
However, in my program, the Z axis points AWAY from the camera and z values increase as one moves away from the camera. Here is an example:
The camera has moved forward, along what should be the negative z axis but appears to be the positive z axis.
If I am correct in interpreting this behavior as abnormal, it would explain all of my problems with the sign of my camera motion, as the motion that currently appears "correct" would in fact be erroneous and I have consistent signs that I could simply invert to result in correct motion.
So the question is:
Is my Z axis backwards, or is it supposed to be this way?
If it is backwards, why? Judging by multiple discussions on the topic (1, 2, 3), the error is likely to lie where I define my perspective frustum, so I'll put that here:
public static final int P_ZNEAR = 1, P_ZFAR = 500;
public static void perspective()
{
int i = GL11.glGetInteger(GL11.GL_MATRIX_MODE);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
double ymax, xmax;
ymax = P_ZNEAR * Math.tan(FOV / 2);
xmax = ymax * ASPECT_RATIO;
GL11.glFrustum(xmax, -xmax, -ymax, ymax, P_ZNEAR, P_ZFAR);
GL11.glMatrixMode(i);
}

Making a rectangle go around in a circle - Java/Swing

My maths isn't that good so I'm having a bit of trouble in one of my applications that I'm trying to do where I want a rectangle to represent a vehicle and I want that vehicle/rectangle to "drive" around in a circle. Imagine a roundabout with only 1 vehicle in it, just circling around forever.
If I can get some help how to do that then I'll be able to build on the example and most importantly learn.
If someone could write up a simple example for me I'd be grateful. No background no images, just a rectangle "driving" around in a circle. I'm using java and Swing.
Sorry, I am not sure if could understand clear you exactly need. If you need to draw rectangle which is moving around inside of circle, you can use sin/cos functions.
Something like that:
double r = 50.0; // radius (it might radius of your circle, but consider dimensions of rectangle to make sure you are drawing inside of circle, e.g. circleRadius - rectangeDimesion / 2.0)
for (int f = 0; f < 360; f++) {
double x = Math.sin(Math.toRadians((double)f)) * r;
double y = Math.cos(Math.toRadians((double)f)) * r;
// draw rectangle on [x, y] coordinates
}
If you know the radius of the round about, all the you would need would be a trigonometric function and the angle which the vehicle makes to the round about. You could take a look at this simple introduction which should get you started in the right direction.
On another hand, another approach would be to use a Transformation Matrix where you start with a matrix containing two points (your X and Y co-ordinates) and you transform them to become the new co-ordinates.
You can then rotate the rectangle to mimic a vehicle turning.
If you have a limited background in Mathematics, the first option might be easier for you to grasp.
This is more an extended comment than an answer.
I would divide the problem up into several easier problems, and work on each of them separately:
Draw your rectangle with a specified center location and long axis orientation.
Determine the center point and long axis orientation for an object orbiting around the origin. Note that to get make the long axis a tangent it needs to be perpendicular to the radius through the center.
Translate the whole system so that it orbits the desired point, rather than the origin.

Rotation in OpenGl ES to place objects then rotate the world

I am developing an augmented reality application for android and trying to use openGl to place cubes at locations in the world. My current method can be seen in the code below:
for(Marker ma: ARData.getMarkerlist().values()) {
Log.d("populating", "");
gl.glPushMatrix();
Location maLoc = new Location("loc");
maLoc.setLatitude(ma.lat);
maLoc.setLongitude(ma.lng);
maLoc.setAltitude(ma.alt);
float distance = currentLoc.distanceTo(maLoc);
float bearing = currentLoc.bearingTo(maLoc);
Log.d("distance", String.valueOf(distance));
Log.d("bearing", String.valueOf(bearing));
gl.glRotatef(bearing,0,0,1);
gl.glTranslatef(0,0,-distance);
ma.cube.draw(gl);
gl.glPopMatrix();
}
gl.glRotatef(y, 0, 1, 0);
gl.glRotatef(x, 1, 0, 0);`
Where y is yaw and x is the pitch. currently I am getting a single cube on the screen at a 45 degree angle someway in the distance. It looks like I am getting sensible bearing and distance values. Could it have something to do with the phones orientation? If you need more code let me know.
EDIT: I updated bearing rotation to gl.glRotatef(bearing,0,1,0); I am now getting my cubes mapped horizontally along the screen at different depths. Still no movement using heading and pitch but #Mirkules has identified some reasons why that might be.
EDIT 2: I am now attempting to place the cubes by rotating the matrix by the difference in angle between heading and bearing to a marker. However, all I get is a sort of jittering where the cubes appear to be rendered in a new position and then jump back to there old position. Code as above except for the following:
float angleDiff = bearing - y;
gl.glRotatef((angleDiff),0,1,0);
gl.glTranslatef(0,0,-distance);
bearing and y are both normalised to a 0 - 360 scale. Also, I moveed my "camera rotation" to above the code where I set the markers.
EDIT 3: I have heading working now using, float angleDiff = (bearing + y)/2;. However, I cant seem to get pitch working. I have attempted to use gl.glRotatef(-x,1,0,0); but that doesn't seem to work.
It's tricky to tell exactly what you're trying to do here, but there are a few things that stick out as potential problems.
Firstly, your final two rotations don't seem to actually apply to anything. If these are supposed to represent a movement of the world or camera (which mostly amounts to much the same thing) then they need to happen before drawing anything.
Then your rotations themselves perhaps won't entirely do what you intend.
Your cube is rotated around the Z axis. The usual convention in GL is for the camera to look down the Z axis, with the Y axis being considered 'up'. You can naturally interpret axes however you like, but a rotation around 'Z' would not typically be 'bearing', but 'roll'. 'Bearing' to me would be analogous to 'yaw'.
As you translate along the Z axis, I assume you are trying to position the object by rotating and translating, but obviously if the rotation is around the same axis as you translate along, it won't actually alter the position of the cube - it will always just be directly in front of the camera, spinning on its axis.
I'm not really clear on why you're trying to position the cube like that when it seems like you start off with a more specific location. You could probably directly construct a more appropriate matrix.
Finally, your camera/world rotation is two concatenated rotations around Y and X. You call these pitch and roll, but typically using euler angles for a camera rotation does not result in an intuitive result where terms like pitch and roll make complete sense. It is common to maintain an orientation and apply individual rotations to that in order to update it, rather than attempting to update several dependent rotations.
So yes, I would expect that this code, in the absence of other matrix operations, would likely result in drawing one or more cubes straight ahead which are simply rotated by some angle around the view direction.

Rotating quaternions based on mouse movement (OpenGL and Java)

I'm writing a game in Java using OpenGL (the LWJGL binding, to be specific). Each entity, including the camera, has a quaternion that represents it's rotation. I've figured out how to apply the quaternion to the current OpenGL matrix and everything rotates just fine. The issue I'm having is getting the camera to rotate with the mouse.
Right now, every frame, the game grabs the amount that the mouse has moved on one axis, then it applies that amount onto the quaternion for the camera's rotation. Here is the code that rotates the quaternion, I'll post it since I think it's where the problem lies (although I'm always wrong about this sort of stuff):
public void rotateX(float amount){
Quaternion rot = new Quaternion(1.0f, 0.0f, 0.0f, (float)Math.toRadians(amount));
Quaternion.mul(rot, rotation, rotation);
rotation.normalise();
}
This method is supposed to rotate the quaternion around the X axis. 'rotation' is the quaternion representing the entity's rotation. 'amount' is the amount that I want to rotate the quaternion (aka the amount that the mouse was moved). 'rot' is a normalized vector along the X axis with a w value of the amount converted to radians (I guess the goal here is to give it an angle- say, 10 degrees- and have it rotate the quaternion along the given axis by that angle). Using Quaternion.mul takes the new quaternion, multiplies it by the rotation quaternion, and then stores the result as the rotation quaternion. I don't know if the normalization is necessary, since 'rot' is normal and 'rotation' should already by normalized.
The rotateY and rotateZ methods do the same thing, except for changing the vector for 'rot' (0.0, 1.0, 0.0 for y and 0.0, 0.0, 1.0 for z).
The code appears to work fine when the game starts and the camera is looking down the negative Z axis. You can spin all the way around on the Y axis OR all the way around the X axis. But as soon as you try to rotate the camera while not looking down the Z axis, everything gets really screwy (I can't even describe it, it rotates very oddly).
My end goal here is to have something to use for controlling a ship in a space with no up vector. So when you move the mouse on the Y axis, no matter what angle the ship is at, it changes the pitch of the ship (rotation along the X axis). Similarly, when you move the mouse on the X axis, it changes the yaw (rotation along the Y axis). I might be going about this the wrong way and I probably just need a push (or shove) in the right direction.
If you need more details on anything (how my rendering is done, any other maths that I'm trying to do) just ask and I'll put it up. I understood everything when I was using euler angles (which apparently are a big no-no for 3D application development... wish somebody would have told me that before I sunk a lot of time into getting them to work) but as soon as I switched over to quaternions, I got in over my head really fast. I've spent the past few months just playing with this code and reading about quaternions trying to get it to work, but I haven't really gotten anywhere at all :'(
Very, very frustrating... starting to regret trying to make something in 3D >_<
Quaternion rot = new Quaternion(1.0f, 0.0f, 0.0f, (float)Math.toRadians(amount));
OK, this is flat-out wrong.
The constructor that takes four floats assumes that they represent an actual quaternion. What you give that constructor is not a quaternion; it's a vec3 axis and an angle that you expect to rotate around.
You can't shove those into a quaternion class and expect to get a legitimate quaternion out of it.
Your quaternion class should have a constructor or some other means of creating a quaternion from an angle and an axis of rotation. But according to the documentation you linked to, it does not. So you have to do it yourself.
A quaternion is not a vec3 axis with a fourth value that is an angle. A unit quaternion representation a change in orientation is a vec3 that is the axis of rotation * the sine of half of the angle of rotation, and a scalar component that is the cosine of half the angle of rotation. This assumes that the angle of rotation is clamped on the range [-pi/2, pi/2].
Therefore, what you want is this:
float radHalfAngle = ... / 2.0; //See below
float sinVal = Math.Sin(radHalfAngle);
float cosVal = Math.Cos(radHalfAngle);
float xVal = 1.0f * sinVal;
float yVal = 0.0f * sinVal; //Here for completeness.
float zVal = 0.0f * sinVal; //Here for completeness.
Quaternion rot = new Quaternion(xVal, yVal, zVal, cosVal);
Also, converting amount to radians directly doesn't make sense, particularly so if amount is just a pixel-coordinate delta that the mouse moved. You need some kind of conversion scale between the distance the mouse moves and how much you want to rotate. And toRadians is not the kind of scale you want.
One more thing. Left-multiplying rot, as you do here, will perform a rotation about the camera space X axis. If you want a rotation about the world-space X axis, you need to right-multiply it.

Categories

Resources