Source: css3d.camera.js

/**
 * CSS 3D engine
 *
 * @category    css3d
 * @package     css3d.camera
 * @author      Jan Fischer, bitWorking <info@bitworking.de>
 * @copyright   2014 Jan Fischer
 * @license     http://www.opensource.org/licenses/mit-license.html  MIT License
 */

/**
 * 
 * @name css3d.camera
 * @class
 * @param {Number} x
 * @param {Number} y
 * @param {Number} z
 * @param {Number} perspective
 * @returns {css3d.camera}
 */
css3d.camera = (function()
{
    /**
     * 
     * @param {Number} x
     * @param {Number} y
     * @param {Number} z
     * @param {Number} perspective
     * @returns {css3d.camera}
     */
    var camera = function(x, y, z, perspective)
    {
        /**
         * Set the perspective value
         * @type {Integer}
         * @memberof! css3d.camera
         * @instance
         */ 
        this.perspective = perspective || 800;

        x = (null != x) ? x : 0;
        y = (null != y) ? y : 0;
        z = (null != z) ? z : this.perspective;

        this._view = css3d.matrix4.identity();
        this._rotation = css3d.matrix4.identity();
        this._translation = new css3d.vector3(x, y, z);
        this._isDirty = true;
    };

    camera.prototype._calcFov = function(perspective, height)
    {
	return (360 * Math.atan(height / (2 * perspective))) / Math.PI;
    }

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {css3d.vector3} axis
     * @param {Number} angle
     * @returns {css3d.camera}
     */
    camera.prototype.setRotation = function(axis, angle)
    {
        this._rotation = css3d.matrix4.rotationAxis(axis, angle);
        this._isDirty = true;
        return this;
    };
    
    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {Number} x
     * @param {Number} y
     * @param {Number} z
     * @returns {css3d.camera}
     */
    camera.prototype.setRotationXYZ = function(x, y, z)
    {
        x = x || 0;
        y = y || 0;
        z = z || 0;
        var rotation = null;
        rotation = css3d.matrix4.rotationZ(z);
        rotation = css3d.matrix4.multiply(rotation, css3d.matrix4.rotationY(y));
        rotation = css3d.matrix4.multiply(rotation, css3d.matrix4.rotationX(x));        
        this._rotation = rotation;
        this._isDirty = true;
        return this;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {css3d.matrix4} m
     * @returns {css3d.camera}
     */
    camera.prototype.setRotationMatrix = function(m)
    {
        this._rotation = m;
        this._isDirty = true;
        return this;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @returns {css3d.matrix4}
     */
    camera.prototype.getRotation = function()
    {
        return this._rotation;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {Number} x
     * @param {Number} y
     * @param {Number} z
     * @returns {css3d.camera}
     */
    camera.prototype.setTranslation = function(x, y, z)
    {
        this._translation.x = x;
        this._translation.y = y;
        this._translation.z = z;
        this._isDirty = true;
        return this;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @returns {css3d.vector3}
     */
    camera.prototype.getTranslation = function()
    {
        return this._translation;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @returns {css3d.vector3}
     */
    camera.prototype.backVector = function()
    {        
        return css3d.matrix4.back(this._rotation).normalize();
    };
    
    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @returns {css3d.vector3}
     */
    camera.prototype.forwardVector = function()
    {
        return css3d.matrix4.forward(this._rotation).normalize();
    };

    /**
     * Move camera forward
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {Number} steps
     * @returns {css3d.camera}
     */
    camera.prototype.forward = function(steps)
    {
        var forwardVector = this.forwardVector();
        this.setTranslation(
            this._translation.x + (forwardVector.x * steps),
            this._translation.y + (forwardVector.y * steps),
            this._translation.z + (forwardVector.z * steps)
        );
        return this;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @returns {css3d.vector3}
     */
    camera.prototype.rightVector = function()
    {        
        return css3d.matrix4.right(this._rotation).normalize();
    };

    /**
     * Move camera left
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {Number} steps
     * @returns {css3d.camera}
     */
    camera.prototype.left = function(steps)
    {
        var rightVector = this.rightVector();
        this.setTranslation(
            this._translation.x - (rightVector.x * steps),
            this._translation.y - (rightVector.y * steps),
            this._translation.z - (rightVector.z * steps)
        );
        return this;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @returns {css3d.vector3}
     */
    camera.prototype.upVector = function()
    {        
        return css3d.matrix4.up(this._rotation).normalize();
    };

    /**
     * Move camera up
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {Number} steps
     * @returns {css3d.camera}
     */
    camera.prototype.up = function(steps)
    {
        var upVector = this.upVector();
        this.setTranslation(
            this._translation.x + (upVector.x * steps),
            this._translation.y + (upVector.y * steps),
            this._translation.z + (upVector.z * steps)
        );
        return this;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @returns {css3d.matrix4}
     */
    camera.prototype.getViewMatrix = function()
    {
        return this._view;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {css3d.matrix4} m
     * @returns {css3d.camera}
     */
    camera.prototype.setViewMatrix = function(m)
    {
        this._rotation = [
            m[0], m[1], m[2],  0,
            m[4], m[5], m[6],  0,
            m[8], m[9], m[10], 0,
            0,    0,    0,     1
        ];
        this.setTranslation(m[3], m[7], m[11]);
        this._isDirty = true;
        return this;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {css3d.element} element
     * @param {Number} distance
     * @returns {css3d.matrix4}
     */
    camera.prototype.getFaceElementMatrix = function(element, distance)
    {
        distance = (null == distance) ? this.perspective : distance;
        
        var pivot = element.getPivotTransformed();
        var matrix = element.getTotalRotation();

        var backVector = element.backVector();

        var x = pivot.x + (backVector.x * distance);
        var y = pivot.y + (backVector.y * distance);
        var z = pivot.z + (backVector.z * distance);

        matrix[3] = x;
        matrix[7] = y;
        matrix[11] = z;

        return matrix;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {css3d.element} element
     * @param {Number} distance
     * @returns {Function}
     */
    camera.prototype.getFaceElementLerpFunction = function(element, distance)
    {
        var viewMatrix = this.getFaceElementMatrix(element, distance);

        var fromTranslation = this.getTranslation().clone()
        var toTranslation = new css3d.vector3(viewMatrix[3], viewMatrix[7], viewMatrix[11]);
        viewMatrix[3] = 0;viewMatrix[7] = 0;viewMatrix[11] = 0;

        var fromRotation = this.getRotation();
        var toRotation = css3d.quaternion.prototype.fromMatrix4(viewMatrix);

        var self = this;

        return function(t)
        {
            self.lerp(fromTranslation, toTranslation, fromRotation, toRotation, t);
        };
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {css3d.vector3} from
     * @param {css3d.vector3} to
     * @param {Number} t
     * @returns {css3d.camera}
     */
    camera.prototype.lerpTranslation = function(from, to, t)
    {
        this.setTranslation(
            css3d.math.lerp(from.x, to.x, t),
            css3d.math.lerp(from.y, to.y, t),
            css3d.math.lerp(from.z, to.z, t)
        );
        return this;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {css3d.matrix4} fromMatrix
     * @param {css3d.quaternion} toQuaternion
     * @param {Number} t
     * @returns {css3d.camera}
     */
    camera.prototype.lerpRotation = function(fromMatrix, toQuaternion, t)
    {
        var fromQuaternion = css3d.quaternion.prototype.fromMatrix4(fromMatrix);
        var out = css3d.quaternion.prototype.slerp(fromQuaternion, toQuaternion, t);
        this.setRotationMatrix(out.toMatrix4());
        return this;
    };

    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {css3d.vector3} fromTranslation
     * @param {css3d.vector3} toTranslation
     * @param {css3d.matrix4} fromRotationMatrix
     * @param {css3d.quaternion} toQuaternion
     * @param {Number} t
     * @returns {css3d.camera}
     */
    camera.prototype.lerp = function(fromTranslation, toTranslation, fromRotationMatrix, toQuaternion, t)
    {
        if (t == null) {
            return this;
        }
        if (fromTranslation && toTranslation) {
            this.lerpTranslation(fromTranslation, toTranslation, t);
        }
        if (fromRotationMatrix && toQuaternion) {
            this.lerpRotation(fromRotationMatrix, toQuaternion, t);
        }
        return this;
    };
    
    /**
     * 
     * @memberof! css3d.camera
     * @instance
     * @param {css3d.element} element
     * @returns {css3d.camera}
     */
    camera.prototype.lookAtElement = function(element)
    {
        var lookAtMatrix = css3d.matrix4.lookAt(this.getTranslation(), element.getTranslation(), new css3d.vector3(0, -1, 0));
        this.setRotationMatrix(lookAtMatrix);
        return this;
    };

    /**
     * Build the view matrix
     * 
     * @memberof! css3d.camera
     * @instance
     * @returns {css3d.camera}
     */
    camera.prototype.update = function()
    {
        if (this._isDirty) {

            var m = this._rotation;
            
            var cameraBackVector = this.backVector();
            m[3] = this._translation.x - (cameraBackVector.x * this.perspective);
            m[7] = this._translation.y - (cameraBackVector.y * this.perspective);
            m[11] = this._translation.z - (cameraBackVector.z * this.perspective);
            
            this._view = css3d.matrix4.fastInverse(m);
           
            this._isDirty = false;
        }
        return this;
    };

    return camera;

}());