/* Copyright (c) 2014 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.primitive;
import se.llbit.math.AABB;
import se.llbit.math.Ray;
import se.llbit.math.Vector3;
/**
* Axis-Aligned Bounding Box. Does not compute intersection normals.
*
* @author Jesper Öqvist <jesper@llbit.se>
*/
public class MutableAABB implements Primitive {
protected double xmin;
protected double xmax;
protected double ymin;
protected double ymax;
protected double zmin;
protected double zmax;
/**
* Construct a new AABB with given bounds.
*/
public MutableAABB(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;
}
/**
* 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;
}
@Override public boolean intersect(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;
}
}
/**
* Expand this AABB to enclose the given AABB.
*/
public void expand(AABB p) {
if (p.xmin < xmin) {
xmin = p.xmin;
}
if (p.xmax > xmax) {
xmax = p.xmax;
}
if (p.ymin < ymin) {
ymin = p.ymin;
}
if (p.ymax > ymax) {
ymax = p.ymax;
}
if (p.zmin < zmin) {
zmin = p.zmin;
}
if (p.zmax > zmax) {
zmax = p.zmax;
}
}
@Override public AABB bounds() {
return new AABB(xmin, xmax, ymin, ymax, zmin, zmax);
}
@Override public String toString() {
return String
.format("[ %.2f, %.2f, %.2f, %.2f, %.2f, %.2f]", xmin, xmax, ymin, ymax, zmin, zmax);
}
/**
* 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);
}
/**
* @return surface area of the bounding box
*/
public double surfaceArea() {
double x = xmax - xmin;
double y = ymax - ymin;
double z = zmax - zmin;
return 2 * (y * z + x * z * x * y);
}
}