Whats wrong with my Matrix Rotation? - java

I'm trying to rotate a model by
(float) Math.atan2(-camX.getXf() * padX, -camDir.getZf() * padY)
Y
and
-MathUtils.HALF_PI
Z
But
model.setRotation(new Matrix3(1,0,0,
0,(float) Math.atan2(-camX.getXf() * padX, -camDir.getZf() * padY),0,
0,0,-MathUtils.HALF_PI));
It rotates on the y axis (Though it's sideways because it's a md2 model) but rotating the Z axis doesn't make it right side up. Any idea why?
Each variable is in it's respective area of the matrix.
EDIT: alright, now I'm using this code:
float x = 0;
float y = (float) Math.atan2(-camX.getXf() * padX, -camDir.getZf() * padY);
float z = (float) -MathUtils.HALF_PI;
float a = (float) Math.sin(x);
float A = (float) Math.cos(x);
float b = (float) Math.sin(y);
float B = (float) Math.cos(y);
float c = (float) Math.sin(z);
float C = (float) Math.cos(z);
Matrix3 m = new Matrix3(A*b, -(B*a),b,
(C*a)+(A*b*c), (A*C)-(a*b*c), -(B*c),
(a*c)-(A*C*b), (A*c)+(C*a*b), B*C);
But now none of the axis are rotating correctly.
This is how the matrix is set up:
xx, xy, xz,
yx, yy, yz,
zx, zy, zz

Rotation matrices don't work this way. Angles don't go into matrices! Instead I assume that Java handles a rotation matrix just like any other transformation matrix in cartesian coordinates. Since I think you don't want to input the rotation matrix by hand, you are probably better off starting with a new Matrix3 (I hope it is automatically initialized at the identity matrix), and then successively rotating it using rotateX(float x), rotateY(float y) and rotateZ(float z), where x, y, z are the angles you want to rotate about. (In case you are using com.threed.jpct.Matrix, at least.) Note that the result does depend on the succession of the three rotations.

Here is a typical tutorial on how to use rotation matrices http://www.euclideanspace.com/maths/algebra/matrix/orthogonal/rotation/index.htm. The order of applying rotations round the three axes is critical. Alternatively you can rotate about an arbitrary axis. Also you may want to explore quaternions.

This is what a rotation matrix looks like in 2D; it rotates a point in (x,y) space about the z-axis in the counterclockwise direction.
http://en.wikipedia.org/wiki/Rotation_matrix

Related

What is the correct way to rotate a quaternion from its current rotation a certain angle?

I am trying to use quaternion rotation using the quaternion from JOML: https://github.com/JOML-CI/JOML/blob/main/src/org/joml/Quaternionf.java.
I can get objects to rotate but they get stuck and are unable to complete a full rotation. The object is being updated every frame.
Edit:
Removed all euler related code and am simply trying to get the object to rotate on a single axis based on a certain angle.
Edit 2:
Am trying to use the conjugate and multiplying the quaternions together like I have seen in some videos. I'm not quite there though as the model spins itself off the screen for some reason.
Edit 3:
Normalizing the quaternion fixed the disappearing behaviour. The issue seems to be that there's no simple way to rotate a certain amount without either having a timer to lerp over which will not work in my case as I am trying to rotate an object an arbitrary amount with no set beginning and end.
Rotation function
public void rotate(float angle, float x, float y, float z) {
Quaternionf rot = new Quaternionf();
rot.rotateAxis((float) Math.toRadians(angle), x, y, z);
Quaternionf conjugate = rot.conjugate();
rotation = rot.mul(rotation).mul(conjugate);
}
Calling the rotation function
entity.transform.rotate( 1,0,1, 0);
It does not matter whether you transform your Euler angles into a matrix or a quaternion or whatever representation: As long as you use Euler angles to represent an orientation, Gimbal Lock is unavoidable.
So, in order to avoid Gimbal Lock from happening, you must discard using Euler Angles and stay with one representation for an orientation (like a 3x3 matrix or a quaternion) and apply delta changes to them, instead of trying to represent a full orientation as three Euler angles. So, whenever you - let's say - rotate the object a few degrees around a certain axis, you apply that delta change or orientation to the matrix/quaternion.
I believe I have figured it out.
You need to create a quaternion and rotate it to your delta values, do not manipulate quaternion values directly (e.g. use rotationX function instead).
To add quaternions together you multiply them.
Finally you need to use the equation:
delta quaternion * quaternion to rotate * inverse of delta quaternion
Code:
public void rotate(float x, float y, float z) {
//Use modulus to fix values to below 360 then convert values to radians
float newX = (float) Math.toRadians(x % 360);
float newY = (float) Math.toRadians(y % 360);
float newZ = (float) Math.toRadians(z % 360);
//Create a quaternion with the delta rotation values
Quaternionf rotationDelta = new Quaternionf();
rotationDelta.rotationXYZ(newX, newY, newZ);
//Calculate the inverse of the delta quaternion
Quaternionf conjugate = rotationDelta.conjugate();
//Multiply this transform by the rotation delta quaternion and its inverse
rotation.mul(rotationDelta).mul(conjugate);
}

Rotating point about pivot point in 3d space

I've made a Java program that displays lines in 3d space projected onto the 2d view, and so far, it's been working pretty well. I tried to make it possible to essentially rotate the 'world' about the camera's position about any axis, but now I'm running into some problems.
public void rotate(){
float ax = main.angleX; //main = camera
float ay = main.angleY;
float az = main.angleZ;
for(Line3d line : lines){ //all lines in the world
Vector3d start = Vector3d.Vector3dPMinus(line.start, main.getPoint()); //vetor value of starting point of line - camera's position
Vector3d end = Vector3d.Vector3dPMinus(line.end, main.getPoint());
start.rotate(ax, ay, az);
end.rotate(ax, ay, az); //rotate each vector
line.start = Point3d.pointFromVector3d(start).add(main.getPoint());
line.end = Point3d.pointFromVector3d(end).add(main.getPoint()); //vectors back into points
}
}
Rotation function:
public Vector3d rotate(float ax, float ay, float az){
Math.toRadians(ax *= 90);
Math.toRadians(ay *= 90);
Math.toRadians(az *= 90);
y = (float) (y * Math.cos(ax) - z * Math.sin(ax));
z = (float) (y * Math.sin(ax) + z * Math.cos(ax));
x = (float) (x * Math.cos(ay) + z * Math.sin(ay));
z = (float) (z * Math.cos(ay) - x * Math.sin(ay));
x = (float) (x * Math.cos(az) - y * Math.sin(az));
y = (float) (x * Math.sin(az) + y * Math.cos(az));
return this;
}
I've set it rotate about the x axis 3 times per second, and it displays exactly what I want it to before it starts rotating, but once it starts rotating, there's just some unidentifiable mess of usually just one horizontal line.
Is the method I used for rotating not right? Is there a better way to do it?
A Rotation of a 3d vector around the 3 axis is not that trivial as one might think. The thing you are probably trying to do is rotating with so called Euler Angles. You should be familiar with matrices to work with 3d space. I checked your rotations and they should work fine. BUT you should keep the following in mind: When you are rotating around an angle. The following rotations are affected by the previous rotation. You are rotating the rotation axis too.
To avoid this behaivour one ugly possibility is to rotate around a free axis. And when you are rotating around the x axis first. You rotate the y axis in reverse and rotate then your point around this y'. When you are rotating around z you need to rotate z axis with your reversed x and y rotation and then rotate around this z'. When you are doing this, you can always rotate around your world coordinatesystem. You should really use some math Library to accomplish this. It makes your life much easier. When you want to code it yourself. You need a proper matrice class with multiplication of matrices and vectors and some inversion method.
Your approach appears to take a reasonable general form. It looks like you are rotating the line endpoints relative to the current camera position, which is correct, and the three specific rotations you are performing could also be correct (but see below).
However, the three Math.toRadians() calls cannot be doing anything useful, because you ignore their results. Moreover, the expression ax *= 90 and its mates look awfully suspicious: are ax, ay, and az really expressed as fractions of a quarter-circle? That seems doubtful, and if it were the case then you would want to multiply by Math.PI/2 and skip toRadians(). On the other hand, if they are expressed in degrees then the following version of rotate() is correct for one reasonable definition of ax, ay, and az, and some possible Vector3D implementations:
public Vector3d rotate(double ax, double ay, double az){
ax = Math.toRadians(ax);
ay = Math.toRadians(ay);
az = Math.toRadians(az);
y = (float) ( y * Math.cos(ax) - z * Math.sin(ax));
z = (float) ( y * Math.sin(ax) + z * Math.cos(ax));
x = (float) ( x * Math.cos(ay) + z * Math.sin(ay));
z = (float) (-x * Math.sin(ay) + z * Math.cos(ay));
x = (float) ( x * Math.cos(az) - y * Math.sin(az));
y = (float) ( x * Math.sin(az) + y * Math.cos(az));
return this;
}
Overall, though, I am also a bit dubious of the correctness of the ax, ay, and az for this purpose. In particular, be aware that you cannot just accumulate separate x, y, and z rotation increments independently, as the resulting aggregate rotation depends greatly on the order in which the incremental rotations are performed. Moreover, even if ax, ay, and az correctly describe the orientation of the camera, it is unlikely that applying the same rotation to the world is what you actually want to do.
DESPITE ALL THE FOREGOING, though, I don't see any reason why your code would distort the model as you describe it doing. I don't think it will apply the rotation you want (even with my suggested fix), but the reason for the distortion is likely somewhere else.

How can I check if an object(s) are in front of the camera?

I have got some trees, which are greatly lagging the game, so I would like to check if the trees are in front of the camera or not.
I have had some help from the Mathematics forum, and also had a look at This link to help me convert pitch/yaw to the directional vector needed.
But for some reason, whenever I move the camera to the left, the trees become visible, wheras whenever I move it to the right, they become unvisible (So if camera is pointing at +1 on the Z axis, it seems to be rendering the trees, but -1 on the Z axis and it seems to not render them).
(See http://i.gyazo.com/cdd05dc3f5dbdc07577c6e41fab3a549 for a less-jumpy .mp4)
I am using the following code to check if an object is in front of the camera or not:
Ship you = shipsID.get(UID);
int dis = 300;
Vector3f X = new Vector3f(camera.x(), camera.y(), camera.z());
float x = (float) (Math.cos(Math.toRadians(camera.yaw()))*Math.cos(Math.toRadians(camera.pitch())));
float y = (float) (Math.sin(Math.toRadians(camera.yaw()))*Math.cos(Math.toRadians(camera.pitch())));
float z = (float) Math.sin(Math.toRadians(camera.pitch()));
Vector3f V = new Vector3f(x, y, z);
for (Tree tree : trees){
Vector3f Y = new Vector3f(tree.location.x, tree.location.y, tree.location.z);
Vector3f YMinusX = Y.negate(X);//new Vector3f(Y.x - X.x, Y.y - X.y, Y.z - X.z);
float dot = Vector3f.dot(YMinusX, V);
if (dot > 0){
tree.render();
}
}
Is anyone able to tell me what I have done wrong here? I can't work out if it's the math.. Or the code.. Or what?
Camera translation code:
public void applyTranslations() {
glPushAttrib(GL_TRANSFORM_BIT);
glMatrixMode(GL_MODELVIEW);
glRotatef(pitch, 1, 0, 0);
glRotatef(yaw, 0, 1, 0);
lastYaw = yaw;
glRotatef(roll, 0, 0, 1);
glTranslatef(-x, -y, -z);
glPopAttrib();
}
UPDATE:
It appears to be where the camera is looking. For example, if I look to -Z, nothing happens, but if I look to +Z, they all render.
The if (dot > 0) code appears to somehow being +Z rather than +TheCameraRotation.
Your camera rotations yaw around Y, implying Y is your up vector. However, float z = (float) Math.sin(Math.toRadians(camera.pitch())); gives Z for your up vector. There is an inconsistency. I'd start by swapping y and z here, then print everything out every frame so you can see what happens as you rotate the camera. Also render just one tree and print dot. E.g. you might quickly notice the numbers approach 1.0 only when you look at 90 degrees left of the tree which narrows down the problem. As #DWilches notes, swapping cos/sin will change the phase of the rotation, which would produce such an effect.
You might consider limiting the dot product to the camera's field of view. There are still problems in that trees are not just points. A better way would be to test tree bounding boxes against the camera frustum, as #glampert suggests.
Still, the tree geometry doesn't look that complex. Optimization wise, I'd start trying to draw them faster. Are you using VBOs? Perhaps look at methods to reduce draw calls such as instancing. Perhaps even use a few models for LOD or billboards. Going even further, billboards with multiple trees on them. Occlusion culling methods could be used to ignore trees behind mountains.
[EDIT]
Since your trees are all roughly on a plane, you could limit the problem to the camera's yaw:
float angleToTree = Math.atan2(tree.location.z - camera.z(), tree.location.x - camera.x());
float angleDiff = angleToTree - camera.yaw();
if (angleDiff > Math.PI)
angleDiff -= 2.0f * Math.PI;
if (angleDiff < -Math.PI)
angleDiff += 2.0f * Math.PI;
if (abs(angleDiff) < cameraFOV + 0.1f) //bias as trees are not points
tree.render();
Could you write it like this
Ship you = shipsID.get(UID);
int dis = 300;
Vector3f X = new Vector3f(camera.x(), camera.y(), camera.z());
float x = (float) (Math.cos(Math.toRadians(camera.yaw()))*Math.cos(Math.toRadians(camera.pitch())));
float y = (float) (Math.sin(Math.toRadians(camera.yaw()))*Math.cos(Math.toRadians(camera.pitch())));
float z = (float) Math.sin(Math.toRadians(camera.pitch()));
Vector3f V = new Vector3f(x, y, z);
for (Tree tree : trees){
Vector3f Y = new Vector3f(tree.location.x, tree.location.y, tree.location.z);
Vector3f YMinusX = Y.negate(X);//new Vector3f(Y.x - X.x, Y.y - X.y, Y.z - X.z);
float dot = Vector3f.dot(YMinusX, V);
if (dot > 0){
tree.render();
}
}
As you can see there is far less calculation being performed for each tree.
For what I see here the correct formulas are:
x = Math.sin(pitch) * Math.cos(yaw);
y = Math.sin(pitch) * Math.sin(yaw);
z = Math.cos(pitch);
Could you try them ?

PVector Heading() for 3D Rotation

I want to use the heading() function in the PVector class, but I am using P3D and have an x,y,and z for my PVector. How would I re-write this function to allow it to work for 3D space? My goal is to do something like:
size(100, 100, P3D);
PVector v = new PVector(.2, .11, .54);
rotate( v.heading() ); //set rotation from PVector named v
line(0, 0, 10, 0); //draw line that will be rotated in correct direction
The above code doesn't display the correct rotation in 3D space since v.heading() is only suited for 2D coordinate space. How can I achieve this using rotateX(), rotateY(), rotateZ()? I'd like to avoid using a quaternion if possible and use the rotate functions.
Thanks in advance!!
Using the heading() function in 2D is transforming 2D Cartesian coordinates (x,y) into 2D polar coordinates (radius, angle). A 3D version of polar coordinates is spherical coordinates:
The 3D point P can be represented as three linear coordinates (x,y,z) or one linear coordinate and two angles (rho, theta, phi). Rho is the length of the vector, theta is the angle in the x-y plane, and phi is the angle in the angle into the z plane. These equations describe the conversion:
rho = sqrt(x^2 + y^2 + z^2)
phi = arccos(z/rho)
theta = arctan(y/x)
You should be able to use the rotateX(), etc functions using these angles. Note that this uses the mathematics convention for the angle names theta and phi; in physics, these labels are reversed from what's shown above.
What kevinsa5 said, except use the inverse sine function to get the elevation. Use the atan2 function for the azimuth (or better, simply use the vector's 2D heading method). And use the vector's mag method for its magnitude.
rho = v.mag();
phi = asin(v.z/rho);
theta = atan2(v.y, v.x);
Working backwards, think "X - Y - Z" and try:
PVector p = new PVector(v.mag(), 0, 0); // X
rotateY3D(p, phi); // Y
rotateZ3D(p, theta); // Z
Then compare p with the original v.

Move Camera Around Sphere

I'm trying to move my camera in a spherical motion around a model in my world. I've seen converting spherical coordinates(rho, theta, phi) to cartesian coordinates (x, y, z), but I'm not sure how to go about setting this up. Here is what I've tried to so far but it isn't continuously orbiting the model. It gets to a certain point and then the rotation seems to reverse itself.
Initialize theta and phi:
private float theta = 0.0f;
private float phi = 0.0f;
Update theta and phi each frame:
// This should move the camera toward the upper-right continuously, correct?
theta = (theta+1.0f)%360;
phi = (phi+1.0f)%360;
Convert theta and phi to cartesian coordinates for the camera:
camera.position.x = CAMERA_DISTANCE * (float)Math.sin(theta*MathHelper.PIOVER180) * (float)Math.cos(phi*MathHelper.PIOVER180);
camera.position.y = CAMERA_DISTANCE * (float)Math.sin(theta*MathHelper.PIOVER180) * (float)Math.sin(phi*MathHelper.PIOVER180);
camera.position.z = CAMERA_DISTANCE * (float)Math.cos(theta*MathHelper.PIOVER180);
Then update the camera look at point and view matrix:
camera.lookAt(0, 0, 0);
camera.update();
Note:
I am using Java on Android with the libGDX framework and I am trying to control the rotation using an 2D on-screen virtual joystick and I still need to find a way to map the joystick to theta and phi.
Any help is greatly appreciated!
I recently did something just like this. This website helped me a lot to visualize what I needed to do.
What you need to do is convert your local joystick coordinates (relative to it's center) to pitch and yaw values:
public float getPitch()
{
return (position.X - center.X) * MathHelper.PIOVER180;
}
public float getYaw()
{
return (position.Y - center.Y) * MathHelper.PIOVER180;
}
Then you can use a quaternion to represent it's rotation:
public void rotate(float pitch, float yaw, float roll)
{
newQuat.setEulerAngles(-pitch, -yaw, roll);
rotationQuat.mulLeft(newQuat);
}
Then you can apply the quaternion to the camera's view matrix using libGDX's built in rotate(quaternion) method:
camera.view.rotate(rotationQuat);
// Remember to set the camera to "look at" your model
camera.lookAt(0, 0, 0);

Categories