package net.hvidtfeldts.meshia.engine3d; import com.jogamp.opengl.util.PMVMatrix; public class Camera { private final float[] right = new float[] { 0.7f, 0, -0.7f }; private float[] up = new float[] { 0.4f, -0.8f, 0.4f }; private float[] forward = new float[] { 0.57f, 0.57f, 0.57f }; private final float[] pos = new float[] { 0, 0, -6 }; public float[] getRight() { return right; } public float[] getUp() { return up; } public float[] getPosInCameraCoords() { return pos; } public float[] getPosInWorldCoords() { // [ r.x r.y r.z q.x ] // [ u.x u.y u.z q.y ] // [ -f.x -f.y -f.z q.z ] // [ 0 0 0 1 ] // eye = -(modelView[3].xyz)*mat3(modelView); return new float[] { -pos[0] * right[0] - pos[1] * up[0] + pos[2] * forward[0], -pos[0] * right[1] - pos[1] * up[1] + pos[2] * forward[1], -pos[0] * right[2] - pos[1] * up[2] + pos[2] * forward[2], }; } public void setUpDir(float[] up) { this.up = up; } public void setForward(float[] forward) { this.forward = forward; } public void rotateAboutUp(float angle) { float[] rot = rotation(angle, up); forward = multiply(rot, forward); ortogonalize(); } public void rotateAboutForward(float angle) { float[] rot = rotation(angle, forward); up = multiply(rot, up); ortogonalize(); } public void rotateAboutRight(float angle) { float[] rot = rotation(angle, right); up = multiply(rot, up); forward = multiply(rot, forward); ortogonalize(); } public static float[] multiply(float[] m, float[] v) { return new float[] { m[0] * v[0] + m[1 + 0] * v[1] + m[2 + 0] * v[2] + m[3], m[0 + 4] * v[0] + m[1 + 4] * v[1] + m[2 + 4] * v[2] + m[3 + 4], m[0 + 8] * v[0] + m[1 + 8] * v[1] + m[2 + 8] * v[2] + m[3 + 8], m[0 + 12] * v[0] + m[1 + 12] * v[1] + m[2 + 12] * v[2] + m[3 + 12], }; } public static float[] multiply2(float[] m, float[] v) { return new float[] { m[0] * v[0] + m[0 + 4] * v[1] + m[0 + 8] * v[2] + m[0 + 12], m[1] * v[0] + m[1 + 4] * v[1] + m[1 + 8] * v[2] + m[1 + 12], m[2] * v[0] + m[2 + 4] * v[1] + m[2 + 8] * v[2] + m[2 + 12], m[3] * v[0] + m[3 + 4] * v[1] + m[3 + 8] * v[2] + m[3 + 12], }; } public static float[] rotation(double angle, float[] v) { float[] m = new float[16]; float c = (float) Math.cos(angle); float s = (float) Math.sin(angle); float omc = 1 - c; float xs = v[0] * s; float ys = v[1] * s; float zs = v[2] * s; float xyomc = v[0] * v[1] * omc; float xzomc = v[0] * v[2] * omc; float yzomc = v[1] * v[2] * omc; m[0] = v[0] * v[0] * omc + c; m[1] = xyomc + zs; m[2] = xzomc - ys; m[3] = 0; m[4] = xyomc - zs; m[5] = v[1] * v[1] * omc + c; m[6] = yzomc + xs; m[7] = 0; m[8] = xzomc + ys; m[9] = yzomc - xs; m[10] = v[2] * v[2] * omc + c; m[11] = 0; m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1; return m; } public void ortogonalize() { // LxU = F; // UxF = L normalize(forward); normalize(up); up = subtract(up, mul(dot(forward, up), forward)); normalize(up); cross(up, forward, right); negate(right); // Logger.log("Up " + toString(up) + " Forward: " + toString(forward) + // " Left: " + toString(left)); } public void negate(float[] v) { v[0] = -v[0]; v[1] = -v[1]; v[2] = -v[2]; } public float dot(float[] a, float[] b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; } public float[] mul(float a, float[] b) { return new float[] { a * b[0], a * b[1], a * b[2] }; } public float[] subtract(float[] a, float[] b) { return new float[] { a[0] - b[0], a[1] - b[1], a[2] - b[2] }; } private void normalize(float[] a) { float l = (float) Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); a[0] /= l; a[1] /= l; a[2] /= l; } private void cross(float[] a, float[] b, float[] out) { out[0] = a[1] * b[2] - a[2] * b[1]; out[1] = -a[0] * b[2] + a[2] * b[0]; out[2] = a[0] * b[1] - a[1] * b[0]; } public void setMatrix(PMVMatrix m) { ortogonalize(); /* * Logger.log("Up:" + Arrays.toString(up)); * Logger.log("Right:" + Arrays.toString(right)); * Logger.log("F:" + Arrays.toString(forward)); * Logger.log("Pos:" + Arrays.toString(pos)); */ // For an idea of these, see: http://3dengine.org/Right-up-back_from_modelview // OpenGL is column-major based. // So these values: float[] values = new float[] { right[0], up[0], -forward[0], 0, right[1], up[1], -forward[1], 0, right[2], up[2], -forward[2], 0, pos[0], pos[1], pos[2], 1, }; // Correspond to this matrix: // [ r.x r.y r.z p.x ] // [ u.x u.y u.z p.y ] // [ -f.x -f.y -f.z p.z ] // [ 0 0 0 1 ] m.glLoadMatrixf(values, 0); } public void move(float x, float y, float z) { pos[0] += x; pos[1] += y; pos[2] += z; } public float[] getForward() { return forward; } }