使用 lwjgl 实现第三人称相机

implementing a 3rd person camera using lwjgl

我试过多次实现第三人称相机,但我似乎无法让它工作。移动和缩放都很好,但是旋转给我带来了很多麻烦:

  1. 当改变相机的俯仰角时(即从你看玩家的高度),相机总是在玩家的位置绕世界 x 轴旋转,而不是相机 x轴

  2. 当改变玩家周围的角度时,相机总是在玩家位置围绕世界 z 轴旋转,而不是相机 z 轴

  3. 当向任何方向旋转相机时,它会慢慢地向该方向移动,而不是停留在玩家位置的固定偏移量

我已经尝试过更改矩阵乘法顺序,但这没有帮助,我也尝试过使用查看矩阵,但这根本不起作用,造成了各种其他问题。我知道,使用数学库会使这件事变得容易得多,但这不是我的目标。我想至少尝试理解这背后的数学原理并理解我的错误。

我的 Matrix4f class:


import java.nio.FloatBuffer;
import java.util.Arrays;
import java.util.Iterator;

import com.engine.toolbox.ArrayUtils;
import com.engine.toolbox.BufferUtils;
import com.engine.toolbox.math.vectors.Vector3f;
import com.engine.toolbox.math.vectors.Vector4f;

public class Matrix4f extends Matrix{

    public float m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44;

    public Matrix4f(float m11, float m12, float m13, float m14,
                    float m21, float m22, float m23, float m24,
                    float m31, float m32, float m33, float m34,
                    float m41, float m42, float m43, float m44) {
        super(new Float[] { m11, m12, m13, m14, 
                            m21, m22, m23, m24, 
                            m31, m32, m33, m34, 
                            m41, m42, m43, m44});
        this.m11 = m11;
        this.m12 = m12;
        this.m13 = m13;
        this.m14 = m14;
        this.m21 = m21;
        this.m22 = m22;
        this.m23 = m23;
        this.m24 = m24;
        this.m31 = m31;
        this.m32 = m32;
        this.m33 = m33;
        this.m34 = m34;
        this.m41 = m41;
        this.m42 = m42;
        this.m43 = m43;
        this.m44 = m44;

    }

    @Override
    public Iterator<Float> iterator() {
        return Arrays.asList(this.toRawTypes()).iterator();
    }

    public Matrix4f invert() {
        Matrix4f matrixOfMinors =   new Matrix4f(   new Matrix3f(   m22, m23, m24,
                                                                    m32, m33, m34,
                                                                    m42, m43, m44).getDeterminant(), new Matrix3f(  m21, m23, m24,
                                                                                                                    m31, m33, m34,
                                                                                                                    m41, m43, m44).getDeterminant(), new Matrix3f(  m21, m22, m24,
                                                                                                                                                                    m31, m32, m34,
                                                                                                                                                                    m41, m42, m44).getDeterminant(), new Matrix3f(  m21, m22, m23,
                                                                                                                                                                                                                    m31, m32, m33,
                                                                                                                                                                                                                    m41, m42, m43).getDeterminant(),
                                                    new Matrix3f(   m12, m13, m14,
                                                                    m32, m33, m34,
                                                                    m42, m43, m44).getDeterminant(), new Matrix3f(  m11, m13, m14,
                                                                                                                    m31, m33, m34,
                                                                                                                    m41, m43, m44).getDeterminant(), new Matrix3f(  m11, m12, m14,
                                                                                                                                                                    m31, m32, m34,
                                                                                                                                                                    m41, m42, m44).getDeterminant(), new Matrix3f(  m11, m12, m23,
                                                                                                                                                                                                                    m31, m32, m33,
                                                                                                                                                                                                                    m41, m42, m43).getDeterminant(),
                                                    new Matrix3f(   m12, m13, m14,
                                                                    m22, m23, m24,
                                                                    m42, m43, m44).getDeterminant(), new Matrix3f(  m11, m13, m14,
                                                                                                                    m21, m23, m24,
                                                                                                                    m41, m43, m44).getDeterminant(), new Matrix3f(  m11, m12, m14,
                                                                                                                                                                    m21, m22, m24,
                                                                                                                                                                    m41, m42, m44).getDeterminant(), new Matrix3f(  m11, m12, m23,
                                                                                                                                                                                                                    m21, m22, m23,
                                                                                                                                                                                                                    m41, m42, m43).getDeterminant(),
                                                    new Matrix3f(   m12, m13, m14,
                                                                    m22, m23, m24,
                                                                    m32, m33, m34).getDeterminant(), new Matrix3f(  m11, m13, m14,
                                                                                                                    m21, m23, m24,
                                                                                                                    m31, m33, m34).getDeterminant(), new Matrix3f(  m11, m12, m14,
                                                                                                                                                                    m21, m22, m24,
                                                                                                                                                                    m31, m32, m34).getDeterminant(), new Matrix3f(  m11, m12, m23,
                                                                                                                                                                                                                    m21, m22, m23,
                                                                                                                                                                                                                    m31, m32, m33).getDeterminant());
        matrixOfMinors.m12 = -m12;
        matrixOfMinors.m14 = -m14;

        matrixOfMinors.m21 = -m21;
        matrixOfMinors.m23 = -m23;

        matrixOfMinors.m32 = -m32;
        matrixOfMinors.m34 = -m34;

        matrixOfMinors.m41 = -m41;
        matrixOfMinors.m43 = -m43;

        float temp = 0;

        temp = matrixOfMinors.m12;
        matrixOfMinors.m12 = matrixOfMinors.m21;
        matrixOfMinors.m21 = temp;

        temp = matrixOfMinors.m13;
        matrixOfMinors.m13= matrixOfMinors.m31;
        matrixOfMinors.m31 = temp;

        temp = matrixOfMinors.m23;
        matrixOfMinors.m23 = matrixOfMinors.m32;
        matrixOfMinors.m32 = temp;

        temp = matrixOfMinors.m14;
        matrixOfMinors.m14 = matrixOfMinors.m41;
        matrixOfMinors.m41 = temp;

        temp = matrixOfMinors.m24;
        matrixOfMinors.m24 = matrixOfMinors.m42;
        matrixOfMinors.m42 = temp;

        temp = matrixOfMinors.m34;
        matrixOfMinors.m34 = matrixOfMinors.m43;
        matrixOfMinors.m43 = temp;

        float d = this.getDeterminant();
        return new Matrix4f(matrixOfMinors.m11/d, matrixOfMinors.m12/d, matrixOfMinors.m13/d, matrixOfMinors.m14/d,
                            matrixOfMinors.m21/d, matrixOfMinors.m22/d, matrixOfMinors.m23/d, matrixOfMinors.m24/d,
                            matrixOfMinors.m31/d, matrixOfMinors.m32/d, matrixOfMinors.m33/d, matrixOfMinors.m34/d,
                            matrixOfMinors.m41/d, matrixOfMinors.m42/d, matrixOfMinors.m43/d, matrixOfMinors.m44/d);
    }

    public float getDeterminant() {
        return  m11*(new Matrix3f(  m22, m23, m24,
                                    m32, m33, m34,
                                    m42, m43, m44).getDeterminant()) - 
                m12*(new Matrix3f(  m21, m23, m24,
                                    m31, m33, m34,
                                    m41, m43, m44).getDeterminant()) + 
                m13*(new Matrix3f(  m21, m22, m24,
                                    m31, m32, m34,
                                    m41, m42, m44).getDeterminant()) - 
                m14*(new Matrix3f(  m21, m22, m23,
                                    m31, m32, m33,
                                    m41, m42, m43).getDeterminant());
    }

    public static Matrix4f identity() {
        return new Matrix4f(1, 0, 0, 0, 
                            0, 1, 0, 0, 
                            0, 0, 1, 0, 
                            0, 0, 0, 1);
    }

    public static Matrix4f perspective(float aspectRatio, float viewAngle, float nearPlane, float farPlane) {
        return new Matrix4f((float) (1/(aspectRatio*Math.tan((viewAngle/2)))),  0,                                      0,                                          0,
                            0,                                                  (float) (1/(Math.tan((viewAngle/2)))),  0,                                          0,
                            0,                                                  0,                                      -(farPlane+nearPlane)/(farPlane-nearPlane), (2*farPlane*nearPlane)/(farPlane-nearPlane),
                            0,                                                  0,                                      -1,                                         1);
    }

    public static Matrix4f translation(Vector3f translation) {
        Matrix4f result = Matrix4f.identity();
        result.m14 = translation.x;
        result.m24 = translation.y;
        result.m34 = translation.z;
        return result;
    }

    public static Matrix4f scale(Vector3f scale) {
        Matrix4f result = Matrix4f.identity();
        result.m11 = scale.x;
        result.m22 = scale.y;
        result.m33 = scale.z;
        return result;
    }

    public Matrix4f minus(Matrix4f m) {
        return new Matrix4f(m11-m.m11, m12-m.m12, m13-m.m13, m14-m.m14,
                            m21-m.m21, m22-m.m22, m13-m.m23, m24-m.m24,
                            m31-m.m31, m32-m.m32, m13-m.m33, m34-m.m34,
                            m41-m.m41, m42-m.m42, m13-m.m43, m44-m.m44);
    }

    public Matrix4f plus(Matrix4f m) {
        return new Matrix4f(m11+m.m11, m12+m.m12, m13+m.m13, m14+m.m14,
                            m21+m.m21, m22+m.m22, m13+m.m23, m24+m.m24,
                            m31+m.m31, m32+m.m32, m13+m.m33, m34+m.m34,
                            m41+m.m41, m42+m.m42, m13+m.m43, m44+m.m44);
    }

    public Matrix4f multiply(Matrix4f m) {
        Vector4f r1 = new Vector4f(m11, m12, m13, m14);
        Vector4f r2 = new Vector4f(m21, m22, m23, m24);
        Vector4f r3 = new Vector4f(m31, m32, m33, m34);
        Vector4f r4 = new Vector4f(m41, m42, m43, m44);

        Vector4f c1 = new Vector4f(m.m11, m.m21, m.m31, m.m41);
        Vector4f c2 = new Vector4f(m.m12, m.m22, m.m32, m.m42);
        Vector4f c3 = new Vector4f(m.m13, m.m23, m.m33, m.m43);
        Vector4f c4 = new Vector4f(m.m14, m.m24, m.m34, m.m44);

        return new Matrix4f(r1.dot(c1), r1.dot(c2), r1.dot(c3), r1.dot(c4), 
                            r2.dot(c1), r2.dot(c2), r2.dot(c3), r2.dot(c4),
                            r3.dot(c1), r3.dot(c2), r3.dot(c3), r3.dot(c4),
                            r4.dot(c1), r4.dot(c2), r4.dot(c3), r4.dot(c4));
    }

    public Vector4f multiply(Vector4f v) {
        return new Vector4f(m11*v.x+m12*v.y+m13*v.z+m14*v.w, 
                            m21*v.x+m22*v.y+m23*v.z+m24*v.w, 
                            m31*v.x+m32*v.y+m33*v.z+m34*v.w, 
                            m41*v.x+m42*v.y+m43*v.z+m44*v.w);
    }

    public static Matrix4f lookAt(Vector3f eye, Vector3f target, Vector3f up) {

        Vector3f direction = eye.minus(target).normalize();    
        Vector3f right = direction.cross(up).normalize();
        Vector3f camUp = right.cross(direction);

        direction = direction.negate();

        Matrix4f viewMatrix = new Matrix4f(
                right.x, right.y, right.z, -right.dot(eye),
                camUp.x, camUp.y, camUp.z, -camUp.dot(eye),
                direction.x, direction.y, direction.z, -direction.dot(eye),
                0, 0, 0, 1);

        return viewMatrix;
    }

    public String toString() {
        return  "" +    this.m11 + " " + this.m12 + " " + this.m13 + " " + this.m14 + "\n" + 
                        this.m21 + " " + this.m22 + " " + this.m23 + " " + this.m24 + "\n" + 
                        this.m31 + " " + this.m32 + " " + this.m33 + " " + this.m34 + "\n" + 
                        this.m41 + " " + this.m42 + " " + this.m43 + " " + this.m44 + "\n";
    }

    public FloatBuffer toFloatBuffer() {
        return BufferUtils.createFloatBuffer(ArrayUtils.toFloatArray(this.toRawTypes()));
    }
}

还有我的相机class:


import com.engine.componentsystem.Component;
import com.engine.eventsystem.eventtypes.engineevents.UpdateEvent;
import com.engine.eventsystem.eventtypes.mouseevents.MouseMovedEvent;
import com.engine.eventsystem.eventtypes.mouseevents.MousePressedEvent;
import com.engine.eventsystem.eventtypes.mouseevents.MouseReleasedEvent;
import com.engine.eventsystem.eventtypes.mouseevents.MouseScrolledEvent;
import com.engine.toolbox.math.matrix.Matrix4f;
import com.engine.toolbox.math.quaternion.Quaternion;
import com.engine.toolbox.math.vectors.Vector3f;

public class CameraComponent extends Component{

    private boolean right = false, left = false;
    private float mouseX = 0, mouseY = 0, oldMouseX, oldMouseY;

    private float distanceFromTarget = -5, angleAroundTarget;

    private Vector3f position = new Vector3f(0.0f), rotation = new Vector3f(0.0f), target;

    public CameraComponent() {}

    public Vector3f getPosition() {
        return position;
    }

    public Vector3f getRotation() {
        return rotation;
    }

    public Vector3f getTarget() {
        return target;
    }

    private float calculateHorizontalDistance() {
        return (float) (this.distanceFromTarget*Math.cos(Math.toRadians(this.rotation.x)));
    }

    private float calculateVerticalDistance() {
        return (float) (this.distanceFromTarget*Math.sin(Math.toRadians(this.rotation.x)));
    }

    public Matrix4f getViewMatrix() {
        Matrix4f result = Matrix4f.identity();
        Matrix4f rotationM = Quaternion.fromEuler(rotation).toMatrix();
        Matrix4f translation = Matrix4f.translation(position.negate());
        Matrix4f origin = Matrix4f.translation(target);
        Matrix4f Iorigin = Matrix4f.translation(target.negate());

        System.out.println("target: " + target);
        System.out.println("position: " + position);
        System.out.println("rotation: " + rotation);

        result = result.multiply(translation);
        result = result.multiply(origin);
        result = result.multiply(rotationM);
        result = result.multiply(Iorigin);
        return result;
    }

    @Override
    public void onAttach() {
        this.target = ((PositionComponent) parent.getComponent(PositionComponent.class)).getPosition();
    }

    @Override
    public boolean onMouseScrolled(MouseScrolledEvent event) {
        this.distanceFromTarget += event.getyOffset()*0.7f;
        return false;
    }

    @Override
    public boolean onMousePressed(MousePressedEvent event) {
        if(event.getButton() == 0 && event.getMods() == 1) {
            left = true;
        }
        if(event.getButton() == 1 && event.getMods() == 1) {
            right = true;
        }
        return false;
    }

    @Override
    public boolean onMouseReleased(MouseReleasedEvent event) {
        if(event.getButton() == 0) {
            left = false;
        }
        if(event.getButton() == 1) {
            right = false;
        }
        return false;
    }

    @Override
    public boolean onMouseMoved(MouseMovedEvent event) {
        oldMouseX = mouseX;
        oldMouseY = mouseY;
        mouseX = event.getX();
        mouseY = event.getY();
        if(left) {
            this.angleAroundTarget -= (mouseX-oldMouseX)*0.01f;
        }
        if(right) {
            this.rotation.x -= (mouseY-oldMouseY)*0.01f;
        }
        return false;
    }

    @Override
    public void onUpdate(UpdateEvent event) {
        this.target = ((PositionComponent) parent.getComponent(PositionComponent.class)).getPosition();
        float horizantalDistance = calculateHorizontalDistance();
        float verticalDistance = calculateVerticalDistance();
        float theta = ((RotationComponent) parent.getComponent(RotationComponent.class)).getRotation().y + angleAroundTarget;

        float xOffset = (float) (horizantalDistance*Math.sin(Math.toRadians(theta)));
        float zOffset = (float) (horizantalDistance*Math.cos(Math.toRadians(theta)));

        this.position.x = target.x - xOffset;
        this.position.y = target.y + verticalDistance;
        this.position.z = target.z - zOffset;

        this.rotation.y = (float) (Math.toRadians(180.0f) - theta);
    }

}

所以我设法通过

解决了我的问题
  1. 更改为先旋转再移动的矩阵乘法顺序(我还向 Matrix4f 添加了新函数 class 以使其更容易)
result = result.rotate((float) -this.rotation.x, new Vector3f(1f, 0f, 0f));
result = result.rotate((float) this.rotation.y, new Vector3f(0f, 1f, 0f));
result = result.translate(position.negate());
  1. 修复四元数 class 中的错误 a 破坏了旋转(我忘了将度数转换为弧度)
roll = (float) Math.toRadians(roll);
pitch = (float) Math.toRadians(pitch);
yaw = (float) Math.toRadians(yaw);

改进的相机 class(我添加了浮动平滑以消除运动中的所有抖动):

package com.assec.engine.componentsystem.components;

import com.assec.engine.componentsystem.Component;
import com.assec.engine.eventsystem.engineevents.UpdateEvent;
import com.assec.engine.eventsystem.mouseevents.MouseMovedEvent;
import com.assec.engine.eventsystem.mouseevents.MousePressedEvent;
import com.assec.engine.eventsystem.mouseevents.MouseReleasedEvent;
import com.assec.engine.eventsystem.mouseevents.MouseScrolledEvent;
import com.assec.engine.toolbox.math.SmoothFloat;
import com.assec.engine.toolbox.math.matrix.Matrix4f;
import com.assec.engine.toolbox.math.vectors.Vector3f;

public class CameraComponent extends Component{

    private boolean right = false, left = false;
    private float mouseX = 0.0f, mouseY = 0.0f, oldMouseX, oldMouseY;

    private SmoothFloat distanceFromTarget = new SmoothFloat(-10.0f, 5.0f);
    private SmoothFloat angleAroundTarget = new SmoothFloat(0.0f, 10.0f);

    private SmoothFloat pitch = new SmoothFloat(0.0f, 10.0f);

    private Vector3f position = new Vector3f(0.0f), rotation = new Vector3f(0.0f), target;

    public CameraComponent() {}

    public Vector3f getPosition() {
        return position;
    }

    public Vector3f getRotation() {
        return rotation;
    }

    public Vector3f getTarget() {
        return target;
    }

    private float calculateHorizontalDistance() {
        return (float) (this.distanceFromTarget.getActual()*Math.cos(Math.toRadians(this.rotation.x)));
    }

    private float calculateVerticalDistance() {
        return (float) (this.distanceFromTarget.getActual()*Math.sin(Math.toRadians(this.rotation.x)));
    }

    public Matrix4f getViewMatrix() {
        Matrix4f result = Matrix4f.identity();

        this.target = new Vector3f(0.0f);

        this.rotation.x = pitch.getActual();
        this.rotation.x%= 360;

        float horizontalDistance = calculateHorizontalDistance();
        float verticalDistance = calculateVerticalDistance();
        float theta = angleAroundTarget.getActual();

        float xOffset = (float) (horizontalDistance*Math.sin(Math.toRadians(theta)));
        float zOffset = (float) (horizontalDistance*Math.cos(Math.toRadians(theta)));

        this.position.x = target.x - xOffset;
        this.position.y = target.y + verticalDistance;
        this.position.z = target.z - zOffset;

        this.rotation.y = 360.0f - theta;
        this.rotation.y%= 360;

        result = result.rotate((float) -this.rotation.x, new Vector3f(1f, 0f, 0f));
        result = result.rotate((float) this.rotation.y, new Vector3f(0f, 1f, 0f));
        result = result.translate(position.negate());

        return result;
    }

    @Override
    public void onAttach() {
        this.target = ((PositionComponent) parent.getComponent(PositionComponent.class)).getPosition();
        this.target = new Vector3f(0.0f);
    }

    @Override
    public boolean onMouseScrolled(MouseScrolledEvent event) {
        this.distanceFromTarget.increaseTarget(-event.getyOffset()*0.7f);
        return false;
    }

    @Override
    public boolean onMousePressed(MousePressedEvent event) {
        if(event.getButton() == 0 && event.getMods() == 1) {
            left = true;
        }
        if(event.getButton() == 1 && event.getMods() == 1) {
            right = true;
        }
        return false;
    }

    @Override
    public boolean onMouseReleased(MouseReleasedEvent event) {
        if(event.getButton() == 0) {
            left = false;
        }
        if(event.getButton() == 1) {
            right = false;
        }
        return false;
    }

    @Override
    public boolean onMouseMoved(MouseMovedEvent event) {
        oldMouseX = mouseX;
        oldMouseY = mouseY;
        mouseX = (float) event.getX();
        mouseY = (float) event.getY();
        if(left) {
            this.angleAroundTarget.increaseTarget(-(mouseX-oldMouseX)*0.1f);
        }
        if(right) {
            this.pitch.increaseTarget(-(mouseY-oldMouseY)*0.1f);
        }
        return false;
    }

    @Override
    public void onUpdate(UpdateEvent event) {
        this.angleAroundTarget.update(0.01f);
        this.distanceFromTarget.update(0.01f);
        this.pitch.update(0.01f);
    }

}

改进后的Matrix4fclass:

package com.assec.engine.toolbox.math.matrix;

import java.nio.FloatBuffer;
import java.util.Arrays;
import java.util.Iterator;

import com.assec.engine.toolbox.ArrayUtils;
import com.assec.engine.toolbox.BufferUtils;
import com.assec.engine.toolbox.math.quaternion.Quaternion;
import com.assec.engine.toolbox.math.vectors.Vector3f;
import com.assec.engine.toolbox.math.vectors.Vector4f;

public class Matrix4f extends Matrix{

    public float m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44;

    public Matrix4f(float m11, float m12, float m13, float m14,
                    float m21, float m22, float m23, float m24,
                    float m31, float m32, float m33, float m34,
                    float m41, float m42, float m43, float m44) {
        super(new Float[] { m11, m12, m13, m14, 
                            m21, m22, m23, m24, 
                            m31, m32, m33, m34, 
                            m41, m42, m43, m44});
        this.m11 = m11;
        this.m12 = m12;
        this.m13 = m13;
        this.m14 = m14;
        this.m21 = m21;
        this.m22 = m22;
        this.m23 = m23;
        this.m24 = m24;
        this.m31 = m31;
        this.m32 = m32;
        this.m33 = m33;
        this.m34 = m34;
        this.m41 = m41;
        this.m42 = m42;
        this.m43 = m43;
        this.m44 = m44;

    }

    @Override
    public Iterator<Float> iterator() {
        return Arrays.asList(this.toRawTypes()).iterator();
    }

    public Matrix4f invert() {
        Matrix4f matrixOfMinors =   new Matrix4f(   new Matrix3f(   m22, m23, m24,
                                                                    m32, m33, m34,
                                                                    m42, m43, m44).getDeterminant(), new Matrix3f(  m21, m23, m24,
                                                                                                                    m31, m33, m34,
                                                                                                                    m41, m43, m44).getDeterminant(), new Matrix3f(  m21, m22, m24,
                                                                                                                                                                    m31, m32, m34,
                                                                                                                                                                    m41, m42, m44).getDeterminant(), new Matrix3f(  m21, m22, m23,
                                                                                                                                                                                                                    m31, m32, m33,
                                                                                                                                                                                                                    m41, m42, m43).getDeterminant(),
                                                    new Matrix3f(   m12, m13, m14,
                                                                    m32, m33, m34,
                                                                    m42, m43, m44).getDeterminant(), new Matrix3f(  m11, m13, m14,
                                                                                                                    m31, m33, m34,
                                                                                                                    m41, m43, m44).getDeterminant(), new Matrix3f(  m11, m12, m14,
                                                                                                                                                                    m31, m32, m34,
                                                                                                                                                                    m41, m42, m44).getDeterminant(), new Matrix3f(  m11, m12, m23,
                                                                                                                                                                                                                    m31, m32, m33,
                                                                                                                                                                                                                    m41, m42, m43).getDeterminant(),
                                                    new Matrix3f(   m12, m13, m14,
                                                                    m22, m23, m24,
                                                                    m42, m43, m44).getDeterminant(), new Matrix3f(  m11, m13, m14,
                                                                                                                    m21, m23, m24,
                                                                                                                    m41, m43, m44).getDeterminant(), new Matrix3f(  m11, m12, m14,
                                                                                                                                                                    m21, m22, m24,
                                                                                                                                                                    m41, m42, m44).getDeterminant(), new Matrix3f(  m11, m12, m23,
                                                                                                                                                                                                                    m21, m22, m23,
                                                                                                                                                                                                                    m41, m42, m43).getDeterminant(),
                                                    new Matrix3f(   m12, m13, m14,
                                                                    m22, m23, m24,
                                                                    m32, m33, m34).getDeterminant(), new Matrix3f(  m11, m13, m14,
                                                                                                                    m21, m23, m24,
                                                                                                                    m31, m33, m34).getDeterminant(), new Matrix3f(  m11, m12, m14,
                                                                                                                                                                    m21, m22, m24,
                                                                                                                                                                    m31, m32, m34).getDeterminant(), new Matrix3f(  m11, m12, m23,
                                                                                                                                                                                                                    m21, m22, m23,
                                                                                                                                                                                                                    m31, m32, m33).getDeterminant());
        matrixOfMinors.m12 = -m12;
        matrixOfMinors.m14 = -m14;

        matrixOfMinors.m21 = -m21;
        matrixOfMinors.m23 = -m23;

        matrixOfMinors.m32 = -m32;
        matrixOfMinors.m34 = -m34;

        matrixOfMinors.m41 = -m41;
        matrixOfMinors.m43 = -m43;

        float temp = 0;

        temp = matrixOfMinors.m12;
        matrixOfMinors.m12 = matrixOfMinors.m21;
        matrixOfMinors.m21 = temp;

        temp = matrixOfMinors.m13;
        matrixOfMinors.m13= matrixOfMinors.m31;
        matrixOfMinors.m31 = temp;

        temp = matrixOfMinors.m23;
        matrixOfMinors.m23 = matrixOfMinors.m32;
        matrixOfMinors.m32 = temp;

        temp = matrixOfMinors.m14;
        matrixOfMinors.m14 = matrixOfMinors.m41;
        matrixOfMinors.m41 = temp;

        temp = matrixOfMinors.m24;
        matrixOfMinors.m24 = matrixOfMinors.m42;
        matrixOfMinors.m42 = temp;

        temp = matrixOfMinors.m34;
        matrixOfMinors.m34 = matrixOfMinors.m43;
        matrixOfMinors.m43 = temp;

        float d = this.getDeterminant();
        return new Matrix4f(matrixOfMinors.m11/d, matrixOfMinors.m12/d, matrixOfMinors.m13/d, matrixOfMinors.m14/d,
                            matrixOfMinors.m21/d, matrixOfMinors.m22/d, matrixOfMinors.m23/d, matrixOfMinors.m24/d,
                            matrixOfMinors.m31/d, matrixOfMinors.m32/d, matrixOfMinors.m33/d, matrixOfMinors.m34/d,
                            matrixOfMinors.m41/d, matrixOfMinors.m42/d, matrixOfMinors.m43/d, matrixOfMinors.m44/d);
    }

    public float getDeterminant() {
        return  m11*(new Matrix3f(  m22, m23, m24,
                                    m32, m33, m34,
                                    m42, m43, m44).getDeterminant()) - 
                m12*(new Matrix3f(  m21, m23, m24,
                                    m31, m33, m34,
                                    m41, m43, m44).getDeterminant()) + 
                m13*(new Matrix3f(  m21, m22, m24,
                                    m31, m32, m34,
                                    m41, m42, m44).getDeterminant()) - 
                m14*(new Matrix3f(  m21, m22, m23,
                                    m31, m32, m33,
                                    m41, m42, m43).getDeterminant());
    }

    public static Matrix4f identity() {
        return new Matrix4f(1, 0, 0, 0, 
                            0, 1, 0, 0, 
                            0, 0, 1, 0, 
                            0, 0, 0, 1);
    }

    public static Matrix4f perspective(float aspectRatio, float viewAngle, float nearPlane, float farPlane) {
        return new Matrix4f((float) (1/(aspectRatio*Math.tan((viewAngle/2)))),  0,                                      0,                                          0,
                            0,                                                  (float) (1/(Math.tan((viewAngle/2)))),  0,                                          0,
                            0,                                                  0,                                      -((farPlane+nearPlane)/(farPlane-nearPlane)), -(2*farPlane*nearPlane)/(farPlane-nearPlane),
                            0,                                                  0,                                      -1,                                         1);
    }

    public static Matrix4f translation(Vector3f translation) {
        Matrix4f result = Matrix4f.identity();
        result.m14 = translation.x;
        result.m24 = translation.y;
        result.m34 = translation.z;
        return result;
    }

    public static Matrix4f scale(Vector3f scale) {
        Matrix4f result = Matrix4f.identity();
        result.m11 = scale.x;
        result.m22 = scale.y;
        result.m33 = scale.z;
        return result;
    }

    public Matrix4f minus(Matrix4f m) {
        return new Matrix4f(m11-m.m11, m12-m.m12, m13-m.m13, m14-m.m14,
                            m21-m.m21, m22-m.m22, m13-m.m23, m24-m.m24,
                            m31-m.m31, m32-m.m32, m13-m.m33, m34-m.m34,
                            m41-m.m41, m42-m.m42, m13-m.m43, m44-m.m44);
    }

    public Matrix4f plus(Matrix4f m) {
        return new Matrix4f(m11+m.m11, m12+m.m12, m13+m.m13, m14+m.m14,
                            m21+m.m21, m22+m.m22, m13+m.m23, m24+m.m24,
                            m31+m.m31, m32+m.m32, m13+m.m33, m34+m.m34,
                            m41+m.m41, m42+m.m42, m13+m.m43, m44+m.m44);
    }

    public Matrix4f multiply(Matrix4f m) {
        Vector4f r1 = new Vector4f(m11, m12, m13, m14);
        Vector4f r2 = new Vector4f(m21, m22, m23, m24);
        Vector4f r3 = new Vector4f(m31, m32, m33, m34);
        Vector4f r4 = new Vector4f(m41, m42, m43, m44);

        Vector4f c1 = new Vector4f(m.m11, m.m21, m.m31, m.m41);
        Vector4f c2 = new Vector4f(m.m12, m.m22, m.m32, m.m42);
        Vector4f c3 = new Vector4f(m.m13, m.m23, m.m33, m.m43);
        Vector4f c4 = new Vector4f(m.m14, m.m24, m.m34, m.m44);

        return new Matrix4f(r1.dot(c1), r1.dot(c2), r1.dot(c3), r1.dot(c4), 
                            r2.dot(c1), r2.dot(c2), r2.dot(c3), r2.dot(c4),
                            r3.dot(c1), r3.dot(c2), r3.dot(c3), r3.dot(c4),
                            r4.dot(c1), r4.dot(c2), r4.dot(c3), r4.dot(c4));
    }

    public Vector4f multiply(Vector4f v) {
        return new Vector4f(m11*v.x+m12*v.y+m13*v.z+m14*v.w, 
                            m21*v.x+m22*v.y+m23*v.z+m24*v.w, 
                            m31*v.x+m32*v.y+m33*v.z+m34*v.w, 
                            m41*v.x+m42*v.y+m43*v.z+m44*v.w);
    }

    public Matrix4f rotate(float angle, Vector3f axis) {
        Matrix4f rotation = Quaternion.fromEuler(axis.normalized().scaled(angle)).toMatrix();
        return this.multiply(rotation);
    }

    public Matrix4f translate(Vector3f translation) {
        Matrix4f translate = Matrix4f.translation(translation);
        return this.multiply(translate);
    }

    public static Matrix4f lookAt(Vector3f eye, Vector3f target, Vector3f up) {

        Vector3f direction = eye.minus(target).normalized();    
        Vector3f right = direction.cross(up).normalized();
        Vector3f camUp = right.cross(direction);

        direction = direction.negate();

        Matrix4f viewMatrix = new Matrix4f(
                right.x, right.y, right.z, -right.dot(eye),
                camUp.x, camUp.y, camUp.z, -camUp.dot(eye),
                direction.x, direction.y, direction.z, -direction.dot(eye),
                0.0f, 0.0f, 0.0f, 1.0f);
        return viewMatrix;
    }

    public String toString() {
        return  "" +    this.m11 + " " + this.m12 + " " + this.m13 + " " + this.m14 + "\n" + 
                        this.m21 + " " + this.m22 + " " + this.m23 + " " + this.m24 + "\n" + 
                        this.m31 + " " + this.m32 + " " + this.m33 + " " + this.m34 + "\n" + 
                        this.m41 + " " + this.m42 + " " + this.m43 + " " + this.m44 + "\n";
    }

    public FloatBuffer toFloatBuffer() {
        return BufferUtils.createFloatBuffer(ArrayUtils.toFloatArray(this.toRawTypes()));
    }
}