/* Copyright (c) 2013 Jesper Öqvist <jesper@llbit.se>
*
* This file is part of Chunky.
*
* Chunky is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chunky is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with Chunky. If not, see <http://www.gnu.org/licenses/>.
*/
package se.llbit.math;
/**
* Axis Aligned Bounding Box for collision detection and Bounding Volume Hierarchy
* construction.
*
* @author Jesper Öqvist <jesper@llbit.se>
*/
public class AABB {
public double xmin;
public double xmax;
public double ymin;
public double ymax;
public double zmin;
public double zmax;
public AABB(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) {
this.xmin = xmin;
this.xmax = xmax;
this.ymin = ymin;
this.ymax = ymax;
this.zmin = zmin;
this.zmax = zmax;
}
/**
* Find intersection between the given ray and this AABB.
*
* @return <code>true</code> if the ray intersects this AABB
*/
public boolean intersect(Ray ray) {
double ix = ray.o.x - QuickMath.floor(ray.o.x + ray.d.x * Ray.OFFSET);
double iy = ray.o.y - QuickMath.floor(ray.o.y + ray.d.y * Ray.OFFSET);
double iz = ray.o.z - QuickMath.floor(ray.o.z + ray.d.z * Ray.OFFSET);
double t;
double u, v;
boolean hit = false;
ray.tNext = ray.t;
t = (xmin - ix) / ray.d.x;
if (t < ray.tNext && t > -Ray.EPSILON) {
u = iz + ray.d.z * t;
v = iy + ray.d.y * t;
if (u >= zmin && u <= zmax &&
v >= ymin && v <= ymax) {
hit = true;
ray.tNext = t;
ray.u = u;
ray.v = v;
ray.n.set(-1, 0, 0);
}
}
t = (xmax - ix) / ray.d.x;
if (t < ray.tNext && t > -Ray.EPSILON) {
u = iz + ray.d.z * t;
v = iy + ray.d.y * t;
if (u >= zmin && u <= zmax &&
v >= ymin && v <= ymax) {
hit = true;
ray.tNext = t;
ray.u = 1 - u;
ray.v = v;
ray.n.set(1, 0, 0);
}
}
t = (ymin - iy) / ray.d.y;
if (t < ray.tNext && t > -Ray.EPSILON) {
u = ix + ray.d.x * t;
v = iz + ray.d.z * t;
if (u >= xmin && u <= xmax &&
v >= zmin && v <= zmax) {
hit = true;
ray.tNext = t;
ray.u = u;
ray.v = v;
ray.n.set(0, -1, 0);
}
}
t = (ymax - iy) / ray.d.y;
if (t < ray.tNext && t > -Ray.EPSILON) {
u = ix + ray.d.x * t;
v = iz + ray.d.z * t;
if (u >= xmin && u <= xmax &&
v >= zmin && v <= zmax) {
hit = true;
ray.tNext = t;
ray.u = u;
ray.v = v;
ray.n.set(0, 1, 0);
}
}
t = (zmin - iz) / ray.d.z;
if (t < ray.tNext && t > -Ray.EPSILON) {
u = ix + ray.d.x * t;
v = iy + ray.d.y * t;
if (u >= xmin && u <= xmax &&
v >= ymin && v <= ymax) {
hit = true;
ray.tNext = t;
ray.u = 1 - u;
ray.v = v;
ray.n.set(0, 0, -1);
}
}
t = (zmax - iz) / ray.d.z;
if (t < ray.tNext && t > -Ray.EPSILON) {
u = ix + ray.d.x * t;
v = iy + ray.d.y * t;
if (u >= xmin && u <= xmax &&
v >= ymin && v <= ymax) {
hit = true;
ray.tNext = t;
ray.u = u;
ray.v = v;
ray.n.set(0, 0, 1);
}
}
return hit;
}
/**
* Only test for intersection and find distance to intersection.
*
* @return {@code true} if there is an intersection
*/
public boolean quickIntersect(Ray ray) {
double t1, t2;
double tNear = Double.NEGATIVE_INFINITY;
double tFar = Double.POSITIVE_INFINITY;
Vector3 d = ray.d;
Vector3 o = ray.o;
if (d.x != 0) {
double rx = 1 / d.x;
t1 = (xmin - o.x) * rx;
t2 = (xmax - o.x) * rx;
if (t1 > t2) {
double t = t1;
t1 = t2;
t2 = t;
}
tNear = t1;
tFar = t2;
}
if (d.y != 0) {
double ry = 1 / d.y;
t1 = (ymin - o.y) * ry;
t2 = (ymax - o.y) * ry;
if (t1 > t2) {
double t = t1;
t1 = t2;
t2 = t;
}
if (t1 > tNear) {
tNear = t1;
}
if (t2 < tFar) {
tFar = t2;
}
}
if (d.z != 0) {
double rz = 1 / d.z;
t1 = (zmin - o.z) * rz;
t2 = (zmax - o.z) * rz;
if (t1 > t2) {
double t = t1;
t1 = t2;
t2 = t;
}
if (t1 > tNear) {
tNear = t1;
}
if (t2 < tFar) {
tFar = t2;
}
}
if (tNear < tFar + Ray.EPSILON && tNear >= 0 && tNear < ray.t) {
ray.tNext = tNear;
return true;
} else {
return false;
}
}
/**
* Test if point is inside the bounding box.
*
* @return true if p is inside this BB.
*/
public boolean inside(Vector3 p) {
return (p.x >= xmin && p.x <= xmax) &&
(p.y >= ymin && p.y <= ymax) &&
(p.z >= zmin && p.z <= zmax);
}
/**
* Test if a ray intersects this AABB.
*
* @return {@code true} if there is an intersection
*/
public boolean hitTest(Ray ray) {
double t1, t2;
double tNear = Double.NEGATIVE_INFINITY;
double tFar = Double.POSITIVE_INFINITY;
Vector3 d = ray.d;
Vector3 o = ray.o;
if (d.x != 0) {
double rx = 1 / d.x;
t1 = (xmin - o.x) * rx;
t2 = (xmax - o.x) * rx;
if (t1 > t2) {
double t = t1;
t1 = t2;
t2 = t;
}
tNear = t1;
tFar = t2;
}
if (d.y != 0) {
double ry = 1 / d.y;
t1 = (ymin - o.y) * ry;
t2 = (ymax - o.y) * ry;
if (t1 > t2) {
double t = t1;
t1 = t2;
t2 = t;
}
if (t1 > tNear) {
tNear = t1;
}
if (t2 < tFar) {
tFar = t2;
}
}
if (d.z != 0) {
double rz = 1 / d.z;
t1 = (zmin - o.z) * rz;
t2 = (zmax - o.z) * rz;
if (t1 > t2) {
double t = t1;
t1 = t2;
t2 = t;
}
if (t1 > tNear) {
tNear = t1;
}
if (t2 < tFar) {
tFar = t2;
}
}
return tNear < tFar + Ray.EPSILON && tFar > 0;
}
/**
* @return AABB rotated about the Y axis
*/
public AABB getYRotated() {
return new AABB(1 - zmax, 1 - zmin, ymin, ymax, xmin, xmax);
}
/**
* @param x X translation
* @param y Y translation
* @param z Z translation
* @return A translated copy of this AABB
*/
public AABB getTranslated(double x, double y, double z) {
return new AABB(xmin + x, xmax + x, ymin + y, ymax + y, zmin + z, zmax + z);
}
/**
* @return an AABB which encloses all given vertices
*/
public static AABB bounds(Vector3... c) {
double xmin = Double.POSITIVE_INFINITY, xmax = Double.NEGATIVE_INFINITY,
ymin = Double.POSITIVE_INFINITY, ymax = Double.NEGATIVE_INFINITY,
zmin = Double.POSITIVE_INFINITY, zmax = Double.NEGATIVE_INFINITY;
for (Vector3 v : c) {
if (v.x < xmin) {
xmin = v.x;
}
if (v.x > xmax) {
xmax = v.x;
}
if (v.y < ymin) {
ymin = v.y;
}
if (v.y > ymax) {
ymax = v.y;
}
if (v.z < zmin) {
zmin = v.z;
}
if (v.z > zmax) {
zmax = v.z;
}
}
return new AABB(xmin, xmax, ymin, ymax, zmin, zmax);
}
public AABB expand(AABB bb) {
return new AABB(Math.min(xmin, bb.xmin), Math.max(xmax, bb.xmax), Math.min(ymin, bb.ymin),
Math.max(ymax, bb.ymax), Math.min(zmin, bb.zmin), Math.max(zmax, bb.zmax));
}
}