Source: objects/camera/Camera.js

import { OBJECT_TYPE, TEXEL_ENCODING_TYPE } from '../../const.js';
import { Object3D } from '../Object3D.js';
import { Matrix4 } from '../../math/Matrix4.js';
import { Frustum } from '../../math/Frustum.js';
import { Vector4 } from '../../math/Vector4.js';
import { Vector3 } from '../../math/Vector3.js';

var _mat4_1 = new Matrix4();

/**
 * The camera used for rendering a 3D scene.
 * @memberof zen3d
 * @constructor
 * @extends zen3d.Object3D
 */
function Camera() {
	Object3D.call(this);

	this.type = OBJECT_TYPE.CAMERA;

	/**
     * This is the inverse of worldMatrix.
     * @type {zen3d.Matrix4}
     */
	this.viewMatrix = new Matrix4();

	/**
     * This is the matrix which contains the projection.
     * @type {zen3d.Matrix4}
     */
	this.projectionMatrix = new Matrix4();

	/**
     * This is the matrix which contains the projection.
     * @type {zen3d.Matrix4}
     */
	this.projectionMatrixInverse = new Matrix4();

	/**
     * The frustum of the camera.
     * @type {zen3d.Frustum}
     */
	this.frustum = new Frustum();

	// gamma space or linear space
	/**
     * The factor of gamma.
     * @type {number}
     * @default 2.0
     */
	this.gammaFactor = 2.0;

	/**
     * Output pixel encoding.
     * @type {zen3d.TEXEL_ENCODING_TYPE}
     * @default zen3d.TEXEL_ENCODING_TYPE.LINEAR
     */
	this.outputEncoding = TEXEL_ENCODING_TYPE.LINEAR;

	/**
     * Where on the screen is the camera rendered in normalized coordinates.
     * @type {zen3d.Vector4}
     * @default zen3d.Vector4(0, 0, 1, 1)
     */
	this.rect = new Vector4(0, 0, 1, 1);

	/**
     * When this is set, it checks every frame if objects are in the frustum of the camera before rendering objects.
     * Otherwise objects gets rendered every frame even if it isn't visible.
     * @type {boolean}
     * @default true
     */
	this.frustumCulled = true;
}

Camera.prototype = Object.assign(Object.create(Object3D.prototype), /** @lends zen3d.Camera.prototype */{

	constructor: Camera,

	/**
     * Set view by look at, this func will set quaternion of this camera.
     * @method
     * @param {zen3d.Vector3} target - The target that the camera look at.
     * @param {zen3d.Vector3} up - The up direction of the camera.
     */
	lookAt: function(target, up) {
		_mat4_1.lookAtRH(this.position, target, up);
		this.quaternion.setFromRotationMatrix(_mat4_1);
	},

	/**
     * Set orthographic projection matrix.
     * @param {number} left — Camera frustum left plane.
     * @param {number} right — Camera frustum right plane.
     * @param {number} bottom — Camera frustum bottom plane.
     * @param {number} top — Camera frustum top plane.
     * @param {number} near — Camera frustum near plane.
     * @param {number} far — Camera frustum far plane.
     */
	setOrtho: function(left, right, bottom, top, near, far) {
		this.projectionMatrix.set(
			2 / (right - left), 0, 0, -(right + left) / (right - left),
			0, 2 / (top - bottom), 0, -(top + bottom) / (top - bottom),
			0, 0, -2 / (far - near), -(far + near) / (far - near),
			0, 0, 0, 1
		);
		this.projectionMatrixInverse.getInverse(this.projectionMatrix);
	},

	/**
     * Set perspective projection matrix.
     * @param {number} fov — Camera frustum vertical field of view.
     * @param {number} aspect — Camera frustum aspect ratio.
     * @param {number} near — Camera frustum near plane.
     * @param {number} far — Camera frustum far plane.
     */
	setPerspective: function(fov, aspect, near, far) {
		this.projectionMatrix.set(
			1 / (aspect * Math.tan(fov / 2)), 0, 0, 0,
			0, 1 / (Math.tan(fov / 2)), 0, 0,
			0, 0, -(far + near) / (far - near), -2 * far * near / (far - near),
			0, 0, -1, 0
		);
		this.projectionMatrixInverse.getInverse(this.projectionMatrix);
	},

	getWorldDirection: function(optionalTarget) {
		optionalTarget = optionalTarget || new Vector3();
		var e = this.worldMatrix.elements;
		return optionalTarget.set(-e[8], -e[9], -e[10]).normalize();
	},

	updateMatrix: function(force) {
		Object3D.prototype.updateMatrix.call(this, force);

		this.viewMatrix.getInverse(this.worldMatrix); // update view matrix

		_mat4_1.multiplyMatrices(this.projectionMatrix, this.viewMatrix); // get PV matrix
		this.frustum.setFromMatrix(_mat4_1); // update frustum
	},

	copy: function (source, recursive) {
		Object3D.prototype.copy.call(this, source, recursive);

		this.viewMatrix.copy(source.viewMatrix);
		this.projectionMatrix.copy(source.projectionMatrix);
		this.projectionMatrixInverse.copy(source.projectionMatrixInverse);

		this.frustum.copy(source.frustum);
		this.gammaFactor = source.gammaFactor;
		this.outputEncoding = source.outputEncoding;
		this.rect.copy(source.rect);
		this.frustumCulled = source.frustumCulled;

		return this;
	}

});

Object.defineProperties(Camera.prototype, {
	gammaInput: {
		get: function() {
			console.warn("zen3d.Camera: .gammaInput has been removed. Use texture.encoding instead.");
			return false;
		},
		set: function(value) {
			console.warn("zen3d.Camera: .gammaInput has been removed. Use texture.encoding instead.");
		}
	},
	gammaOutput: {
		get: function() {
			console.warn("zen3d.Camera: .gammaOutput has been removed. Use .outputEncoding or renderTarget.texture.encoding instead.");
			return this.outputEncoding == TEXEL_ENCODING_TYPE.GAMMA;
		},
		set: function(value) {
			console.warn("zen3d.Camera: .gammaOutput has been removed. Use .outputEncoding or renderTarget.texture.encoding instead.");
			if (value) {
				this.outputEncoding = TEXEL_ENCODING_TYPE.GAMMA;
			} else {
				this.outputEncoding = TEXEL_ENCODING_TYPE.LINEAR;
			}
		}
	}
});

export { Camera };