3D Camera class not working like it should - java

Can someone tell me why my camera class isn't working correctly? I set the position vector to (0,0,-10) and the look at vector to (0,0,0) but when I draw something on (0,0,0) it isn't there. I'm very new to vector math and matrix stuff, so I'm betting it's LookThrough() where the problem is.
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Matrix3f;
import org.lwjgl.util.vector.Vector3f;
public class Camera {
//3d vector to store the camera's position in
Vector3f position = null;
Vector3f lookAt = null;
//the rotation around the Y axis of the camera
float yaw = 0;
//the rotation around the X axis of the camera
float pitch = 0;
//the rotation around the Z axis of the camera
float roll = 0;
public Camera(float x, float y, float z)
{
//instantiate position Vector3f to the x y z params.
position = new Vector3f(x, y, z);
lookAt = new Vector3f();
}
public void lookThrough()
{
Matrix3f m = new Matrix3f();
Vector3f out = new Vector3f();
Vector3f.sub(position, lookAt, out);
out.normalise();
//set forward vector
m.m00 = out.x;
m.m01 = out.y;
m.m02 = out.z;
//set right vector
m.m10 = 1;
m.m11 = 0;
m.m12 = 0;
//set up vector
m.m20 = 0;
m.m21 = 1;
m.m22 = 0;
yaw = (float) -(Math.tan(m.m10/m.m00));
pitch = (float) -(Math.tan((-m.m20)/(Math.sqrt(Math.pow(m.m21, 2) + Math.pow(m.m22, 2)))));
roll = (float) -(Math.tan(m.m21/m.m22));
//roatate the pitch around the X axis
GL11.glRotatef(pitch, 1.0f, 0.0f, 0.0f);
//roatate the yaw around the Y axis
GL11.glRotatef(yaw, 0.0f, 1.0f, 0.0f);
//roatate the yaw around the Y axis
GL11.glRotatef(roll, 0.0f, 0.0f, 1.0f);
//translate to the position vector's location
GL11.glTranslatef(position.x, position.y, position.z);
}
}

There are a couple of things about your class that I would highlight:
1) You are fixing your 'right' and 'up' vectors, leaving only one degree of rotational freedom. As the camera re-orients in 3D space these will need to change. As it stands, your calculation for pitch always evaluates to 0, while your calculation to roll always evaluates to tan(1/0).
2) Though I'm not familiar with Java, you appear to be using the calculation (position - lookAt) to derive your forward vector. Ought it not be the reverse? The forward vector points away from the viewer's position.
3) Again - not familiar with java - but calling pow() to do a single multiplication is likely overkill.
1 & 2 could be the cause of your woes. Ultimately I would suggest taking a look at gluLookAt - assuming the GLU library is available in Java. If it is not, then look at the source code for gluLookAt (one variant is available here).

Related

Can't Correctly rotate 3D Point A to B (on X, Y, Z axis)

I has tirelessly been researching for three weeks now, each and every procedure for rotating a 3D Point 'A' to 3D Point 'B', the following are the techniques I attempted with no success:
Rotation Matrix
Euler Angles to Rotation Matrix
Axis Angle to Rotation Matrix
Quaternion Coordinate Rotation
Trigonometry Multiple Axis Rotation
I would like to perform a simultaneous 3d 3 axis (so, X, Y, Z) rotation in java (please know I don't particularly understand the mathematics behind it, I would prefer the answer to be in java code, with the example I displayed).
e.g.
Pointf a = new Pointf(0f, 2f, 0f);
Pointf b = new Pointf(2f, 0f, 2f);
// ~~~ Start of Rotation Matrix ~~~
// azimuth (Z Axis)
float azimuth = (float) Math.toRadians(90f);
// elevation (Y Axis)
float elevation = (float) Math.toRadians(0f);
// tilt (X Axis)
float tilt = (float) Math.toRadians(90f);
/*
public static Matrix4x4f createRotationMatrix(double azimuth, double elevation, double tilt) {
// Assuming the angles are in radians.
//Precompute sines and cosines of Euler angles
double su = sin(tilt);
double cu = cos(tilt);
double sv = sin(elevation);
double cv = cos(elevation);
double sw = sin(azimuth);
double cw = cos(azimuth);
//Create and populate RotationMatrix
Matrix4x4f A = Matrix4x4f.create();
A.values[0] = (float) (cv*cw);
A.values[1] = (float) ((su*sv*cw) - (cu*sw));
A.values[2] = (float) ((su*sw) + (cu*sv*cw));
A.values[4] = (float) (cv*sw);
A.values[5] = (float) ((cu*cw) + (su*sv*sw));
A.values[6] = (float) ((cu*sv*sw) - (su*cw));
A.values[8] = (float) -sv;
A.values[9] = (float) (su*cv);
A.values[10] = (float) (cu*cv);
return A;
}
*/
// Multiplies the Z * Y * X Rotation Matrices to form 'Matrix4x4f m'
Matrix4x4f m = Matrix4x4.createRotationMatrix(azimuth, elevation, tilt);
// Multiple point 'a' with Matrix4x4f 'm' to get point 'b'
m.transform(a); // Should return {2, 0, 2} same 'b', but returns {2, 0, 0}
// ~~~ End of Rotation Matrix ~~~
FYI. My main source of information was from the following:
http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToMatrix/index.htm
Thanks All
I can explain an algorithm for finding the matrix of rotation, though I don't have code for it.
Every rotation is around an axis. I assume that you want an axis that goes through the origin O. In that case, the rotation takes place in the plane defined by the three points O, A, and B. As the rotation axis, you can use the vector which is the cross product of the two vectors OA and OB. Here is the formula.
Let's call the three components of this direction vector for the axis (u,v,w), for simplicity (and we'll assume its normalized). Next find the angle theta between OA and OB, this is found by the standard formula for the angle between two vectors.
Finally, the hard part is done for you at this site, which links to the following 3D matrix of rotation about the origin, which will rotate A to B. Java code for this matrix can be downloaded at this site.
You can check out some rotations interactively here.

How does one angle axis transform a vector to match the direction of another?

I am making a space game for a school project and my ships' AI rely on the ability to move towards another ship. My current implementation does not work because it rotates the ship to both directions of my destination vector, so occasionally it attacking ship will go in the exact opposite direction, while other times it will go the correct direction. Any feedback would be greatly appreciated!
//ship heading (already calculated)
Vector3f heading /* = ... */;
heading.normalize();
//direction of enemy ship relative to ours
Vector3d direction = new Vector3f(enemy.x - ship.x, enemy.y - ship.y, enemy.z - ship.z);
direction.normalize();
//angle between vectors
float angle = heading.angle(direction);
//axis to rotate upon
Vector3f axis = new Vector3f();
axis.cross(heading, direction);
axis.normalize();
//initialize matrix to hold rotation
Matrix4f rot = new Matrix4f();
rot.setIdentity();
//rotate the ship if we are more than 10 deg off
if (angle > Math.toRadians(10)) {
rot.setRotation(new AxisAngle4f(axis, rotationVelocity * deltaTime));
}
Alright I have figured out a better way to do it, which involves some linear interpolation.
The following still remains the same:
//ship heading (already calculated)
Vector3f heading /* = ... */;
heading.normalize();
//direction of enemy ship relative to ours
Vector3d direction = new Vector3f(enemy.x - ship.x, enemy.y - ship.y, enemy.z - ship.z);
direction.normalize();
//angle between vectors
float angle = heading.angle(direction);
Now I calculate the percent of heading vector to be interpolated to the direction vector:
//calculate angle to rotate
float deltaRotation = rotationVelocity * deltaTime;
//calcuate percent of rotation
float percent = Math.min(deltaRotation / angle, 1.0f);
//create interpolated vector representing the new forward direction
Vector3f inter = new Vector3f(heading.x + percent * (direction.x - heading.x), heading.y + percent * (direction.y - heading.y), heading.z + percent * (direction.z - heading.z));
inter.normalize();
Now we can create the new rotation matrix:
//the object's default forward size without any transformations
Vector3f forward = new Vector3f(0.0f, 0.0f, 1.0f);
//rotation axis
Vector3f axis = new Vector3f();
axis.cross(forward, inter);
axis.normalize();
//New rotation matrix to be applied
Matrix4f rotationMatrix = new Matrix4f();
rotationMatrix.setIdentity();
rotationMatrix.setRotation(new AxisAngle4f(axis, forward.angle(inter)));
Now my ship always faces the correct direction! :)

Generate vertices for a polygon

I'm trying to make a useful/generic 2D polygon class for an OpenGL ES renderer.
When I create a polygon, I give it several parameters:
Polygon(Vector3 centerpoint, int numVertices, float inPolySize)
Then, I try to generate the vertices. This is where i'm having a tough time. I need to determine the number of vertices, get an angle, find the x/y position of that angle, someone take the size into account, AND offset by the position.
OpenGL works with big arrays of data. Nothing is nice like Lists of Vector3's. Instead it's float[] arrays, with the first index being X1, second being Y1, third being Z1, fourth being X2, etc...
final int XPOS = 0;
final int YPOS = 1;
final int ZPOS = 2;
int mvSize = 3; // (x, y, z);
float[] vertices = new float[mvSize * mNumVertices];
for (int verticeIndex = 0; verticeIndex < mNumVertices; verticeIndex++)
{
double angle = 2 * verticeIndex * Math.PI / mNumVertices;
vertices[mvSize * verticeIndex + XPOS] = (((float)Math.cos(angle)) * mPolygonSize) + mPosition.GetX();
vertices[mvSize * verticeIndex + YPOS] = (((float)Math.sin(angle)) * mPolygonSize) + mPosition.GetY();
vertices[mvSize * verticeIndex + ZPOS] = mPolygonSize + mPosition.GetZ();
}
Unfortunatley, my triangle is never quite right. It's skewed a lot, the size doesn't seem right...
I figure i'm throwing the size into the wrong formula, can anyone help?
EDIT:
Here's some sample data
Polygon test = new Polygon( new Vector3(0, 1, 0), 3, .5f);
vertices[0] = -0.25
vertices[1] = 1.4330127
vertices[2] = 0.0
vertices[3] = -0.25
vertices[4] = 0.5669873
vertices[5] = 0.0
vertices[6] = 0.5
vertices[7] = 1.0
vertices[8] = 0.0
vertices[9] = -0.25
vertices[10] = 1.4330127
vertices[11] = 0.0
I can't believe I was this stupid. Basically, my render window was smaller than my screen. If my screen is a rectangle, my render window was a square.
This being the case, any triangle I draw that was up was clipped by my render window. To me, it looked like the triangle was skewed. Really, it was just clipped!
The Java math library takes radians as input, not degrees. I didn't see the angles you were using for your calculation, but if you're not converting to radians from degrees, you will get some skewed looking shapes, and would explain that your calculations are correct, but the expected result is off.

Translate Java 3D coordinates to 2D screen coordinates

I'm working with a Java 3D application called "Walrus" that is used to display directed graphs. The code already has a feature to highlight a node and draw label adjacent in graph given its screen coordinates.
Upon rotating the screen, the node is no more highlighted.
What I have is the node coordinates in 3D. I need to draw label to it.
Code for highlight using 3D coordinates
Point3d p = new Point3d();
m_graph.getNodeCoordinates(node, p);
PointArray array = new PointArray(1, PointArray.COORDINATES);
array.setCoordinate(0, p);
m_parameters.putModelTransform(gc);
gc.setAppearance(m_parameters.getPickAppearance());
How can I draw Label with 3D coordinates( Raster graphics throws error Renderer: Error creating immediate mode Canvas3D graphics context )
How can I convert 3D coordinates to 2D screen and use existing code to draw label at 2D screen point
Thanks,
Dakshina
I have an algorithm/method for converting [x,y,z] into [x,y] with the depth parameter:
The x value is : (int) (x - (z / depth * x))
The y value is : (int) (y - (z / depth * y))
Essentially, the depth is the focal point. The vanishing point will be at [0,0,depth].
Here's what i used to convert my 3D coordinates into perspective 2D, x2 and y2 being the 2dimensional coordinates, xyz being the 3D coordinates.
use these formulas:
x2 = cos(30)*x - cos(30)*y
y2 = sin(30)*x + sin(30)*y + z
I picked the angle 30 as it is easy for perspective purposes, also used in Isometric grids for drawing 3D on 2D papers. As the z axe will be the vertical one, x and y are the ones at 60 degrees from it right and left. Isometric Grid Picture.
I'm still working on rotation, but without altering the axes, just coordinate rotation in 3D.
Enjoy.
I found the solution.
This is the function to display Text3D at image 2D coordinates
public void drawLabel(GraphicsContext3D gc, double x, double y, int zOffset, String s) {
boolean frontBufferRenderingState = gc.getFrontBufferRendering();
gc.setBufferOverride(true);
gc.setFrontBufferRendering(true);
Point3d eye = getEye();
double labelZ = zOffset * LABEL_Z_OFFSET_SCALE
+ LABEL_Z_SCALE * eye.z + LABEL_Z_OFFSET;
double xOffset = LABEL_X_OFFSET * m_pixelToMeterScale;
double yOffset = LABEL_Y_OFFSET * m_pixelToMeterScale;
Point3d p = new Point3d(x + xOffset, y + yOffset, 0.0);
{
// Project given (x, y) coordinates to the plane z=labelZ.
// Convert from image-plate to eye coordinates.
p.x -= eye.x;
p.y -= eye.y;
double inversePerspectiveScale = 1.0 - labelZ / eye.z;
p.x *= inversePerspectiveScale;
p.y *= inversePerspectiveScale;
// Convert from eye to image-plate coordinates.
p.x += eye.x;
p.y += eye.y;
}
Transform3D scale = new Transform3D();
scale.set(LABEL_SCALE);
Vector3d t = new Vector3d(p.x, p.y, labelZ);
Transform3D translation = new Transform3D();
translation.set(t);
translation.mul(scale);
Transform3D transform = new Transform3D(m_imageToVworld);
transform.mul(translation);
gc.setModelTransform(transform);
//-----------------
int fontSize=(int)(10*m_magnification);
if(fontSize>20)
fontSize=20;
//---------------
// XXX: Courier may not be available on all systems.
Text2D text = new Text2D(s, new Color3f(1.0f, 1.0f, 1.0f),
"Courier", fontSize, Font.BOLD);
gc.draw(text);
gc.flush(true);
// NOTE: Resetting the model transform here is very important.
// For some reason, not doing this causes the immediate
// following frame to render incorrectly (but subsequent
// frames will render correctly). In some ways, this
// makes sense, because most rendering code assumes that
// GraphicsContext3D has been set to some reasonable
// transform.
gc.setModelTransform(m_objectTransform);
gc.setFrontBufferRendering(frontBufferRenderingState);
}
This is the function to take 3D coordinates and convert them to image 2D coordinates and render using above function
private boolean displayOnScreenLabel(int node, String label) {
boolean success = false;
try {
Transform3D transform = m_parameters.getObjectToEyeTransform();
Point3d nodeC = new Point3d();
m_graph.getNodeCoordinates(node, nodeC);
transform.transform(nodeC);
Point3d eye = m_parameters.getEye();
double perspectiveScale = 1.0 / (1.0 - nodeC.z / eye.z);
double centerX = eye.x + nodeC.x * perspectiveScale;
double centerY = eye.y + nodeC.y * perspectiveScale;
GraphicsContext3D gc = m_canvas.getGraphicsContext3D();
m_parameters.drawLabel(gc, centerX, centerY, m_labelZOffsetCounter++, label);
success = true;
} catch (final java.lang.OutOfMemoryError error) {
JOptionPane.showMessageDialog(m_frame, "The 3D Graphics is unable to find enough memory on your system. Kill the application!", "Out Of Memory!", JOptionPane.ERROR_MESSAGE);
} catch (Exception e) {
success = false;
}
return success;
}

quaternion to angle

Alright, so this is how I am doing it:
float xrot = 0;
float yrot = 0;
float zrot = 0;
Quaternion q = new Quaternion().fromRotationMatrix(player.model.getRotation());
if (q.getW() > 1) {
q.normalizeLocal();
}
float angle = (float) (2 * Math.acos(q.getW()));
double s = Math.sqrt(1-q.getW()*q.getW());
// test to avoid divide by zero, s is always positive due to sqrt
// if s close to zero then direction of axis not important
if (s < 0.001) {
// if it is important that axis is normalised then replace with x=1; y=z=0;
xrot = q.getXf();
yrot = q.getYf();
zrot = q.getZf();
// z = q.getZ();
} else {
xrot = (float) (q.getXf() / s); // normalise axis
yrot = (float) (q.getYf() / s);
zrot = (float) (q.getZf() / s);
}
But it doesn't seem to work when I try to put it into use:
player.model.addTranslation(xrot * player.speed, 0, zrot * player.speed);
AddTranslation takes 3 numbers to move my model by than many spaces (x, y, z), but hen I give it the numbers above it doesn't move the model in the direction it has been rotated (on the XZ plane)
Why isn't this working?
Edit: new code, though it's about 45 degrees off now.
Vector3 move = new Vector3();
move = player.model.getRotation().applyPost(new Vector3(1,0,0), move);
move.multiplyLocal(-player.speed);
player.model.addTranslation(move);
xrot, yrot, and zrot define the axis of the rotation specified by the quaternion. I don't think you want to be using them in your addTranslation() call...in general, that won't have anything to do with the direction of motion.
What I mean by that is: your 3-D object -- let's say for the sake of argument that it's an airplane -- will have a certain preferred direction of motion in its original coordinate
system. So if the original orientation has the center of mass at the origin, and the
propeller somewhere along the +X axis, the plane wants to fly in the +X direction.
Now you introduce some coordinate transformation that rotates the airplane into some other orientation. That rotation is described by a rotation matrix, or equivalently, by a
quaternion. Which way does the plane want to move after the rotation?
You could find
that by taking a unit vector in the +X direction: (1.0, 0.0, 0.0), then applying the
rotation matrix or quaternion to that vector to transform it into the new coordinate
system. (Then scale it by the speed, as you're doing above.) The X, Y, and Z components
of the transformed, scaled vector give the desired incremental motion along each axis. That transformed vector is generally not going to be the rotation axis of the quaternion, and I think that's probably your problem.

Categories