import { EventDispatcher, MathUtils } from '@uino/base-thing';
import { ObjectColliderBoxHelper } from '../helpers/ObjectColliderBoxHelper';
import { ObjectColliderSphereHelper } from '../helpers/ObjectColliderSphereHelper';
import { BaseComponent } from './BaseComponent';
import { EventType, ColliderType } from '../const';
const __ = {
private: Symbol('private'),
}
let _vec3_0 = MathUtils.createVec3();
let _vec3_1 = MathUtils.createVec3();
let _vec3_2 = MathUtils.createVec3();
let _vec3_3 = MathUtils.createVec3();
let _vec3_4 = MathUtils.createVec3();
let _vec3_5 = MathUtils.createVec3();
let _vec3_6 = MathUtils.createVec3();
let _vec3_7 = MathUtils.createVec3();
let _vec3Lib = MathUtils.vec3;
/**
* @class ColliderComponent
* The collider component.
* @memberof THING
* @extends THING.BaseComponent
*/
class ColliderComponent extends BaseComponent {
static mustCopyWithInstance = true;
/**
* The collider of object(s) what can listen on collider events to process hit logic code.
*/
constructor() {
super();
this[__.private] = {};
let _private = this[__.private];
_private.enable = true;
_private.visible = false;
_private.helper = null;
_private.eventDispatcher = new EventDispatcher();
// For fast usage, we define here ...
// The common info
this._radius = 0.5;
this._halfSize = null;
this._offset = [0, 0, 0];
this._mode = ColliderType.Box;
// Get the min and max points
let min = [-this._radius, -this._radius, -this._radius];
let max = [this._radius, this._radius, this._radius];
// The area info
this._matrixWorld = null;
this._areaInfo = {
base: {
min,
max,
},
local: [
// TOP
[min[0], min[1], min[2]], // 0
[max[0], min[1], min[2]], // 1
[max[0], min[1], max[2]], // 2
[min[0], min[1], max[2]], // 3
// BOTTOM
[min[0], max[1], min[2]], // 4
[max[0], max[1], min[2]], // 5
[max[0], max[1], max[2]], // 6
[min[0], max[1], max[2]], // 7
],
transform: {
min: min.slice(0),
max: max.slice(0)
},
};
}
// #region Private Functions
_createHelper() {
let _private = this[__.private];
switch (this._mode) {
case ColliderType.Box:
_private.helper = new ObjectColliderBoxHelper({ object: this.object });
this.app.scene.rootObjects['debug'].node.add(_private.helper.lines);
break;
case ColliderType.Sphere:
_private.helper = new ObjectColliderSphereHelper({ object: this.object });
this.app.scene.rootObjects['debug'].node.add(_private.helper.lines);
break;
}
}
_disposeHelper() {
let _private = this[__.private];
if (_private.helper) {
_private.helper.dispose();
_private.helper = null;
}
}
_refreshHelper() {
this._disposeHelper();
this._createHelper();
}
_refreshAreaInfo() {
let areaInfo = this._areaInfo;
let min = [], max = [];
MathUtils.vec3.add(min, areaInfo.base.min, this._offset);
MathUtils.vec3.add(max, areaInfo.base.max, this._offset);
areaInfo.local = [
// TOP
[min[0], min[1], min[2]], // 0
[max[0], min[1], min[2]], // 1
[max[0], min[1], max[2]], // 2
[min[0], min[1], max[2]], // 3
// BOTTOM
[min[0], max[1], min[2]], // 4
[max[0], max[1], min[2]], // 5
[max[0], max[1], max[2]], // 6
[min[0], max[1], max[2]], // 7
];
}
// #endregion
// #region Overrides
onBeforeRemove() {
this.enable = false;
this.visible = false;
}
// #endregion
// #region Callbacks
onColliderEnter(collider) {
let _private = this[__.private];
_private.eventDispatcher.dispatchEvent({ type: EventType.ColliderEnter.toLowerCase(), object: collider.object });
}
onColliderLeave(collider) {
let _private = this[__.private];
_private.eventDispatcher.dispatchEvent({ type: EventType.ColliderLeave.toLowerCase(), object: collider.object });
}
// #endregion
// #region Events
/**
* Add event listener.
* @param {String} type The event type.
* @param {Function} listener The event callback function.
*/
addEventListener(type, listener) {
let _private = this[__.private];
_private.eventDispatcher.addEventListener(type.toLowerCase(), listener);
}
/**
* Remove event listener.
* @param {String} type The event type.
* @param {Function} listener The event callback function.
*/
removeEventListener(type, listener) {
let _private = this[__.private];
_private.eventDispatcher.removeEventListener(type.toLowerCase(), listener);
}
// #endregion
test(collider) {
switch (this._mode) {
case ColliderType.Box:
let transform1 = this._areaInfo.transform;
let transform2 = collider._areaInfo.transform;
return MathUtils.intersectsBox(transform1.min, transform1.max, transform2.min, transform2.max);
case ColliderType.Sphere:
let matrixWorld1 = this._matrixWorld;
let matrixWorld2 = collider._matrixWorld;
let dx = matrixWorld1[12] - matrixWorld2[12];
let dy = matrixWorld1[13] - matrixWorld2[13];
let dz = matrixWorld1[14] - matrixWorld2[14];
let distance = Math.sqrt(dx * dx + dy * dy + dz * dz) * 0.5;
if (distance < this._radius || distance < collider._radius) {
return true;
}
break;
default:
break;
}
return false;
}
setMatrixWorld(value) {
this._matrixWorld = value;
let areaInfo = this._areaInfo;
let min = areaInfo.transform.min;
let max = areaInfo.transform.max;
min[0] = min[1] = min[2] = +Infinity;
max[0] = max[1] = max[2] = -Infinity;
let local = this._areaInfo.local;
_vec3Lib.transformMat4(_vec3_0, local[0], this._matrixWorld);
_vec3Lib.min(min, min, _vec3_0);
_vec3Lib.max(max, max, _vec3_0);
_vec3Lib.transformMat4(_vec3_1, local[1], this._matrixWorld);
_vec3Lib.min(min, min, _vec3_1);
_vec3Lib.max(max, max, _vec3_1);
_vec3Lib.transformMat4(_vec3_2, local[2], this._matrixWorld);
_vec3Lib.min(min, min, _vec3_2);
_vec3Lib.max(max, max, _vec3_2);
_vec3Lib.transformMat4(_vec3_3, local[3], this._matrixWorld);
_vec3Lib.min(min, min, _vec3_3);
_vec3Lib.max(max, max, _vec3_3);
_vec3Lib.transformMat4(_vec3_4, local[4], this._matrixWorld);
_vec3Lib.min(min, min, _vec3_4);
_vec3Lib.max(max, max, _vec3_4);
_vec3Lib.transformMat4(_vec3_5, local[5], this._matrixWorld);
_vec3Lib.min(min, min, _vec3_5);
_vec3Lib.max(max, max, _vec3_5);
_vec3Lib.transformMat4(_vec3_6, local[6], this._matrixWorld);
_vec3Lib.min(min, min, _vec3_6);
_vec3Lib.max(max, max, _vec3_6);
_vec3Lib.transformMat4(_vec3_7, local[7], this._matrixWorld);
_vec3Lib.min(min, min, _vec3_7);
_vec3Lib.max(max, max, _vec3_7);
}
// #region Accessors
/**
* Enable/Disable.
* @type {Boolean}
*/
get enable() {
let _private = this[__.private];
return _private.enable;
}
set enable(value) {
let _private = this[__.private];
_private.enable = value;
if (value) {
this.object.app.colliderManager.add(this);
}
else {
this.object.app.colliderManager.remove(this);
}
}
/**
* Get/Set the mode.
* @type {ColliderType}
*/
get mode() {
return this._mode;
}
set mode(value) {
let _private = this[__.private];
if (this._mode == value) {
return;
}
this._mode = value;
// If helper is showing then refresh it
if (_private.visible) {
this._refreshHelper();
}
}
/**
* Get/Set the radius.
* @type {Number}
*/
get radius() {
return this._radius;
}
set radius(value) {
this._radius = value;
}
/**
* Get/Set the offset.
* @type {Array<Number>}
*/
get offset() {
return this._offset;
}
set offset(value) {
if (value) {
this._offset = value.slice(0);
}
else {
this._offset = [0, 0, 0];
}
this._refreshAreaInfo();
}
/**
* Get/Set the half size.
* @type {Array<Number>}
*/
get halfSize() {
return this._halfSize;
}
set halfSize(value) {
this._halfSize = value.slice(0);
let base = this._areaInfo.base;
base.min[0] = -value[0];
base.min[1] = -value[1];
base.min[2] = -value[2];
base.max[0] = value[0];
base.max[1] = value[1];
base.max[2] = value[2];
this._refreshAreaInfo();
}
/**
* Get/Set the visible.
* @type {Boolean}
*/
get visible() {
let _private = this[__.private];
return _private.visible;
}
set visible(value) {
let _private = this[__.private];
if (_private.visible == value) {
return;
}
_private.visible = value;
if (value) {
this._createHelper();
}
else {
this._disposeHelper();
}
}
/**
* Get the area info.
* @type {Object}
* @private
*/
get areaInfo() {
return this._areaInfo;
}
// #endregion
}
export { ColliderComponent }