blob: 62f15eaf388526407753a199698ec74d495a402f [file] [log] [blame]
// Copyright (c) 2015, Google Inc. Please see the AUTHORS file for details.
// All rights reserved. Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
part of vector_math_64;
/// Defines a 3-dimensional oriented bounding box defined with a [center],
/// [halfExtents] and axes.
class Obb3 {
final Vector3 _center;
final Vector3 _halfExtents;
final Vector3 _axis0;
final Vector3 _axis1;
final Vector3 _axis2;
/// The center of the OBB.
Vector3 get center => _center;
/// The half extends of the OBB.
Vector3 get halfExtents => _halfExtents;
/// The first axis of the OBB.
Vector3 get axis0 => _axis0;
/// The second axis of the OBB.
Vector3 get axis1 => _axis1;
/// The third axis of the OBB.
Vector3 get axis2 => _axis2;
/// Create a new OBB with erverything set to zero.
Obb3()
: _center = Vector3.zero(),
_halfExtents = Vector3.zero(),
_axis0 = Vector3(1.0, 0.0, 0.0),
_axis1 = Vector3(0.0, 1.0, 0.0),
_axis2 = Vector3(0.0, 0.0, 1.0);
/// Create a new OBB as a copy of [other].
Obb3.copy(Obb3 other)
: _center = Vector3.copy(other._center),
_halfExtents = Vector3.copy(other._halfExtents),
_axis0 = Vector3.copy(other._axis0),
_axis1 = Vector3.copy(other._axis1),
_axis2 = Vector3.copy(other._axis2);
/// Create a new OBB using [center], [halfExtents] and axis.
Obb3.centerExtentsAxes(Vector3 center, Vector3 halfExtents, Vector3 axis0,
Vector3 axis1, Vector3 axis2)
: _center = Vector3.copy(center),
_halfExtents = Vector3.copy(halfExtents),
_axis0 = Vector3.copy(axis0),
_axis1 = Vector3.copy(axis1),
_axis2 = Vector3.copy(axis2);
/// Copy from [other] into this.
void copyFrom(Obb3 other) {
_center.setFrom(other._center);
_halfExtents.setFrom(other._halfExtents);
_axis0.setFrom(other._axis0);
_axis1.setFrom(other._axis1);
_axis2.setFrom(other._axis2);
}
/// Copy from this into [other].
void copyInto(Obb3 other) {
other._center.setFrom(_center);
other._halfExtents.setFrom(_halfExtents);
other._axis0.setFrom(_axis0);
other._axis1.setFrom(_axis1);
other._axis2.setFrom(_axis2);
}
/// Reset the rotation of this.
void resetRotation() {
_axis0.setValues(1.0, 0.0, 0.0);
_axis1.setValues(0.0, 1.0, 0.0);
_axis2.setValues(0.0, 0.0, 1.0);
}
/// Translate this by [offset].
void translate(Vector3 offset) {
_center.add(offset);
}
/// Rotate this by the rotation matrix [t].
void rotate(Matrix3 t) {
t
..transform(_axis0..scale(_halfExtents.x))
..transform(_axis1..scale(_halfExtents.y))
..transform(_axis2..scale(_halfExtents.z));
_halfExtents
..x = _axis0.normalize()
..y = _axis1.normalize()
..z = _axis2.normalize();
}
/// Transform this by the transform [t].
void transform(Matrix4 t) {
t
..transform3(_center)
..rotate3(_axis0..scale(_halfExtents.x))
..rotate3(_axis1..scale(_halfExtents.y))
..rotate3(_axis2..scale(_halfExtents.z));
_halfExtents
..x = _axis0.normalize()
..y = _axis1.normalize()
..z = _axis2.normalize();
}
/// Store the corner with [cornerIndex] in [corner].
void copyCorner(int cornerIndex, Vector3 corner) {
assert(cornerIndex >= 0 || cornerIndex < 8);
corner.setFrom(_center);
switch (cornerIndex) {
case 0:
corner
..addScaled(_axis0, -_halfExtents.x)
..addScaled(_axis1, -_halfExtents.y)
..addScaled(_axis2, -_halfExtents.z);
break;
case 1:
corner
..addScaled(_axis0, -_halfExtents.x)
..addScaled(_axis1, -_halfExtents.y)
..addScaled(_axis2, _halfExtents.z);
break;
case 2:
corner
..addScaled(_axis0, -_halfExtents.x)
..addScaled(_axis1, _halfExtents.y)
..addScaled(_axis2, -_halfExtents.z);
break;
case 3:
corner
..addScaled(_axis0, -_halfExtents.x)
..addScaled(_axis1, _halfExtents.y)
..addScaled(_axis2, _halfExtents.z);
break;
case 4:
corner
..addScaled(_axis0, _halfExtents.x)
..addScaled(_axis1, -_halfExtents.y)
..addScaled(_axis2, -_halfExtents.z);
break;
case 5:
corner
..addScaled(_axis0, _halfExtents.x)
..addScaled(_axis1, -_halfExtents.y)
..addScaled(_axis2, _halfExtents.z);
break;
case 6:
corner
..addScaled(_axis0, _halfExtents.x)
..addScaled(_axis1, _halfExtents.y)
..addScaled(_axis2, -_halfExtents.z);
break;
case 7:
corner
..addScaled(_axis0, _halfExtents.x)
..addScaled(_axis1, _halfExtents.y)
..addScaled(_axis2, _halfExtents.z);
break;
}
}
/// Find the closest point [q] on the OBB to the point [p] and store it in [q].
void closestPointTo(Vector3 p, Vector3 q) {
final d = p - _center;
q.setFrom(_center);
var dist = d.dot(_axis0);
dist = dist.clamp(-_halfExtents.x, _halfExtents.x).toDouble();
q.addScaled(_axis0, dist);
dist = d.dot(_axis1);
dist = dist.clamp(-_halfExtents.y, _halfExtents.y).toDouble();
q.addScaled(_axis1, dist);
dist = d.dot(_axis2);
dist = dist.clamp(-_halfExtents.z, _halfExtents.z).toDouble();
q.addScaled(_axis2, dist);
}
// Avoid allocating these instance on every call to intersectsWithObb3
static final _r = Matrix3.zero();
static final _absR = Matrix3.zero();
static final _t = Vector3.zero();
/// Check for intersection between this and [other].
bool intersectsWithObb3(Obb3 other, [double epsilon = 1e-3]) {
// Compute rotation matrix expressing other in this's coordinate frame
_r
..setEntry(0, 0, _axis0.dot(other._axis0))
..setEntry(1, 0, _axis1.dot(other._axis0))
..setEntry(2, 0, _axis2.dot(other._axis0))
..setEntry(0, 1, _axis0.dot(other._axis1))
..setEntry(1, 1, _axis1.dot(other._axis1))
..setEntry(2, 1, _axis2.dot(other._axis1))
..setEntry(0, 2, _axis0.dot(other._axis2))
..setEntry(1, 2, _axis1.dot(other._axis2))
..setEntry(2, 2, _axis2.dot(other._axis2));
// Compute translation vector t
_t
..setFrom(other._center)
..sub(_center);
// Bring translation into this's coordinate frame
_t.setValues(_t.dot(_axis0), _t.dot(_axis1), _t.dot(_axis2));
// 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 (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
_absR.setEntry(i, j, _r.entry(i, j).abs() + epsilon);
}
}
double ra;
double rb;
// Test axes L = A0, L = A1, L = A2
for (var i = 0; i < 3; i++) {
ra = _halfExtents[i];
rb = other._halfExtents[0] * _absR.entry(i, 0) +
other._halfExtents[1] * _absR.entry(i, 1) +
other._halfExtents[2] * _absR.entry(i, 2);
if (_t[i].abs() > ra + rb) {
return false;
}
}
// Test axes L = B0, L = B1, L = B2
for (var i = 0; i < 3; i++) {
ra = _halfExtents[0] * _absR.entry(0, i) +
_halfExtents[1] * _absR.entry(1, i) +
_halfExtents[2] * _absR.entry(2, i);
rb = other._halfExtents[i];
if ((_t[0] * _r.entry(0, i) +
_t[1] * _r.entry(1, i) +
_t[2] * _r.entry(2, i))
.abs() >
ra + rb) {
return false;
}
}
// Test axis L = A0 x B0
ra = _halfExtents[1] * _absR.entry(2, 0) +
_halfExtents[2] * _absR.entry(1, 0);
rb = other._halfExtents[1] * _absR.entry(0, 2) +
other._halfExtents[2] * _absR.entry(0, 1);
if ((_t[2] * _r.entry(1, 0) - _t[1] * _r.entry(2, 0)).abs() > ra + rb) {
return false;
}
// Test axis L = A0 x B1
ra = _halfExtents[1] * _absR.entry(2, 1) +
_halfExtents[2] * _absR.entry(1, 1);
rb = other._halfExtents[0] * _absR.entry(0, 2) +
other._halfExtents[2] * _absR.entry(0, 0);
if ((_t[2] * _r.entry(1, 1) - _t[1] * _r.entry(2, 1)).abs() > ra + rb) {
return false;
}
// Test axis L = A0 x B2
ra = _halfExtents[1] * _absR.entry(2, 2) +
_halfExtents[2] * _absR.entry(1, 2);
rb = other._halfExtents[0] * _absR.entry(0, 1) +
other._halfExtents[1] * _absR.entry(0, 0);
if ((_t[2] * _r.entry(1, 2) - _t[1] * _r.entry(2, 2)).abs() > ra + rb) {
return false;
}
// Test axis L = A1 x B0
ra = _halfExtents[0] * _absR.entry(2, 0) +
_halfExtents[2] * _absR.entry(0, 0);
rb = other._halfExtents[1] * _absR.entry(1, 2) +
other._halfExtents[2] * _absR.entry(1, 1);
if ((_t[0] * _r.entry(2, 0) - _t[2] * _r.entry(0, 0)).abs() > ra + rb) {
return false;
}
// Test axis L = A1 x B1
ra = _halfExtents[0] * _absR.entry(2, 1) +
_halfExtents[2] * _absR.entry(0, 1);
rb = other._halfExtents[0] * _absR.entry(1, 2) +
other._halfExtents[2] * _absR.entry(1, 0);
if ((_t[0] * _r.entry(2, 1) - _t[2] * _r.entry(0, 1)).abs() > ra + rb) {
return false;
}
// Test axis L = A1 x B2
ra = _halfExtents[0] * _absR.entry(2, 2) +
_halfExtents[2] * _absR.entry(0, 2);
rb = other._halfExtents[0] * _absR.entry(1, 1) +
other._halfExtents[1] * _absR.entry(1, 0);
if ((_t[0] * _r.entry(2, 2) - _t[2] * _r.entry(0, 2)).abs() > ra + rb) {
return false;
}
// Test axis L = A2 x B0
ra = _halfExtents[0] * _absR.entry(1, 0) +
_halfExtents[1] * _absR.entry(0, 0);
rb = other._halfExtents[1] * _absR.entry(2, 2) +
other._halfExtents[2] * _absR.entry(2, 1);
if ((_t[1] * _r.entry(0, 0) - _t[0] * _r.entry(1, 0)).abs() > ra + rb) {
return false;
}
// Test axis L = A2 x B1
ra = _halfExtents[0] * _absR.entry(1, 1) +
_halfExtents[1] * _absR.entry(0, 1);
rb = other._halfExtents[0] * _absR.entry(2, 2) +
other._halfExtents[2] * _absR.entry(2, 0);
if ((_t[1] * _r.entry(0, 1) - _t[0] * _r.entry(1, 1)).abs() > ra + rb) {
return false;
}
// Test axis L = A2 x B2
ra = _halfExtents[0] * _absR.entry(1, 2) +
_halfExtents[1] * _absR.entry(0, 2);
rb = other._halfExtents[0] * _absR.entry(2, 1) +
other._halfExtents[1] * _absR.entry(2, 0);
if ((_t[1] * _r.entry(0, 2) - _t[0] * _r.entry(1, 2)).abs() > ra + rb) {
return false;
}
// Since no separating axis is found, the OBBs must be intersecting
return true;
}
// Avoid allocating these instance on every call to intersectsWithTriangle
static final _triangle = Triangle();
static final _aabb3 = Aabb3();
static final _zeroVector = Vector3.zero();
/// Return if this intersects with [other]
bool intersectsWithTriangle(Triangle other, {IntersectionResult? result}) {
_triangle.copyFrom(other);
_triangle.point0
..sub(_center)
..setValues(_triangle.point0.dot(axis0), _triangle.point0.dot(axis1),
_triangle.point0.dot(axis2));
_triangle.point1
..sub(_center)
..setValues(_triangle.point1.dot(axis0), _triangle.point1.dot(axis1),
_triangle.point1.dot(axis2));
_triangle.point2
..sub(_center)
..setValues(_triangle.point2.dot(axis0), _triangle.point2.dot(axis1),
_triangle.point2.dot(axis2));
_aabb3.setCenterAndHalfExtents(_zeroVector, _halfExtents);
return _aabb3.intersectsWithTriangle(_triangle, result: result);
}
// Avoid allocating these instance on every call to intersectsWithVector3
static final _vector = Vector3.zero();
/// Return if this intersects with [other]
bool intersectsWithVector3(Vector3 other) {
_vector
..setFrom(other)
..sub(_center)
..setValues(_vector.dot(axis0), _vector.dot(axis1), _vector.dot(axis2));
_aabb3.setCenterAndHalfExtents(_zeroVector, _halfExtents);
return _aabb3.intersectsWithVector3(_vector);
}
// Avoid allocating these instance on every call to intersectsWithTriangle
static final _quadTriangle0 = Triangle();
static final _quadTriangle1 = Triangle();
/// Return if this intersects with [other]
bool intersectsWithQuad(Quad other, {IntersectionResult? result}) {
other.copyTriangles(_quadTriangle0, _quadTriangle1);
return intersectsWithTriangle(_quadTriangle0, result: result) ||
intersectsWithTriangle(_quadTriangle1, result: result);
}
}