recently I have been implementing 3D AABB's into my game engine, to accomplish rotations I use a simple method of rotating all 8 calculated corners around the center of the box using my Vector3f.rotate() method. But as you may notice below it is very inefficient. If you want to sort though the whole class here is the github (https://github.com/EquilibriumGames/Flounder-Engine/blob/master/src/flounder/physics/AABB.java) otherwise here is the snipit I need help with, I beleve there could be simpler methods out there but I want to know what you think. Thank you!
// Creates the 8 AABB corners and rotates them.
Vector3f FLL = new Vector3f(destination.minExtents.x, destination.minExtents.y, destination.minExtents.z);
Vector3f.rotate(FLL, rotation, FLL);
Vector3f FLR = new Vector3f(destination.maxExtents.x, destination.minExtents.y, destination.minExtents.z);
Vector3f.rotate(FLR, rotation, FLR);
Vector3f FUL = new Vector3f(destination.minExtents.x, destination.maxExtents.y, destination.minExtents.z);
Vector3f.rotate(FUL, rotation, FUL);
Vector3f FUR = new Vector3f(destination.maxExtents.x, destination.maxExtents.y, destination.minExtents.z);
Vector3f.rotate(FUR, rotation, FUR);
Vector3f BUR = new Vector3f(destination.maxExtents.x, destination.maxExtents.y, destination.maxExtents.z);
Vector3f.rotate(BUR, rotation, BUR);
Vector3f BUL = new Vector3f(destination.minExtents.x, destination.maxExtents.y, destination.maxExtents.z);
Vector3f.rotate(BUL, rotation, BUL);
Vector3f BLR = new Vector3f(destination.maxExtents.x, destination.minExtents.y, destination.maxExtents.z);
Vector3f.rotate(BLR, rotation, BLR);
Vector3f BLL = new Vector3f(destination.minExtents.x, destination.minExtents.y, destination.maxExtents.z);
Vector3f.rotate(BLL, rotation, BLL);
destination.minExtents = Maths.min(FLL, Maths.min(FLR, Maths.min(FUL, Maths.min(FUR, Maths.min(BUR, Maths.min(BUL, Maths.min(BLR, BLL)))))));
destination.maxExtents = Maths.max(FLL, Maths.max(FLR, Maths.max(FUL, Maths.max(FUR, Maths.max(BUR, Maths.max(BUL, Maths.max(BLR, BLL)))))));
Related
I am also fiddling with the global/local rotation problem and I cannnot put my finger on it. I used a lwjgl book for the implementation of my game, using openGL and LWJGL. I am using the JOML lib for vectors and matrices.
The modelview matrix construction is below. By the book, this is originally without local rotations, I added them myself. The idea is that each object has a global and local rotation. Those rotations get individually calculated and then are multiplied left/right side with the modelview matrix.
public Matrix4f getModelViewMatrix(Object obj, Matrix4f viewMatrix) {
Vector3f rotation = obj.getRot();
Vector3f localRot = obj.getLocalRot();
Matrix4f localRotMat = new Matrix4f().identity();
Matrix4f worldRotMat = new Matrix4f().identity();
localRotMat.rotateLocalX((float)Math.toRadians(localRot.x)).
rotateLocalY((float)Math.toRadians(localRot.y)).
rotateLocalZ((float)Math.toRadians(localRot.z));
worldRotMat.rotateX((float)Math.toRadians(-rotation.x)).
rotateY((float)Math.toRadians(-rotation.y)).
rotateZ((float)Math.toRadians(-rotation.z));
modelViewMatrix.identity().translate(obj.getPos());
modelViewMatrix.mulLocal(localRotMat);
modelViewMatrix.mul(worldRotMat);
modelViewMatrix.scale(obj.getScale());
Matrix4f viewCurr = new Matrix4f(viewMatrix);
return viewCurr.mul(modelViewMatrix);
}
This still results in local rotations around the 'wrong' axes. I've seen implementations using quaternions and read about gimbal lock and the like, but either the answers are very specific or too general for me. Furthermore, it would be great if I wouldn't need to use a quaternions implementation, as I would have to refactor a lot of code possibly.
Relevant code for the object class:
// Object class
private final Vector3f rot;
private final Vector3f localRot;
public Object() {
pos = new Vector3f(0, 0, 0);
scale = 1;
rot = new Vector3f(0, 0, 0);
localRot = new Vector3f(0, 0, 0);
}
// getters and setters for above
Can somebody explain what is wrong about the calculation of the rotations for the modelview matrix?
EDIT:
I can rewrite the code like below, which is a bit more in line with the hints from #GeestWagen. However, the 'local rotation' of my object is still displayed as global, so it indeed seems like it is applied 'the same' rotation twice. However, now I am stuck, because I cant find more documentation on these functions (rotateLocal/rotate).
modelViewMatrix.identity().translate(obj.getPos()).
rotateLocalX((float)Math.toRadians(-localRot.x)).
rotateLocalY((float)Math.toRadians(-localRot.y)).
rotateLocalZ((float)Math.toRadians(-localRot.z)).
rotateX((float)Math.toRadians(-rotation.x)).
rotateY((float)Math.toRadians(-rotation.y)).
rotateZ((float)Math.toRadians(-rotation.z)).
scale(obj.getScale());
Okay, I finally fixed it. It resulted in me doing a bunch more research. What I came up with was the following:
Vector3f rotation = obj.getRot();
Vector3f localRot = obj.getLocalRot();
Quaternionf rotationQ = new Quaternionf().
rotateAxis((float)Math.toRadians(-localRot.z), new Vector3f(0f, 0f, 1f)).
rotateAxis((float)Math.toRadians(-localRot.y), new Vector3f(0f, 1f, 0f)).
rotateAxis((float)Math.toRadians(-localRot.x), new Vector3f(1f, 0f, 0f)).
premul(new Quaternionf().
rotateX((float)Math.toRadians(-rotation.x)).
rotateY((float)Math.toRadians(-rotation.y)).
rotateZ((float)Math.toRadians(-rotation.z))
);
modelViewMatrix.identity().
translate(obj.getPos()).
rotate(rotationQ).
scale(obj.getScale());
This is inspired by among others this and this. What confused me a lot was the lack of hits on doing local and global rotations. Most stuff I was able to find was either. This creates a quaternion and sets the x, y, and z axes to the local rotation of the object. Then it pre-multiplies by a quaternion which axes are set to the global rotation of the object. Then this resulting quaternion is used for the modelView matrix.
Thus, for combining local and global rotations, a quaternion seems to be necessary. I thought it was used to make sure the axes do not change in a local/global rotation, but they should also be used when combining both.
I'm working on a game in Java using LWJGL and I'm trying to make it rotate to look at a point in 3D space. I have tried every "lookAt" method I can find on the internet, But I just can't get it to work (The camera just doesn't look in the right direction).
Could you help me figure out how to do this?
EDIT:
Figured it out, Just needed to invert the matrix. Here is the final code if you were having the same issue:
public static Matrix4f lookAt(Vector3f eye, Vector3f center, Vector3f up) {
Vector3f forward = new Vector3f(0, 0, 0);
Vector3f.sub(center, eye, forward);
forward.normalise();
Vector3f side = new Vector3f(0, 0, 0);
Vector3f.cross(forward, up, side);
side.normalise();
Vector3f.cross(side, forward, up);
Matrix4f matrix = new Matrix4f();
matrix.m00 = side.x;
matrix.m01 = side.y;
matrix.m02 = side.z;
matrix.m10 = up.x;
matrix.m11 = up.y;
matrix.m12 = up.z;
matrix.m20 = -forward.x;
matrix.m21 = -forward.y;
matrix.m22 = -forward.z;
matrix.invert();
return matrix;
}
I would like to rotate an object to face a point which I'm have a bit of of trouble with.
So I'm starting with an object that has a base at zero and is aligned on the y axis.
I would like to rotate it so that the top of the object is facing the destination
My process so far is to:
Given axis A
find the distance between my position and my look position: D
create a direction vector: V = D.normalize()
find the right vector: R = A cross D
find the up vector: U = D cross R
find the angle between up and direction: ANGLE = acos((U dot D) / (U.length * D.length))
rotate by angle scaled by direction on each axis
here is the code representation of that. I'm not sure what exactly is wrong with this I've worked it out on paper and to my knowledge this approach should work but the results are completely incorrect when drawn. If anyone sees any flaws and could point me in the right direction it would be great.
Vector3 distance = new Vector3(from.x, from.y, from.z).sub(to.x, to.y, to.z);
final Vector3 axis = new Vector3(0, 1, 0);
final Vector3 direction = distance.clone().normalize();
final Vector3 right = (axis.clone().cross(direction));
final Vector3 up = (distance.clone().cross(right));
float angle = (float) Math.acos((up.dot(direction)/ (up.length() * direction.length())));
bondObject.rotateLocal(angle, direction.x , direction.y, direction.z);
The basic idea here is as follows.
Determine which way the object is facing: directionA
Determine which way the object should be facing: directionB
Determine the angle between those directions: rotationAngle
Determine the rotation axis: rotationAxis
Here's the modified code.
Vector3 distance = new Vector3(from.x, from.y, from.z).sub(to.x, to.y, to.z);
if (distance.length() < DISTANCE_EPSILON)
{
//exit - don't do any rotation
//distance is too small for rotation to be numerically stable
}
//Don't actually need to call normalize for directionA - just doing it to indicate
//that this vector must be normalized.
final Vector3 directionA = new Vector3(0, 1, 0).normalize();
final Vector3 directionB = distance.clone().normalize();
float rotationAngle = (float)Math.acos(directionA.dot(directionB));
if (Math.abs(rotationAngle) < ANGLE_EPSILON)
{
//exit - don't do any rotation
//angle is too small for rotation to be numerically stable
}
final Vector3 rotationAxis = directionA.clone().cross(directionB).normalize();
//rotate object about rotationAxis by rotationAngle
i have made a transform and rendered a Polygon object with it(mesh is of type Polygon):
at.setToTranslation(gameObject.position.x, gameObject.position.y);
at.rotate(Math.toRadians(rotation));
at.scale(scale, scale);
g2d.setTransform(at);
g2d.fillPolygon(mesh);
now i want to return the exact mesh i rendered so that i can do collision checks on it. only problem is that if i return mesh it returns the un-transformed mesh. so i tried setting the transform to the Polygon object (mesh) like so:
mesh = (Polygon)at.createTransformedShape(mesh);
but unfortunately at.createTransformedShape() returns a Shape that can only be casted to Path2D.Double. so if anyone knows how to convert Path2D.Double to Polygon or knows another way to set the transformations to the mesh please please help.
If AffineTransform#createTransformedShape doesn't provide the desired result for Polygons (as it seems to be the case), you can split the Polygon into Points, transform each Point and combine into a new Polygon. Try:
//Polygon mesh
//AffineTransform at
int[] x = mesh.xpoints;
int[] y = mesh.ypoints;
int[] rx = new int[x.length];
int[] ry = new int[y.length];
for(int i=0; i<mesh.npoints; i++){
Point2d p = new Point2d.Double(x[i], y[i]);
at.transform(p,p);
rx[i]=p.x;
ry[i]=p.y;
}
mesh = new Polygon(rx, ry, mesh.npoints)
I've been playing with some algorithms on the internet for a while and I can't seem to get them to work, so I'm tossing the question out here;
I am attempting to render a velocity vector line from a point. Drawing the line isn't difficult: just insert a line with length velocity.length into the graph. This puts the line centered at the point in the y-axis direction. We need to get this now in the proper rotation and translation.
The translational vector is not difficult to calculate: it is half the velocity vector. The rotational matrix, however, is being exceedingly elusive to me. Given a directional vector <x, y, z>, what's the matrix I need?
Edit 1: Look; if you don't understand the question, you probably won't be able to give me an answer.
Here is what I currently have:
Vector3f translation = new Vector3f();
translation.scale(1f/2f, body.velocity);
Vector3f vec_z = (Vector3f) body.velocity.clone();
vec_z.normalize();
Vector3f vec_y; // reference vector, will correct later
if (vec_z.x == 0 && vec_z.z == 0) {
vec_y = new Vector3f(-vec_z.y, 0f, 0f); // could be optimized
} else {
vec_y = new Vector3f(0f, 1f, 0f);
}
Vector3f vec_x = new Vector3f();
vec_x.cross(vec_y, vec_z);
vec_z.normalize();
vec_y.cross(vec_x, vec_z);
vec_y.normalize();
vec_y.negate();
Matrix3f rotation = new Matrix3f(
vec_z.z, vec_z.x, vec_z.y,
vec_x.z, vec_x.x, vec_x.y,
vec_y.z, vec_y.x, vec_y.y
);
arrowTransform3D.set(rotation, translation, 1f);
based off of this article. And yes, I've tried the standard rotation matrix (vec_x.x, vec_y.x, etc) and it didn't work. I've been rotating the columns and rows to see if there's any effect.
Edit 2:
Apologies about the rude wording of my comments.
So it looks like there were a combination of two errors; one of which House MD pointed out (really bad naming of variables: vec_z was actually vec_y, and so on), and the other was that I needed to invert the matrix before passing it off to the rendering engine (transposing was close!). So the modified code is:
Vector3f vec_y = (Vector3f) body.velocity.clone();
vec_y.normalize();
Vector3f vec_x; // reference vector, will correct later
if (vec_y.x == 0 && vec_y.z == 0) {
vec_x = new Vector3f(-vec_y.y, 0f, 0f); // could be optimized
} else {
vec_x = new Vector3f(0f, 1f, 0f);
}
Vector3f vec_z = new Vector3f();
vec_z.cross(vec_x, vec_y);
vec_z.normalize();
vec_x.cross(vec_z, vec_y);
vec_x.normalize();
vec_x.negate();
Matrix3f rotation = new Matrix3f(
vec_x.x, vec_x.y, vec_x.z,
vec_y.x, vec_y.y, vec_y.z,
vec_z.x, vec_z.y, vec_z.z
);
rotation.invert();
This should do you
Dupe.
The question there involves getting a rotation to a certain axis, whereas I'm concerned with getting a rotation matrix.
Gee, I wonder if you could turn convert one to the other?
BTW, your current solution of picking an arbitrary y axis and then reorthogonalising should work fine; it looks bugged though, or at least badly written. 'z_vec' is not a good variable-name for the y-axis. What's with the 'z,x,y' ordering, anyway?
If it still doesn't work, try making random changes until it does - transpose the matrix, negate vectors until you have an even number of sign errors, that kind of thing.
Also your tone of voice comes across as sort-of rude, given that you're asking strangers to spend their time helping you.