Object.assign(pc, function () {
var tmpVecA = new pc.Vec3();
var tmpVecB = new pc.Vec3();
var tmpVecC = new pc.Vec3();
var tmpVecD = new pc.Vec3();
var tmpVecE = new pc.Vec3();
/**
* @constructor
* @name pc.BoundingBox
* @description Create a new axis-aligned bounding box.
* @classdesc Axis-Aligned Bounding Box.
* @param {pc.Vec3} [center] Center of box. The constructor takes a reference of this parameter.
* @param {pc.Vec3} [halfExtents] Half the distance across the box in each axis. The constructor takes a reference of this parameter.
*/
var BoundingBox = function BoundingBox(center, halfExtents) {
this.center = center || new pc.Vec3(0, 0, 0);
this.halfExtents = halfExtents || new pc.Vec3(0.5, 0.5, 0.5);
this._min = new pc.Vec3();
this._max = new pc.Vec3();
};
Object.assign(BoundingBox.prototype, {
/**
* @function
* @name pc.BoundingBox#add
* @description Combines two bounding boxes into one, enclosing both.
* @param {pc.BoundingBox} other Bounding box to add.
*/
add: function (other) {
var tc = this.center;
var tcx = tc.x;
var tcy = tc.y;
var tcz = tc.z;
var th = this.halfExtents;
var thx = th.x;
var thy = th.y;
var thz = th.z;
var tminx = tcx - thx;
var tmaxx = tcx + thx;
var tminy = tcy - thy;
var tmaxy = tcy + thy;
var tminz = tcz - thz;
var tmaxz = tcz + thz;
var oc = other.center;
var ocx = oc.x;
var ocy = oc.y;
var ocz = oc.z;
var oh = other.halfExtents;
var ohx = oh.x;
var ohy = oh.y;
var ohz = oh.z;
var ominx = ocx - ohx;
var omaxx = ocx + ohx;
var ominy = ocy - ohy;
var omaxy = ocy + ohy;
var ominz = ocz - ohz;
var omaxz = ocz + ohz;
if (ominx < tminx) tminx = ominx;
if (omaxx > tmaxx) tmaxx = omaxx;
if (ominy < tminy) tminy = ominy;
if (omaxy > tmaxy) tmaxy = omaxy;
if (ominz < tminz) tminz = ominz;
if (omaxz > tmaxz) tmaxz = omaxz;
tc.x = (tminx + tmaxx) * 0.5;
tc.y = (tminy + tmaxy) * 0.5;
tc.z = (tminz + tmaxz) * 0.5;
th.x = (tmaxx - tminx) * 0.5;
th.y = (tmaxy - tminy) * 0.5;
th.z = (tmaxz - tminz) * 0.5;
},
copy: function (src) {
this.center.copy(src.center);
this.halfExtents.copy(src.halfExtents);
this.type = src.type;
},
clone: function () {
return new pc.BoundingBox(this.center.clone(), this.halfExtents.clone());
},
/**
* @function
* @name pc.BoundingBox#intersects
* @description Test whether two axis-aligned bounding boxes intersect.
* @param {pc.BoundingBox} other Bounding box to test against.
* @returns {Boolean} True if there is an intersection.
*/
intersects: function (other) {
var aMax = this.getMax();
var aMin = this.getMin();
var bMax = other.getMax();
var bMin = other.getMin();
return (aMin.x <= bMax.x) && (aMax.x >= bMin.x) &&
(aMin.y <= bMax.y) && (aMax.y >= bMin.y) &&
(aMin.z <= bMax.z) && (aMax.z >= bMin.z);
},
_intersectsRay: function (ray, point) {
var tMin = tmpVecA.copy(this.getMin()).sub(ray.origin);
var tMax = tmpVecB.copy(this.getMax()).sub(ray.origin);
var dir = ray.direction;
// Ensure that we are not dividing it by zero
if (dir.x === 0) {
tMin.x = tMin.x < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
tMax.x = tMax.x < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
} else {
tMin.x /= dir.x;
tMax.x /= dir.x;
}
if (dir.y === 0) {
tMin.y = tMin.y < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
tMax.y = tMax.y < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
} else {
tMin.y /= dir.y;
tMax.y /= dir.y;
}
if (dir.z === 0) {
tMin.z = tMin.z < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
tMax.z = tMax.z < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
} else {
tMin.z /= dir.z;
tMax.z /= dir.z;
}
var realMin = tmpVecC.set(Math.min(tMin.x, tMax.x), Math.min(tMin.y, tMax.y), Math.min(tMin.z, tMax.z));
var realMax = tmpVecD.set(Math.max(tMin.x, tMax.x), Math.max(tMin.y, tMax.y), Math.max(tMin.z, tMax.z));
var minMax = Math.min(Math.min(realMax.x, realMax.y), realMax.z);
var maxMin = Math.max(Math.max(realMin.x, realMin.y), realMin.z);
var intersects = minMax >= maxMin && maxMin >= 0;
if (intersects)
point.copy(ray.direction).scale(maxMin).add(ray.origin);
return intersects;
},
_fastIntersectsRay: function (ray) {
var diff = tmpVecA;
var cross = tmpVecB;
var prod = tmpVecC;
var absDiff = tmpVecD;
var absDir = tmpVecE;
var rayDir = ray.direction;
diff.sub2(ray.origin, this.center);
absDiff.set(Math.abs(diff.x), Math.abs(diff.y), Math.abs(diff.z));
prod.mul2(diff, rayDir);
if (absDiff.x > this.halfExtents.x && prod.x >= 0)
return false;
if (absDiff.y > this.halfExtents.y && prod.y >= 0)
return false;
if (absDiff.z > this.halfExtents.z && prod.z >= 0)
return false;
absDir.set(Math.abs(rayDir.x), Math.abs(rayDir.y), Math.abs(rayDir.z));
cross.cross(rayDir, diff);
cross.set(Math.abs(cross.x), Math.abs(cross.y), Math.abs(cross.z));
if (cross.x > this.halfExtents.y * absDir.z + this.halfExtents.z * absDir.y)
return false;
if (cross.y > this.halfExtents.x * absDir.z + this.halfExtents.z * absDir.x)
return false;
if (cross.z > this.halfExtents.x * absDir.y + this.halfExtents.y * absDir.x)
return false;
return true;
},
/**
* @function
* @name pc.BoundingBox#intersectsRay
* @description Test if a ray intersects with the AABB.
* @param {pc.Ray} ray Ray to test against (direction must be normalized).
* @param {pc.Vec3} [point] If there is an intersection, the intersection point will be copied into here.
* @returns {Boolean} True if there is an intersection.
*/
intersectsRay: function (ray, point) {
if (point) {
return this._intersectsRay(ray, point);
}
return this._fastIntersectsRay(ray);
},
setMinMax: function (min, max) {
this.center.add2(max, min).scale(0.5);
this.halfExtents.sub2(max, min).scale(0.5);
},
/**
* @function
* @name pc.BoundingBox#getMin
* @description Return the minimum corner of the AABB.
* @returns {pc.Vec3} minimum corner.
*/
getMin: function () {
return this._min.copy(this.center).sub(this.halfExtents);
},
/**
* @function
* @name pc.BoundingBox#getMax
* @description Return the maximum corner of the AABB.
* @returns {pc.Vec3} maximum corner.
*/
getMax: function () {
return this._max.copy(this.center).add(this.halfExtents);
},
/**
* @function
* @name pc.BoundingBox#containsPoint
* @description Test if a point is inside a AABB.
* @param {pc.Vec3} point Point to test.
* @returns {Boolean} true if the point is inside the AABB and false otherwise.
*/
containsPoint: function (point) {
var min = this.getMin();
var max = this.getMax();
if (point.x < min.x || point.x > max.x ||
point.y < min.y || point.y > max.y ||
point.z < min.z || point.z > max.z) {
return false;
}
return true;
},
/**
* @function
* @name pc.BoundingBox#setFromTransformedAabb
* @description Set an AABB to enclose the specified AABB if it were to be
* transformed by the specified 4x4 matrix.
* @param {pc.BoundingBox} aabb Box to transform and enclose
* @param {pc.Mat4} m Transformation matrix to apply to source AABB.
*/
setFromTransformedAabb: function (aabb, m) {
var bc = this.center;
var br = this.halfExtents;
var ac = aabb.center;
var ar = aabb.halfExtents;
m = m.data;
var mx0 = m[0];
var mx1 = m[4];
var mx2 = m[8];
var my0 = m[1];
var my1 = m[5];
var my2 = m[9];
var mz0 = m[2];
var mz1 = m[6];
var mz2 = m[10];
var mx0a = Math.abs(mx0);
var mx1a = Math.abs(mx1);
var mx2a = Math.abs(mx2);
var my0a = Math.abs(my0);
var my1a = Math.abs(my1);
var my2a = Math.abs(my2);
var mz0a = Math.abs(mz0);
var mz1a = Math.abs(mz1);
var mz2a = Math.abs(mz2);
bc.set(
m[12] + mx0 * ac.x + mx1 * ac.y + mx2 * ac.z,
m[13] + my0 * ac.x + my1 * ac.y + my2 * ac.z,
m[14] + mz0 * ac.x + mz1 * ac.y + mz2 * ac.z
);
br.set(
mx0a * ar.x + mx1a * ar.y + mx2a * ar.z,
my0a * ar.x + my1a * ar.y + my2a * ar.z,
mz0a * ar.x + mz1a * ar.y + mz2a * ar.z
);
},
compute: function (vertices) {
var min = tmpVecA.set(vertices[0], vertices[1], vertices[2]);
var max = tmpVecB.set(vertices[0], vertices[1], vertices[2]);
var numVerts = vertices.length / 3;
for (var i = 1; i < numVerts; i++) {
var x = vertices[i * 3 + 0];
var y = vertices[i * 3 + 1];
var z = vertices[i * 3 + 2];
if (x < min.x) min.x = x;
if (y < min.y) min.y = y;
if (z < min.z) min.z = z;
if (x > max.x) max.x = x;
if (y > max.y) max.y = y;
if (z > max.z) max.z = z;
}
this.setMinMax(min, max);
},
/**
* @function
* @name pc.BoundingBox#intersectsBoundingSphere
* @description Test if a Bounding Sphere is overlapping, enveloping, or inside this AABB.
* @param {pc.BoundingSphere} sphere Bounding Sphere to test.
* @returns {Boolean} true if the Bounding Sphere is overlapping, enveloping, or inside the AABB and false otherwise.
*/
intersectsBoundingSphere: function (sphere) {
var sq = this._distanceToBoundingSphereSq(sphere);
if (sq <= sphere.radius * sphere.radius) {
return true;
}
return false;
},
_distanceToBoundingSphereSq: function (sphere) {
var boxMin = this.getMin();
var boxMax = this.getMax();
var sq = 0;
var axis = ['x', 'y', 'z'];
for (var i = 0; i < 3; ++i) {
var out = 0;
var pn = sphere.center[axis[i]];
var bMin = boxMin[axis[i]];
var bMax = boxMax[axis[i]];
var val = 0;
if (pn < bMin) {
val = (bMin - pn);
out += val * val;
}
if (pn > bMax) {
val = (pn - bMax);
out += val * val;
}
sq += out;
}
return sq;
}
});
return {
BoundingBox: BoundingBox
};
}());