import { Box3, OBB } from '@uino/base-thing';
import { Utils } from '../common/Utils';
import { MathUtils } from '../math/MathUtils';
import { BaseComponent } from './BaseComponent';
import { LightSphereModeType, NodeUserDataType, InheritType } from '../const';
const __ = {
private: Symbol('private'),
}
let _aabb = { center: MathUtils.createVec3(), halfSize: MathUtils.createVec3(), rotation: MathUtils.createQuat() };
let _obb = { center: MathUtils.createVec3(), halfSize: MathUtils.createVec3(), rotation: MathUtils.createQuat() };
let _emptyAABB = new Box3();
let _emptyOBB = new OBB();
let _origin = [0, 0, 0];
// #region Private Functions
// Filter node callback.
function _filterNodeCallback(node) {
let userData = node.getUserData();
if (userData[NodeUserDataType.BoundingBoxInheritType] == InheritType.Stop) {
return false;
}
// if the node's parent node type is break
let parent = node.getParent();
let parentUserData = parent ? parent.getUserData() : null;
if (parentUserData && parentUserData[NodeUserDataType.BoundingBoxInheritType] == InheritType.Break) {
// if node is bodyNode,return true
// if node is child of that who's type is break,return false
if (userData[NodeUserDataType.BoundingBoxInheritType] != 'continue') {
return false;
}
}
if (userData['isDebugNode']) {
return false;
}
if (userData['active'] === false) {
return false;
}
return true;
}
// #endregion
/**
* @class BoundingComponent
* The bounding component.
* @memberof THING
* @extends THING.BaseComponent
*/
class BoundingComponent extends BaseComponent {
static mustCopyWithInstance = true;
/**
* The bounding box(AABB, OBB) of object.
*/
constructor() {
super();
this[__.private] = {};
let _private = this[__.private];
_private.inheritType = InheritType.Normal;
_private.localBoundingBoxResult = null;
_private.lightSphereModeType = LightSphereModeType.Dynamic;
_private.lightCenterInSelfSpace = null;
_private.lightSphere = null;
}
// #region Private Functions
_getNode(recursive) {
let object = this.object;
return recursive ? object.node : object.bodyNode;
}
_getLightSphere() {
let _private = this[__.private];
return _private.lightSphere = _private.lightSphere || {
center: [0, 0, 0],
radius: 0,
shadowRadius: 0
};
}
// #endregion
getAABB(recursive = true, updateMatrix = true, local = false) {
let node = this._getNode(recursive);
// Get bounding box
if (node.getAABB) {
let box = node.getAABB(_aabb, _filterNodeCallback, updateMatrix, local);
if (MathUtils.exactEqualsVector3(box.halfSize, _origin)) {
return new Box3(this.object.position, [Number.EPSILON, Number.EPSILON, Number.EPSILON]);
}
else {
return new Box3(box.center, MathUtils.fixScaleFactor(box.halfSize));
}
}
else {
return _emptyAABB;
}
}
getOBB(recursive = true, updateMatrix = true, local = false) {
let node = this._getNode(recursive);
// Get oriented bounding box
if (node.getOBB) {
let box = node.getOBB(_obb, _filterNodeCallback, updateMatrix, local);
if (MathUtils.exactEqualsVector3(box.halfSize, _origin)) {
return new OBB(this.object.position, [Number.EPSILON, Number.EPSILON, Number.EPSILON], box.rotation);
}
else {
return new OBB(box.center, MathUtils.fixScaleFactor(box.halfSize), box.rotation);
}
}
else {
return _emptyOBB;
}
}
/**
* Get the initial local bounding box.
* @returns {BoundingBoxResult}
* @private
*/
getInitialLocalBoundingBox() {
let _private = this[__.private];
return _private.localBoundingBoxResult;
}
/**
* Set the local bounding box.
* @param {BoundingBoxResult} value The value.
* @private
*/
setInitialLocalBoundingBox(value) {
let _private = this[__.private];
let bodyNode = this.object.bodyNode;
if (value) {
_private.localBoundingBoxResult = {
center: value.center,
halfSize: value.halfSize
};
if (bodyNode.isRenderableNode) {
bodyNode.setLocalBoundingBox(_private.localBoundingBoxResult);
}
}
else if (_private.localBoundingBoxResult) {
_private.localBoundingBoxResult = null;
if (bodyNode.isRenderableNode) {
bodyNode.clearLocalBoundingBox();
}
}
}
/**
* @typedef {Object} LightSphereInfo
* @property {Array<Number>} center The center of light sphere.
* @property {Number} radius The radius of light sphere, 0 indicates use the radius of bounding box.
* @property {Number} shadowRadius The shadow's radius of light sphere, 0 indicates use the radius or radius of bounding box.
*/
/**
* Get light sphere info.
* @param {Boolean} [updateMatrix=true] True indicates update matrix for self and all children.
* @returns {LightSphereInfo}
* @private
*/
getLightSphere(updateMatrix = true) {
let _private = this[__.private];
let lightSphere = this._getLightSphere();
switch (_private.lightSphereModeType) {
case LightSphereModeType.Dynamic:
let box = this.getAABB(true, updateMatrix);
lightSphere.center = box.center;
lightSphere.radius = box.radius;
break;
case LightSphereModeType.Static:
lightSphere.center = this.object.selfToWorld(_private.lightCenterInSelfSpace);
break;
default:
break;
}
return lightSphere;
}
// #region Accessor
/**
* Get/Set light sphere mode type.
* @type {LightSphereModeType}
* @private
*/
get lightSphereModeType() {
let _private = this[__.private];
return _private.lightSphereModeType;
}
set lightSphereModeType(value) {
let _private = this[__.private];
switch (value) {
case LightSphereModeType.Static:
let lightSphere = this._getLightSphere();
let box = this.getAABB(true);
_private.lightCenterInSelfSpace = this.object.worldToSelf(box.center);
lightSphere.radius = box.radius;
break;
default:
break;
}
_private.lightSphereModeType = value;
}
get boundingBox() {
return this.getAABB();
}
get orientedBox() {
return this.getOBB();
}
get initialLocalBoundingBox() {
return this.getInitialLocalBoundingBox();
}
set initialLocalBoundingBox(value) {
this.setInitialLocalBoundingBox(value);
}
/**
* Get/Set the bounding box inherit type.
* @type {InheritType}
* @example
* let component = new THING.BoundingComponent();;
* component.inheritType = InheritType.Normal
* // @expect(component.inheritType == InheritType.Normal)
*/
get inheritType() {
let _private = this[__.private];
return _private.inheritType;
}
set inheritType(value) {
let _private = this[__.private];
let object = this.object;
if (value == InheritType.Normal) {
object.node.getUserData()[NodeUserDataType.BoundingBoxInheritType] = InheritType.Normal;
object.bodyNode.getUserData()[NodeUserDataType.BoundingBoxInheritType] = InheritType.Normal;
}
else if (value == InheritType.Break) {
object.node.getUserData()[NodeUserDataType.BoundingBoxInheritType] = InheritType.Break;
object.bodyNode.getUserData()[NodeUserDataType.BoundingBoxInheritType] = 'continue';
}
else if (value == InheritType.Jump) {
object.node.getUserData()[NodeUserDataType.BoundingBoxInheritType] = InheritType.Normal;
object.bodyNode.getUserData()[NodeUserDataType.BoundingBoxInheritType] = InheritType.Stop;
}
else if (value == InheritType.Stop) {
object.node.getUserData()[NodeUserDataType.BoundingBoxInheritType] = InheritType.Stop;
object.bodyNode.getUserData()[NodeUserDataType.BoundingBoxInheritType] = InheritType.Stop;
}
_private.inheritType = value;
}
/**
* Get/Set the bounding box inherit type.
* @type {InheritType}
* @deprecated Since 2.7.0
* @private
*/
get inheritActionType() {
Utils.warn(`Please use '.inheritType' attribute, '.inheritActionType' has been deprecated`);
return this.inheritType;
}
set inheritActionType(value) {
Utils.warn(`Please use '.inheritType' attribute, '.inheritActionType' has been deprecated`);
this.inheritType = value;
}
/**
* Get/Set the size of the picked bounding box (set null to clear).
* @type {Array<Number>}
*/
get pickedSize() {
let target = this.object.node.getPickBoundingBox();
if (!target) {
return null;
}
return MathUtils.scaleVector(target.halfSize, 2);
}
set pickedSize(value) {
if (value) {
let center = this.getAABB().center;
this.object.node.setPickBoundingBox({ center, halfSize: MathUtils.scaleVector(value, 0.5) });
}
else {
this.object.node.clearPickBoundingBox();
}
}
// #endregion
}
// #endregion
export { BoundingComponent }