import { Plane } from "@uino/base-thing";
import { Object3D } from './Object3D';
import { MathUtils } from '../math/MathUtils';
import { FrustumHelperComponent } from '../components/FrustumHelperComponent';
const __ = {
private: Symbol('private'),
}
const registerComponentParam = { isResident: true };
/**
* @class Frustum
* The frustum entity object.
* @memberof THING
* @extends THING.Object3D
* @public
*/
class Frustum extends Object3D {
/**
* The frustum object in scene.
* @param {Object} param The initial parameters.
*/
constructor(param = {}) {
super(param);
this[__.private] = {};
let _private = this[__.private];
_private.fov = param.fov || 50;
_private.aspect = param.aspect || 1;
_private.near = param.near || 0.1;
_private.far = param.far || 1;
_private.projectionMatrix = MathUtils.createMat4();
this.planes = [
new Plane(),
new Plane(),
new Plane(),
new Plane(),
new Plane(),
new Plane()
];
this._updateProjectionMatrix();
}
// #region Overrides
onSetupComponent(param) {
super.onSetupComponent(param);
this.addComponent(FrustumHelperComponent, 'helper', registerComponentParam);
}
// #endregion
// #region Private
_updateProjectionMatrix() {
let _private = this[__.private];
MathUtils.mat4.perspective(_private.projectionMatrix, _private.fov / 180 * Math.PI, _private.aspect, _private.near, _private.far);
this._setFromProjectionMatrix(_private.projectionMatrix);
}
_setFromProjectionMatrix(m) {
const planes = this.planes;
const me = m;
const me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3];
const me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7];
const me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11];
const me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15];
planes[0].setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12).normalize();
planes[1].setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12).normalize();
planes[2].setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13).normalize();
planes[3].setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13).normalize();
planes[4].setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14).normalize();
planes[5].setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14).normalize();
return this;
}
// #endregion
/**
* set projection matrix.
* @param {Number} fov The fov.
* @param {Number} aspect The aspect.
* @param {Number} near The near.
* @param {Number} far The far.
*/
setProjectionMatrix(fov, aspect, near, far) {
let _private = this[__.private];
_private.fov = fov;
_private.aspect = aspect;
_private.near = near;
_private.far = far;
this._updateProjectionMatrix();
}
/**
* intersects object
* @param {Object} object
* @returns {Boolean}
*/
intersectsObject(object) {
const sphere = object.getAABB().getBoundingSphere();
return this.intersectsSphere(sphere);
}
/**
* intersects sphere
* @param {Object} sphere
* @param {Array<Number>} sphere.center
* @param {Number} sphere.radius
* @returns {Boolean}
*/
intersectsSphere(sphere) {
const planes = this.planes;
const center = sphere.center;
const negRadius = -sphere.radius;
for (let i = 0; i < 6; i++) {
const distance = planes[i].distanceToPoint(center);
if (distance < negRadius) {
return false;
}
}
return true;
}
/**
* intersects sphere
* @param {Array<Number>} point
* @returns {Boolean}
*/
containsPoint(point) {
const planes = this.planes;
for (let i = 0; i < 6; i++) {
if (planes[i].distanceToPoint(point) < 0) {
return false;
}
}
return true;
}
// #region Common
/**
* @private
*/
get projectionMatrix() {
let _private = this[__.private];
return _private.projectionMatrix;
}
/**
* Get/Set fov.
* @type {Number}
* @example
* object.name = 45;
*/
get fov() {
let _private = this[__.private];
return _private.fov;
}
set fov(value) {
let _private = this[__.private];
_private.fov = value;
this._updateProjectionMatrix();
}
/**
* Get/Set aspect.
* @type {Number}
* @example
* object.aspect = 1;
*/
get aspect() {
let _private = this[__.private];
return _private.aspect;
}
set aspect(value) {
let _private = this[__.private];
_private.aspect = value;
this._updateProjectionMatrix();
}
/**
* Get/Set near.
* @type {Number}
* @example
* object.near = 0.1;
*/
get near() {
let _private = this[__.private];
return _private.near;
}
set near(value) {
let _private = this[__.private];
_private.near = value;
this._updateProjectionMatrix();
}
/**
* Get/Set far.
* @type {Number}
* @example
* object.far = 10;
*/
get far() {
let _private = this[__.private];
return _private.far;
}
set far(value) {
let _private = this[__.private];
_private.far = value;
this._updateProjectionMatrix();
}
/**
* Check whether it's frustum object.
* @type {Boolean}
*/
get isFrustum() {
return true;
}
// #endregion
}
export { Frustum }