Source: components/FrustumHelperComponent.js

import { Utils } from '../common/Utils';
import { MathUtils } from '../math/MathUtils';
import { BaseLineSegmentsHelperComponent } from './BaseLineSegmentsHelperComponent';

const __ = {
	private: Symbol('private'),
}

let _vec3 = MathUtils.createVec3();
let _mat4 = MathUtils.createMat4();

/**
 * @class FrustumHelperComponent
 * The frustum helper component.
 * @memberof THING
 * @extends THING.BaseLineSegmentsHelperComponent
 */
class FrustumHelperComponent extends BaseLineSegmentsHelperComponent {

	/**
	 * The frustum view helper, can show frustum of frustum.
	 */
	constructor() {
		super();

		this[__.private] = {};
		let _private = this[__.private];

		_private.pointsMap = {};

		_private.projectionMatrix = MathUtils.createMat4();
	}

	// #region Private Functions

	_addPoint(id, color) {
		let _private = this[__.private];

		_private.pointsMap[id] = _private.pointsMap[id] || [];
		_private.pointsMap[id].push(this.points.length);

		this.addPoint([0, 0, 0], color);
	}

	_addLine(a, b, color) {
		this._addPoint(a, color);
		this._addPoint(b, color);
	}

	_setPoint(point, projectionMatrixInverse, x, y, z) {
		let _private = this[__.private];

		const indexBuffer = _private.pointsMap[point];
		if (!indexBuffer) {
			return;
		}

		MathUtils.vec3.set(_vec3, x, y, z);
		MathUtils.vec3.transformMat4(_vec3, _vec3, projectionMatrixInverse);

		for (let i = 0, l = indexBuffer.length; i < l; i++) {
			let index = indexBuffer[i];

			let points = this.points[index];
			points[0] = _vec3[0];
			points[1] = _vec3[1];
			points[2] = _vec3[2];
		}
	}

	_updateProjectionMatrix() {
		let _private = this[__.private];

		_private.projectionMatrix = this.object.projectionMatrix;
	}

	_updateFrustumView() {
		let _private = this[__.private];

		this._updateProjectionMatrix();

		MathUtils.mat4.invert(_mat4, _private.projectionMatrix);

		const w = 1, h = 1;

		// center / target

		this._setPoint('c', _mat4, 0, 0, -1);
		this._setPoint('t', _mat4, 0, 0, 1);

		// near

		this._setPoint('n1', _mat4, -w, -h, -1);
		this._setPoint('n2', _mat4, w, -h, -1);
		this._setPoint('n3', _mat4, -w, h, -1);
		this._setPoint('n4', _mat4, w, h, -1);

		// far

		this._setPoint('f1', _mat4, -w, -h, 1);
		this._setPoint('f2', _mat4, w, -h, 1);
		this._setPoint('f3', _mat4, -w, h, 1);
		this._setPoint('f4', _mat4, w, h, 1);

		// up

		this._setPoint('u1', _mat4, w * 0.7, h * 1.1, -1);
		this._setPoint('u2', _mat4, -w * 0.7, h * 1.1, -1);
		this._setPoint('u3', _mat4, 0, h * 2, -1);

		// cross

		this._setPoint('cf1', _mat4, -w, 0, 1);
		this._setPoint('cf2', _mat4, w, 0, 1);
		this._setPoint('cf3', _mat4, 0, -h, 1);
		this._setPoint('cf4', _mat4, 0, h, 1);

		this._setPoint('cn1', _mat4, -w, 0, -1);
		this._setPoint('cn2', _mat4, w, 0, -1);
		this._setPoint('cn3', _mat4, 0, -h, -1);
		this._setPoint('cn4', _mat4, 0, h, -1);

		// Update points
		this.lineSegments.setPoints(this.points);
	}

	// #endregion

	// #region BaseComponent Interface

	onAdd(object) {
		super.onAdd(object);

		// Initialize points and colors

		const colorFrustum = Utils.parseColor(0xffaa00);
		const colorCone = Utils.parseColor(0xff0000);
		const colorUp = Utils.parseColor(0x00aaff);
		const colorTarget = Utils.parseColor(0xffffff);
		const colorCross = Utils.parseColor(0x333333);

		// near

		this._addLine('n1', 'n2', colorFrustum);
		this._addLine('n2', 'n4', colorFrustum);
		this._addLine('n4', 'n3', colorFrustum);
		this._addLine('n3', 'n1', colorFrustum);

		// far

		this._addLine('f1', 'f2', colorFrustum);
		this._addLine('f2', 'f4', colorFrustum);
		this._addLine('f4', 'f3', colorFrustum);
		this._addLine('f3', 'f1', colorFrustum);

		// sides

		this._addLine('n1', 'f1', colorFrustum);
		this._addLine('n2', 'f2', colorFrustum);
		this._addLine('n3', 'f3', colorFrustum);
		this._addLine('n4', 'f4', colorFrustum);

		// cone

		this._addLine('p', 'n1', colorCone);
		this._addLine('p', 'n2', colorCone);
		this._addLine('p', 'n3', colorCone);
		this._addLine('p', 'n4', colorCone);

		// up

		this._addLine('u1', 'u2', colorUp);
		this._addLine('u2', 'u3', colorUp);
		this._addLine('u3', 'u1', colorUp);

		// target

		this._addLine('c', 't', colorTarget);
		this._addLine('p', 'c', colorCross);

		// cross

		this._addLine('cn1', 'cn2', colorCross);
		this._addLine('cn3', 'cn4', colorCross);

		this._addLine('cf1', 'cf2', colorCross);
		this._addLine('cf3', 'cf4', colorCross);

		// Update points and colors
		this.lineSegments.setSize(this.points.length * 3);
		this.lineSegments.setColors(this.colors);

		// Update default options
		this.lineSegments.setVisible(false);
		Utils.markAsDebugNode(this.lineSegments);
	}

	onUpdate(deltaTime) {
		this._updateFrustumView();
	}

	// #endregion

	// #region Accessors

	get projectionMatrix() {
		let _private = this[__.private];

		return _private.projectionMatrix;
	}

	set projectionMatrix(value) {
		let _private = this[__.private];

		_private.projectionMatrix = value;
	}

	/**
	 * Show/Hide helper.
	 * @type {Boolean}
	 */
	get visible() {
		return this.lineSegments.getVisible();
	}
	set visible(value) {
		this.lineSegments.setVisible(value);
	}

	// #endregion

}

export { FrustumHelperComponent }