com.samrj.devil.graphics.Camera3D.java Source code

Java tutorial

Introduction

Here is the source code for com.samrj.devil.graphics.Camera3D.java

Source

/*
 * Copyright (c) 2016 Sam Johnson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.samrj.devil.graphics;

import com.samrj.devil.math.Mat3;
import com.samrj.devil.math.Mat4;
import com.samrj.devil.math.Quat;
import com.samrj.devil.math.Vec2;
import com.samrj.devil.math.Vec2i;
import com.samrj.devil.math.Vec3;
import com.samrj.devil.math.Vec4;
import org.lwjgl.opengl.GL11;

/**
 * 3D camera class.
 * 
 * @author Samuel Johnson (SmashMaster)
 */
public class Camera3D {
    /**
     * Returns an array of eight vectors, each one a vertex of this camera's
     * frustum. Returned in local space.
     * 
     * @param near The near plane of the frustum.
     * @param far The far plane of the frustum.
     * @return The frustum of this camera.
     */
    public static Vec3[] getFrustum(float near, float far, float hSlope, float vSlope) {
        float wn = near * hSlope, wf = far * hSlope;
        float hn = near * vSlope, hf = far * vSlope;

        Vec3[] array = { new Vec3(-wn, -hn, -near), new Vec3(-wf, -hf, -far), new Vec3(-wn, hn, -near),
                new Vec3(-wf, hf, -far), new Vec3(wn, -hn, -near), new Vec3(wf, -hf, -far), new Vec3(wn, hn, -near),
                new Vec3(wf, hf, -far), };

        return array;
    }

    public final Vec3 pos = new Vec3();
    public final Quat dir = Quat.identity();
    public final float zNear, zFar;
    public final float fov;
    public final float hSlope, vSlope;
    public final Mat4 projMat, viewMat;
    public final Vec3 right, up, forward;

    public Camera3D(float zNear, float zFar, float fov, float aspectRatio) {
        this.zNear = zNear;
        this.zFar = zFar;
        this.fov = fov;
        float tanFov = (float) Math.tan(fov * 0.5f);

        if (aspectRatio <= 1.0f) //Width is greater or equal to height.
        {
            hSlope = tanFov;
            vSlope = tanFov * aspectRatio;
        } else //Widgth is smaller than height.
        {
            hSlope = tanFov / aspectRatio;
            vSlope = tanFov;
        }

        projMat = Mat4.frustum(hSlope * zNear, vSlope * zNear, zNear, zFar);
        viewMat = Mat4.identity();
        right = new Vec3(1.0f, 0.0f, 0.0f);
        up = new Vec3(0.0f, 1.0f, 0.0f);
        forward = new Vec3(0.0f, 0.0f, -1.0f);
    }

    public Camera3D(float zNear, float zFar, float fov, Vec2i resolution) {
        this(zNear, zFar, fov, resolution.y / (float) resolution.x);
    }

    /**
     * Returns whether this camera frustum's width is smaller than its height.
     */
    public boolean isSkinny() {
        return hSlope < vSlope;
    }

    public void pointAt(Vec3 p) {
        dir.setRotation(new Vec3(0.0f, 0.0f, -1.0f), Vec3.sub(p, pos));
    }

    /**
     * Projects the given vector to this camera's clip space
     */
    public void project(Vec3 v, Vec3 result) {
        Vec4 h = new Vec4(v, 1.0f);
        h.mult(viewMat);
        h.mult(projMat);
        result.set(h.x, h.y, h.z).div(h.w);
    }

    public Vec3 project(Vec3 v) {
        Vec3 out = new Vec3();
        project(v, out);
        return out;
    }

    /**
     * Updates the matrices and axis directions for this camera.
     */
    public void update() {
        viewMat.setRotation(Quat.invert(dir));
        viewMat.translate(Vec3.negate(pos));

        Mat3 rot = Mat3.rotation(dir);
        right.setAsColumn(rot, 0);
        up.setAsColumn(rot, 1);
        forward.setAsColumn(rot, 2).negate();
    }

    /**
     * Loads this camera's matrices into OpenGL.
     */
    public void glLoadMatrices() {
        GraphicsUtil.glLoadMatrix(projMat, GL11.GL_PROJECTION);
        GraphicsUtil.glLoadMatrix(viewMat, GL11.GL_MODELVIEW);
    }

    /**
     * Returns an array of eight vectors, each one a vertex of this camera's
     * frustum. Returned in local space, with actual.
     * 
     * @return The frustum of this camera.
     */
    public Vec3[] getFrustum() {
        return getFrustum(zNear, zFar, hSlope, vSlope);
    }

    /**
     * There is a unique sphere whose surface touches each vertex of this
     * camera's frustum. This method returns the distance to and radius of that
     * sphere, in the first and second components of a 2D vector.
     * 
     * @param near The near plane to use.
     * @param far The far plane to use.
     * @return The circumsphere of this camera.
     */
    public Vec2 getFrustumCircumsphere(float near, float far) {
        float slopeSq = hSlope * hSlope + vSlope * vSlope;
        float z = (near * (slopeSq - 1.0f) + far * (slopeSq + 1.0f)) * 0.5f;
        float r = (float) Math.sqrt(near * near * slopeSq + z * z);

        return new Vec2(-(near + z), r);
    }

    /**
     * There is a unique sphere whose surface touches each vertex of this
     * camera's frustum. This method returns the distance to and radius of that
     * sphere, in the first and second components of a 2D vector.
     * 
     * @return The circumsphere of this camera.
     */
    public Vec2 getFrustumCircumsphere() {
        return getFrustumCircumsphere(zNear, zFar);
    }
}