import { MathUtils } from '../math/MathUtils';
import { BaseTickableObject3D } from './BaseTickableObject3D';
const __ = {
private: Symbol('private'),
}
let _vec3_1 = MathUtils.createVec3();
let _vec3_2 = MathUtils.createVec3();
let _mat3 = MathUtils.createMat3();
let _mat4 = MathUtils.createMat4();
// #region Private Functions
// Convert clipping plane to world space.
function _convertPlaneToWorldSpace(plane, matrixWorld) {
let dir_1 = MathUtils.vec3.copy(_vec3_1, plane.direction);
let dir_2 = MathUtils.vec3.copy(_vec3_2, plane.direction);
const normalMatrix = MathUtils.mat3.normalFromMat4(_mat3, matrixWorld);
const referencePoint = MathUtils.vec3.transformMat4(dir_1, MathUtils.vec3.scale(dir_1, dir_1, -plane.height), matrixWorld);
const normal = MathUtils.vec3.normalize(dir_2, MathUtils.vec3.transformMat3(dir_2, dir_2, normalMatrix));
const constant = -MathUtils.vec3.dot(referencePoint, normal);
return {
direction: normal.slice(0),
height: constant
}
}
// #endregion
/**
* @class ClippingPlanes
* The clipping planes object.
* @memberof THING
* @extends THING.BaseTickableObject3D
* @public
*/
class ClippingPlanes extends BaseTickableObject3D {
/**
* The clipping planes that use with object style to clip object by planes.
* @param {Object} param The initial parameters.
*/
constructor(param) {
super(param);
this[__.private] = {};
let _private = this[__.private];
_private.lastMat4 = null;
_private.resources = [];
_private.planes = null;
_private.worldPlanes = null;
let planes = param['planes'];
if (planes) {
this.planes = planes;
}
}
// #region Private
_updatePlanes(force) {
let _private = this[__.private];
// Get the object's matrix world
this.node.getMatrixWorld(_mat4);
// Check whether matrix world has been changed
if (!force) {
if (_private.lastMat4) {
if (MathUtils.mat4.equals(_mat4, _private.lastMat4)) {
return;
}
}
else {
_private.lastMat4 = MathUtils.createMat4();
}
}
// Keep matrix world to latest
if (_private.lastMat4) {
MathUtils.mat4.copy(_private.lastMat4, _mat4);
}
// Convert local planes to world planes
_private.worldPlanes = _private.planes.map(plane => {
return _convertPlaneToWorldSpace(plane, _mat4);
});
// Update styles's resource clipping planes
_private.resources.forEach(resource => {
resource.setClippingPlanes(_private.worldPlanes);
});
}
// #endregion
// #region BaseObject Interface
onUpdate(deltaTime) {
super.onUpdate(deltaTime);
let _private = this[__.private];
if (!_private.planes) {
return;
}
if (_private.resources.length) {
this._updatePlanes();
}
}
// #endregion
addStyleResource(resource) {
let _private = this[__.private];
if (_private.resources.indexOf(resource) !== -1) {
return;
}
_private.resources.push(resource);
resource.setClippingPlanes(_private.worldPlanes);
}
removeStyleResource(resource) {
let _private = this[__.private];
let index = _private.resources.indexOf(resource);
if (index === -1) {
return;
}
_private.resources.splice(index, 1);
resource.setClippingPlanes(null);
}
// #endregion
// #region Accessor
/**
* @typedef {Object} ClippingPlaneResult
* @property {Array<Number>} direction The direction in world space.
* @property {Number} height The height.
*/
/**
* Get/Set the clipping planes.
* @type {Array<ClippingPlaneResult>}
* @public
*/
get planes() {
let _private = this[__.private];
return _private.planes;
}
set planes(value) {
let _private = this[__.private];
_private.planes = value.map(plane => {
return {
direction: plane.direction.slice(0),
height: plane.height
}
});
this._updatePlanes(true);
}
// #endregion
/**
* Check whether it's ClippingPlanes type or inherit from it.
* @type {Boolean}
* @public
*/
get isClippingPlanes() {
return true;
}
}
export { ClippingPlanes }