import { MathUtils } from '../math/MathUtils'
import { Box3 } from './Box3';
import { Frustum } from './Frustum'
var a = {
c: null, // center
u: [[], [], []], // basis vectors
e: [] // half width
};
var b = {
c: null, // center
u: [[], [], []], // basis vectors
e: [] // half width
};
var R = [[], [], []];
var AbsR = [[], [], []];
var t = [];
var v1 = [];
/**
* @class OBB
* 有向包围盒。
* @memberof THING
* @public
*/
class OBB extends Box3 {
/**
* 有向包围盒类,用于创建有向包围盒实例。
* @param {Array<number>} center 盒子的中心位置。
* @param {Array<number>} halfSize 盒子的半尺寸。
* @param {Array<number>} rotation 盒子的旋转四元数。
* @example
* // 创建一个有向包围盒
* let box = new BASE.THING.OBB([0, 0, 0], [1, 1, 1], [0, 0, 0, 1]);
* @public
*/
constructor(center, halfSize, rotation) {
super(center, halfSize);
this._rotation = rotation ? rotation.slice(0) : [0, 0, 0, 1];
}
_getWorldPosition(point, target) {
let position = target || [];
MathUtils.vec3.transformQuat(position, point, this._rotation);
return MathUtils.vec3.add(position, position, this.center);
}
/**
* 获取角度。
* @type {Array<number>}
* @example
* // 打印盒子的角度。
* console.log(box.angles);
* @public
*/
get angles() {
return MathUtils.getAnglesFromQuat(this._rotation);
}
/**
* 获取最小位置。
* @type {Array<number>}
* @example
* // 打印盒子的最小位置。
* console.log(box.min);
* @public
*/
get min() {
return this._getWorldPosition(MathUtils.negVector(this.halfSize));
}
/**
* 获取最大位置。
* @type {Array<number>}
* @example
* // 打印盒子的最大位置。
* console.log(box.max);
* @public
*/
get max() {
return this._getWorldPosition(this.halfSize);
}
get originalMin() {
return super.min;
}
get originalMax() {
return super.max;
}
getPoints() {
let points = this._getPoints(MathUtils.negVector(this.halfSize), this.halfSize);
return points.map(point => {
return this._getWorldPosition(point);
});
}
getLayoutPosition(types) {
let halfSize = this.halfSize;
let selfPosition = this._getLayoutPosition(types, MathUtils.negVector(halfSize), halfSize);
return this._getWorldPosition(selfPosition);
}
containsPoint(point) {
const v1 = MathUtils.subVector(point, this.center);
const xAxis = MathUtils.vec3ApplyQuat([1, 0, 0], this._rotation);
const yAxis = MathUtils.vec3ApplyQuat([0, 1, 0], this._rotation);
const zAxis = MathUtils.vec3ApplyQuat([0, 0, 1], this._rotation);
// project v1 onto each axis and check if these points lie inside the OBB
return MathUtils.abs(MathUtils.dotVector(v1, xAxis)) <= this.halfSize[0] &&
MathUtils.abs(MathUtils.dotVector(v1, yAxis)) <= this.halfSize[1] &&
MathUtils.abs(MathUtils.dotVector(v1, zAxis)) <= this.halfSize[2];
}
/**
* Reference: OBB-OBB Intersection in Real-Time Collision Detection
* @param obb
* @param epsilon
* @example
* // 创建两个有向包围盒
* let obb1 = new BASE.THING.OBB([0, 0, 0], [1, 1, 1], [0, 0, 0, 1]);
* let obb2 = new BASE.THING.OBB([2, 0, 0], [1, 1, 1], [0, 0, 0, 1]);
*
* // 检查两个包围盒是否相交
* let intersects = obb1.intersectsOBB(obb2);
* console.log(intersects); // false,因为两个包围盒之间有距离
*
* // 移动第二个包围盒使其与第一个相交
* let obb3 = new BASE.THING.OBB([1, 0, 0], [1, 1, 1], [0, 0, 0, 1]);
* intersects = obb1.intersectsOBB(obb3);
* console.log(intersects); // true,因为两个包围盒相交
*/
intersectsOBB(obb, epsilon = Number.EPSILON) {
// prepare data structures (the code uses the same nomenclature like the reference)
a.c = this.center;
a.e = this.halfSize;
const cMatrix = MathUtils.mat3.fromQuat([], this._rotation);
a.u[0] = [cMatrix[0], cMatrix[1], cMatrix[2]];
a.u[1] = [cMatrix[3], cMatrix[4], cMatrix[5]];
a.u[2] = [cMatrix[6], cMatrix[7], cMatrix[8]];
b.c = obb.center;
b.e = obb.halfSize;
const tMatrix = MathUtils.mat3.fromQuat([], obb._rotation);
b.u[0] = [tMatrix[0], tMatrix[1], tMatrix[2]];
b.u[1] = [tMatrix[3], tMatrix[4], tMatrix[5]];
b.u[2] = [tMatrix[6], tMatrix[7], tMatrix[8]];
// compute rotation matrix expressing b in a's coordinate frame
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
R[i][j] = MathUtils.dotVector(a.u[i], b.u[j]);
}
}
// compute translation vector
v1 = MathUtils.subVector(b.c, a.c);
// bring translation into a's coordinate frame
t[0] = MathUtils.dotVector(v1, a.u[0]);
t[1] = MathUtils.dotVector(v1, a.u[1]);
t[2] = MathUtils.dotVector(v1, a.u[2]);
// compute common subexpressions. Add in an epsilon term to
// counteract arithmetic errors when two edges are parallel and
// their cross product is (near) null
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
AbsR[i][j] = MathUtils.abs(R[i][j]) + epsilon;
}
}
let ra, rb;
// test axes L = A0, L = A1, L = A2
for (let i = 0; i < 3; i++) {
ra = a.e[i];
rb = b.e[0] * AbsR[i][0] + b.e[1] * AbsR[i][1] + b.e[2] * AbsR[i][2];
if (MathUtils.abs(t[i]) > ra + rb) return false;
}
// test axes L = B0, L = B1, L = B2
for (let i = 0; i < 3; i++) {
ra = a.e[0] * AbsR[0][i] + a.e[1] * AbsR[1][i] + a.e[2] * AbsR[2][i];
rb = b.e[i];
if (MathUtils.abs(t[0] * R[0][i] + t[1] * R[1][i] + t[2] * R[2][i]) > ra + rb) return false;
}
// test axis L = A0 x B0
ra = a.e[1] * AbsR[2][0] + a.e[2] * AbsR[1][0];
rb = b.e[1] * AbsR[0][2] + b.e[2] * AbsR[0][1];
if (MathUtils.abs(t[2] * R[1][0] - t[1] * R[2][0]) > ra + rb) return false;
// test axis L = A0 x B1
ra = a.e[1] * AbsR[2][1] + a.e[2] * AbsR[1][1];
rb = b.e[0] * AbsR[0][2] + b.e[2] * AbsR[0][0];
if (MathUtils.abs(t[2] * R[1][1] - t[1] * R[2][1]) > ra + rb) return false;
// test axis L = A0 x B2
ra = a.e[1] * AbsR[2][2] + a.e[2] * AbsR[1][2];
rb = b.e[0] * AbsR[0][1] + b.e[1] * AbsR[0][0];
if (MathUtils.abs(t[2] * R[1][2] - t[1] * R[2][2]) > ra + rb) return false;
// test axis L = A1 x B0
ra = a.e[0] * AbsR[2][0] + a.e[2] * AbsR[0][0];
rb = b.e[1] * AbsR[1][2] + b.e[2] * AbsR[1][1];
if (MathUtils.abs(t[0] * R[2][0] - t[2] * R[0][0]) > ra + rb) return false;
// test axis L = A1 x B1
ra = a.e[0] * AbsR[2][1] + a.e[2] * AbsR[0][1];
rb = b.e[0] * AbsR[1][2] + b.e[2] * AbsR[1][0];
if (MathUtils.abs(t[0] * R[2][1] - t[2] * R[0][1]) > ra + rb) return false;
// test axis L = A1 x B2
ra = a.e[0] * AbsR[2][2] + a.e[2] * AbsR[0][2];
rb = b.e[0] * AbsR[1][1] + b.e[1] * AbsR[1][0];
if (MathUtils.abs(t[0] * R[2][2] - t[2] * R[0][2]) > ra + rb) return false;
// test axis L = A2 x B0
ra = a.e[0] * AbsR[1][0] + a.e[1] * AbsR[0][0];
rb = b.e[1] * AbsR[2][2] + b.e[2] * AbsR[2][1];
if (MathUtils.abs(t[1] * R[0][0] - t[0] * R[1][0]) > ra + rb) return false;
// test axis L = A2 x B1
ra = a.e[0] * AbsR[1][1] + a.e[1] * AbsR[0][1];
rb = b.e[0] * AbsR[2][2] + b.e[2] * AbsR[2][0];
if (MathUtils.abs(t[1] * R[0][1] - t[0] * R[1][1]) > ra + rb) return false;
// test axis L = A2 x B2
ra = a.e[0] * AbsR[1][2] + a.e[1] * AbsR[0][2];
rb = b.e[0] * AbsR[2][1] + b.e[1] * AbsR[2][0];
if (MathUtils.abs(t[1] * R[0][2] - t[0] * R[1][2]) > ra + rb) return false;
// since no separating axis is found, the OBBs must be intersecting
return true;
}
toBoundingBoxAndTransform(box3, matrix) {
MathUtils.mat4.fromQuat(matrix, this._rotation);
box3.set([matrix[12], matrix[13], matrix[14]], this.halfSize)
return this;
}
intersectsFrustum(frustum) {
this.toBoundingBoxAndTransform(aabb, matrix);
MathUtils.mat4.invert(inverse, matrix);
localFrustum.copy(frustum).setFromProjectionMatrix(inverse);
return localFrustum.intersectsBoxV1(aabb);
}
}
const aabb = new Box3()
const localFrustum = new Frustum()
const matrix = MathUtils.createMat4();
const inverse = MathUtils.createMat4();
export { OBB }