311 lines
9.0 KiB
JavaScript
311 lines
9.0 KiB
JavaScript
import Vector3 from './Vector3';
|
|
import vec3 from '../glmatrix/vec3';
|
|
|
|
var vec3Set = vec3.set;
|
|
var vec3Copy = vec3.copy;
|
|
|
|
/**
|
|
* Axis aligned bounding box
|
|
* @constructor
|
|
* @alias clay.BoundingBox
|
|
* @param {clay.Vector3} [min]
|
|
* @param {clay.Vector3} [max]
|
|
*/
|
|
var BoundingBox = function (min, max) {
|
|
|
|
/**
|
|
* Minimum coords of bounding box
|
|
* @type {clay.Vector3}
|
|
*/
|
|
this.min = min || new Vector3(Infinity, Infinity, Infinity);
|
|
|
|
/**
|
|
* Maximum coords of bounding box
|
|
* @type {clay.Vector3}
|
|
*/
|
|
this.max = max || new Vector3(-Infinity, -Infinity, -Infinity);
|
|
|
|
this.vertices = null;
|
|
};
|
|
|
|
BoundingBox.prototype = {
|
|
|
|
constructor: BoundingBox,
|
|
/**
|
|
* Update min and max coords from a vertices array
|
|
* @param {array} vertices
|
|
*/
|
|
updateFromVertices: function (vertices) {
|
|
if (vertices.length > 0) {
|
|
var min = this.min;
|
|
var max = this.max;
|
|
var minArr = min.array;
|
|
var maxArr = max.array;
|
|
vec3Copy(minArr, vertices[0]);
|
|
vec3Copy(maxArr, vertices[0]);
|
|
for (var i = 1; i < vertices.length; i++) {
|
|
var vertex = vertices[i];
|
|
|
|
if (vertex[0] < minArr[0]) { minArr[0] = vertex[0]; }
|
|
if (vertex[1] < minArr[1]) { minArr[1] = vertex[1]; }
|
|
if (vertex[2] < minArr[2]) { minArr[2] = vertex[2]; }
|
|
|
|
if (vertex[0] > maxArr[0]) { maxArr[0] = vertex[0]; }
|
|
if (vertex[1] > maxArr[1]) { maxArr[1] = vertex[1]; }
|
|
if (vertex[2] > maxArr[2]) { maxArr[2] = vertex[2]; }
|
|
}
|
|
min._dirty = true;
|
|
max._dirty = true;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Union operation with another bounding box
|
|
* @param {clay.BoundingBox} bbox
|
|
*/
|
|
union: function (bbox) {
|
|
var min = this.min;
|
|
var max = this.max;
|
|
vec3.min(min.array, min.array, bbox.min.array);
|
|
vec3.max(max.array, max.array, bbox.max.array);
|
|
min._dirty = true;
|
|
max._dirty = true;
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Intersection operation with another bounding box
|
|
* @param {clay.BoundingBox} bbox
|
|
*/
|
|
intersection: function (bbox) {
|
|
var min = this.min;
|
|
var max = this.max;
|
|
vec3.max(min.array, min.array, bbox.min.array);
|
|
vec3.min(max.array, max.array, bbox.max.array);
|
|
min._dirty = true;
|
|
max._dirty = true;
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* If intersect with another bounding box
|
|
* @param {clay.BoundingBox} bbox
|
|
* @return {boolean}
|
|
*/
|
|
intersectBoundingBox: function (bbox) {
|
|
var _min = this.min.array;
|
|
var _max = this.max.array;
|
|
|
|
var _min2 = bbox.min.array;
|
|
var _max2 = bbox.max.array;
|
|
|
|
return ! (_min[0] > _max2[0] || _min[1] > _max2[1] || _min[2] > _max2[2]
|
|
|| _max[0] < _min2[0] || _max[1] < _min2[1] || _max[2] < _min2[2]);
|
|
},
|
|
|
|
/**
|
|
* If contain another bounding box entirely
|
|
* @param {clay.BoundingBox} bbox
|
|
* @return {boolean}
|
|
*/
|
|
containBoundingBox: function (bbox) {
|
|
|
|
var _min = this.min.array;
|
|
var _max = this.max.array;
|
|
|
|
var _min2 = bbox.min.array;
|
|
var _max2 = bbox.max.array;
|
|
|
|
return _min[0] <= _min2[0] && _min[1] <= _min2[1] && _min[2] <= _min2[2]
|
|
&& _max[0] >= _max2[0] && _max[1] >= _max2[1] && _max[2] >= _max2[2];
|
|
},
|
|
|
|
/**
|
|
* If contain point entirely
|
|
* @param {clay.Vector3} point
|
|
* @return {boolean}
|
|
*/
|
|
containPoint: function (p) {
|
|
var _min = this.min.array;
|
|
var _max = this.max.array;
|
|
|
|
var _p = p.array;
|
|
|
|
return _min[0] <= _p[0] && _min[1] <= _p[1] && _min[2] <= _p[2]
|
|
&& _max[0] >= _p[0] && _max[1] >= _p[1] && _max[2] >= _p[2];
|
|
},
|
|
|
|
/**
|
|
* If bounding box is finite
|
|
*/
|
|
isFinite: function () {
|
|
var _min = this.min.array;
|
|
var _max = this.max.array;
|
|
return isFinite(_min[0]) && isFinite(_min[1]) && isFinite(_min[2])
|
|
&& isFinite(_max[0]) && isFinite(_max[1]) && isFinite(_max[2]);
|
|
},
|
|
|
|
/**
|
|
* Apply an affine transform matrix to the bounding box
|
|
* @param {clay.Matrix4} matrix
|
|
*/
|
|
applyTransform: function (matrix) {
|
|
this.transformFrom(this, matrix);
|
|
},
|
|
|
|
/**
|
|
* Get from another bounding box and an affine transform matrix.
|
|
* @param {clay.BoundingBox} source
|
|
* @param {clay.Matrix4} matrix
|
|
*/
|
|
transformFrom: (function () {
|
|
// http://dev.theomader.com/transform-bounding-boxes/
|
|
var xa = vec3.create();
|
|
var xb = vec3.create();
|
|
var ya = vec3.create();
|
|
var yb = vec3.create();
|
|
var za = vec3.create();
|
|
var zb = vec3.create();
|
|
|
|
return function (source, matrix) {
|
|
var min = source.min.array;
|
|
var max = source.max.array;
|
|
|
|
var m = matrix.array;
|
|
|
|
xa[0] = m[0] * min[0]; xa[1] = m[1] * min[0]; xa[2] = m[2] * min[0];
|
|
xb[0] = m[0] * max[0]; xb[1] = m[1] * max[0]; xb[2] = m[2] * max[0];
|
|
|
|
ya[0] = m[4] * min[1]; ya[1] = m[5] * min[1]; ya[2] = m[6] * min[1];
|
|
yb[0] = m[4] * max[1]; yb[1] = m[5] * max[1]; yb[2] = m[6] * max[1];
|
|
|
|
za[0] = m[8] * min[2]; za[1] = m[9] * min[2]; za[2] = m[10] * min[2];
|
|
zb[0] = m[8] * max[2]; zb[1] = m[9] * max[2]; zb[2] = m[10] * max[2];
|
|
|
|
min = this.min.array;
|
|
max = this.max.array;
|
|
min[0] = Math.min(xa[0], xb[0]) + Math.min(ya[0], yb[0]) + Math.min(za[0], zb[0]) + m[12];
|
|
min[1] = Math.min(xa[1], xb[1]) + Math.min(ya[1], yb[1]) + Math.min(za[1], zb[1]) + m[13];
|
|
min[2] = Math.min(xa[2], xb[2]) + Math.min(ya[2], yb[2]) + Math.min(za[2], zb[2]) + m[14];
|
|
|
|
max[0] = Math.max(xa[0], xb[0]) + Math.max(ya[0], yb[0]) + Math.max(za[0], zb[0]) + m[12];
|
|
max[1] = Math.max(xa[1], xb[1]) + Math.max(ya[1], yb[1]) + Math.max(za[1], zb[1]) + m[13];
|
|
max[2] = Math.max(xa[2], xb[2]) + Math.max(ya[2], yb[2]) + Math.max(za[2], zb[2]) + m[14];
|
|
|
|
this.min._dirty = true;
|
|
this.max._dirty = true;
|
|
|
|
return this;
|
|
};
|
|
})(),
|
|
|
|
/**
|
|
* Apply a projection matrix to the bounding box
|
|
* @param {clay.Matrix4} matrix
|
|
*/
|
|
applyProjection: function (matrix) {
|
|
var min = this.min.array;
|
|
var max = this.max.array;
|
|
|
|
var m = matrix.array;
|
|
// min in min z
|
|
var v10 = min[0];
|
|
var v11 = min[1];
|
|
var v12 = min[2];
|
|
// max in min z
|
|
var v20 = max[0];
|
|
var v21 = max[1];
|
|
var v22 = min[2];
|
|
// max in max z
|
|
var v30 = max[0];
|
|
var v31 = max[1];
|
|
var v32 = max[2];
|
|
|
|
if (m[15] === 1) { // Orthographic projection
|
|
min[0] = m[0] * v10 + m[12];
|
|
min[1] = m[5] * v11 + m[13];
|
|
max[2] = m[10] * v12 + m[14];
|
|
|
|
max[0] = m[0] * v30 + m[12];
|
|
max[1] = m[5] * v31 + m[13];
|
|
min[2] = m[10] * v32 + m[14];
|
|
}
|
|
else {
|
|
var w = -1 / v12;
|
|
min[0] = m[0] * v10 * w;
|
|
min[1] = m[5] * v11 * w;
|
|
max[2] = (m[10] * v12 + m[14]) * w;
|
|
|
|
w = -1 / v22;
|
|
max[0] = m[0] * v20 * w;
|
|
max[1] = m[5] * v21 * w;
|
|
|
|
w = -1 / v32;
|
|
min[2] = (m[10] * v32 + m[14]) * w;
|
|
}
|
|
this.min._dirty = true;
|
|
this.max._dirty = true;
|
|
|
|
return this;
|
|
},
|
|
|
|
updateVertices: function () {
|
|
var vertices = this.vertices;
|
|
if (!vertices) {
|
|
// Cube vertices
|
|
vertices = [];
|
|
for (var i = 0; i < 8; i++) {
|
|
vertices[i] = vec3.fromValues(0, 0, 0);
|
|
}
|
|
|
|
/**
|
|
* Eight coords of bounding box
|
|
* @type {Float32Array[]}
|
|
*/
|
|
this.vertices = vertices;
|
|
}
|
|
var min = this.min.array;
|
|
var max = this.max.array;
|
|
//--- min z
|
|
// min x
|
|
vec3Set(vertices[0], min[0], min[1], min[2]);
|
|
vec3Set(vertices[1], min[0], max[1], min[2]);
|
|
// max x
|
|
vec3Set(vertices[2], max[0], min[1], min[2]);
|
|
vec3Set(vertices[3], max[0], max[1], min[2]);
|
|
|
|
//-- max z
|
|
vec3Set(vertices[4], min[0], min[1], max[2]);
|
|
vec3Set(vertices[5], min[0], max[1], max[2]);
|
|
vec3Set(vertices[6], max[0], min[1], max[2]);
|
|
vec3Set(vertices[7], max[0], max[1], max[2]);
|
|
|
|
return this;
|
|
},
|
|
/**
|
|
* Copy values from another bounding box
|
|
* @param {clay.BoundingBox} bbox
|
|
*/
|
|
copy: function (bbox) {
|
|
var min = this.min;
|
|
var max = this.max;
|
|
vec3Copy(min.array, bbox.min.array);
|
|
vec3Copy(max.array, bbox.max.array);
|
|
min._dirty = true;
|
|
max._dirty = true;
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Clone a new bounding box
|
|
* @return {clay.BoundingBox}
|
|
*/
|
|
clone: function () {
|
|
var boundingBox = new BoundingBox();
|
|
boundingBox.copy(this);
|
|
return boundingBox;
|
|
}
|
|
};
|
|
|
|
export default BoundingBox;
|