org.joml.RayAabIntersection Maven / Gradle / Ivy
/*
* The MIT License
*
* Copyright (c) 2015-2020 Kai Burjack
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.joml;
/**
* This is an implementation of the Fast Ray/Axis-Aligned Bounding Box
* Overlap Tests using Ray Slopes paper.
*
* It is an efficient implementation when testing many axis-aligned boxes against the same ray.
*
* This class is thread-safe and can be used in a multithreaded environment when testing many axis-aligned boxes against the same ray concurrently.
*
* @author Kai Burjack
*/
public class RayAabIntersection {
private float originX, originY, originZ;
private float dirX, dirY, dirZ;
/* Needed for ray slope intersection method */
private float c_xy, c_yx, c_zy, c_yz, c_xz, c_zx;
private float s_xy, s_yx, s_zy, s_yz, s_xz, s_zx;
private byte classification;
/**
* Create a new {@link RayAabIntersection} without initializing a ray.
*
* Before using the {@link #test(float, float, float, float, float, float) intersect()} method,
* the method {@link #set(float, float, float, float, float, float) set()} must be called in order to
* initialize the created RayAabIntersection instance with a ray.
*
* @see #set(float, float, float, float, float, float)
*/
public RayAabIntersection() {
}
/**
* Create a new {@link RayAabIntersection} and initialize it with a ray with origin (originX, originY, originZ)
* and direction (dirX, dirY, dirZ)
.
*
* In order to change the direction and/or origin of the ray later, use {@link #set(float, float, float, float, float, float) set()}.
*
* @see #set(float, float, float, float, float, float)
*
* @param originX
* the x coordinate of the origin
* @param originY
* the y coordinate of the origin
* @param originZ
* the z coordinate of the origin
* @param dirX
* the x coordinate of the direction
* @param dirY
* the y coordinate of the direction
* @param dirZ
* the z coordinate of the direction
*/
public RayAabIntersection(float originX, float originY, float originZ, float dirX, float dirY, float dirZ) {
set(originX, originY, originZ, dirX, dirY, dirZ);
}
/**
* Update the ray stored by this {@link RayAabIntersection} with the new origin (originX, originY, originZ)
* and direction (dirX, dirY, dirZ)
.
*
* @param originX
* the x coordinate of the ray origin
* @param originY
* the y coordinate of the ray origin
* @param originZ
* the z coordinate of the ray origin
* @param dirX
* the x coordinate of the ray direction
* @param dirY
* the y coordinate of the ray direction
* @param dirZ
* the z coordinate of the ray direction
*/
public void set(float originX, float originY, float originZ, float dirX, float dirY, float dirZ) {
this.originX = originX;
this.originY = originY;
this.originZ = originZ;
this.dirX = dirX;
this.dirY = dirY;
this.dirZ = dirZ;
precomputeSlope();
}
private static int signum(float f) {
return (f == 0.0f || Float.isNaN(f)) ? 0 : ((1 - Float.floatToIntBits(f) >>> 31) << 1) - 1;
}
/**
* Precompute the values necessary for the ray slope algorithm.
*/
private void precomputeSlope() {
float invDirX = 1.0f / dirX;
float invDirY = 1.0f / dirY;
float invDirZ = 1.0f / dirZ;
s_yx = dirX * invDirY;
s_xy = dirY * invDirX;
s_zy = dirY * invDirZ;
s_yz = dirZ * invDirY;
s_xz = dirZ * invDirX;
s_zx = dirX * invDirZ;
c_xy = originY - s_xy * originX;
c_yx = originX - s_yx * originY;
c_zy = originY - s_zy * originZ;
c_yz = originZ - s_yz * originY;
c_xz = originZ - s_xz * originX; // <- original paper had a bug here. It switched originZ/originX
c_zx = originX - s_zx * originZ; // <- original paper had a bug here. It switched originZ/originX
int sgnX = signum(dirX);
int sgnY = signum(dirY);
int sgnZ = signum(dirZ);
classification = (byte) ((sgnZ+1) << 4 | (sgnY+1) << 2 | (sgnX+1));
}
/**
* Test whether the ray stored in this {@link RayAabIntersection} intersect the axis-aligned box
* given via its minimum corner (minX, minY, minZ)
and its maximum corner (maxX, maxY, maxZ)
.
*
* This implementation uses a tableswitch to dispatch to the correct intersection method.
*
* This method is thread-safe and can be used to test many axis-aligned boxes concurrently.
*
* @param minX
* the x coordinate of the minimum corner
* @param minY
* the y coordinate of the minimum corner
* @param minZ
* the z coordinate of the minimum corner
* @param maxX
* the x coordinate of the maximum corner
* @param maxY
* the y coordinate of the maximum corner
* @param maxZ
* the z coordinate of the maximum corner
* @return true
iff the ray intersects the given axis-aligned box; false
otherwise
*/
public boolean test(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
// tableswitch with dense and consecutive cases (will be a simple jump based on the switch argument)
switch (classification) {
case 0: // 0b000000: // MMM
return MMM(minX, minY, minZ, maxX, maxY, maxZ);
case 1: // 0b000001: // OMM
return OMM(minX, minY, minZ, maxX, maxY, maxZ);
case 2: // 0b000010: // PMM
return PMM(minX, minY, minZ, maxX, maxY, maxZ);
case 3: // 0b000011: // not used
return false;
case 4: // 0b000100: // MOM
return MOM(minX, minY, minZ, maxX, maxY, maxZ);
case 5: // 0b000101: // OOM
return OOM(minX, minY, minZ, maxX, maxY);
case 6: // 0b000110: // POM
return POM(minX, minY, minZ, maxX, maxY, maxZ);
case 7: // 0b000111: // not used
return false;
case 8: // 0b001000: // MPM
return MPM(minX, minY, minZ, maxX, maxY, maxZ);
case 9: // 0b001001: // OPM
return OPM(minX, minY, minZ, maxX, maxY, maxZ);
case 10: // 0b001010: // PPM
return PPM(minX, minY, minZ, maxX, maxY, maxZ);
case 11: // 0b001011: // not used
case 12: // 0b001100: // not used
case 13: // 0b001101: // not used
case 14: // 0b001110: // not used
case 15: // 0b001111: // not used
return false;
case 16: // 0b010000: // MMO
return MMO(minX, minY, minZ, maxX, maxY, maxZ);
case 17: // 0b010001: // OMO
return OMO(minX, minY, minZ, maxX, maxZ);
case 18: // 0b010010: // PMO
return PMO(minX, minY, minZ, maxX, maxY, maxZ);
case 19: // 0b010011: // not used
return false;
case 20: // 0b010100: // MOO
return MOO(minX, minY, minZ, maxY, maxZ);
case 21: // 0b010101: // OOO
return false; // <- degenerate case
case 22: // 0b010110: // POO
return POO(minY, minZ, maxX, maxY, maxZ);
case 23: // 0b010111: // not used
return false;
case 24: // 0b011000: // MPO
return MPO(minX, minY, minZ, maxX, maxY, maxZ);
case 25: // 0b011001: // OPO
return OPO(minX, minZ, maxX, maxY, maxZ);
case 26: // 0b011010: // PPO
return PPO(minX, minY, minZ, maxX, maxY, maxZ);
case 27: // 0b011011: // not used
case 28: // 0b011100: // not used
case 29: // 0b011101: // not used
case 30: // 0b011110: // not used
case 31: // 0b011111: // not used
return false;
case 32: // 0b100000: // MMP
return MMP(minX, minY, minZ, maxX, maxY, maxZ);
case 33: // 0b100001: // OMP
return OMP(minX, minY, minZ, maxX, maxY, maxZ);
case 34: // 0b100010: // PMP
return PMP(minX, minY, minZ, maxX, maxY, maxZ);
case 35: // 0b100011: // not used
return false;
case 36: // 0b100100: // MOP
return MOP(minX, minY, minZ, maxX, maxY, maxZ);
case 37: // 0b100101: // OOP
return OOP(minX, minY, maxX, maxY, maxZ);
case 38: // 0b100110: // POP
return POP(minX, minY, minZ, maxX, maxY, maxZ);
case 39: // 0b100111: // not used
return false;
case 40: // 0b101000: // MPP
return MPP(minX, minY, minZ, maxX, maxY, maxZ);
case 41: // 0b101001: // OPP
return OPP(minX, minY, minZ, maxX, maxY, maxZ);
case 42: // 0b101010: // PPP
return PPP(minX, minY, minZ, maxX, maxY, maxZ);
default:
return false;
}
}
/* Intersection tests for all possible ray direction cases */
private boolean MMM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX >= minX && originY >= minY && originZ >= minZ
&& s_xy * minX - maxY + c_xy <= 0.0f
&& s_yx * minY - maxX + c_yx <= 0.0f
&& s_zy * minZ - maxY + c_zy <= 0.0f
&& s_yz * minY - maxZ + c_yz <= 0.0f
&& s_xz * minX - maxZ + c_xz <= 0.0f
&& s_zx * minZ - maxX + c_zx <= 0.0f;
}
private boolean OMM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX >= minX && originX <= maxX && originY >= minY && originZ >= minZ
&& s_zy * minZ - maxY + c_zy <= 0.0f
&& s_yz * minY - maxZ + c_yz <= 0.0f;
}
private boolean PMM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX <= maxX && originY >= minY && originZ >= minZ
&& s_xy * maxX - maxY + c_xy <= 0.0f
&& s_yx * minY - minX + c_yx >= 0.0f
&& s_zy * minZ - maxY + c_zy <= 0.0f
&& s_yz * minY - maxZ + c_yz <= 0.0f
&& s_xz * maxX - maxZ + c_xz <= 0.0f
&& s_zx * minZ - minX + c_zx >= 0.0f;
}
private boolean MOM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originY >= minY && originY <= maxY && originX >= minX && originZ >= minZ
&& s_xz * minX - maxZ + c_xz <= 0.0f
&& s_zx * minZ - maxX + c_zx <= 0.0f;
}
private boolean OOM(float minX, float minY, float minZ, float maxX, float maxY) {
return originZ >= minZ && originX >= minX && originX <= maxX && originY >= minY && originY <= maxY;
}
private boolean POM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originY >= minY && originY <= maxY && originX <= maxX && originZ >= minZ
&& s_xz * maxX - maxZ + c_xz <= 0.0f
&& s_zx * minZ - minX + c_zx >= 0.0f;
}
private boolean MPM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX >= minX && originY <= maxY && originZ >= minZ
&& s_xy * minX - minY + c_xy >= 0.0f
&& s_yx * maxY - maxX + c_yx <= 0.0f
&& s_zy * minZ - minY + c_zy >= 0.0f
&& s_yz * maxY - maxZ + c_yz <= 0.0f
&& s_xz * minX - maxZ + c_xz <= 0.0f
&& s_zx * minZ - maxX + c_zx <= 0.0f;
}
private boolean OPM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX >= minX && originX <= maxX && originY <= maxY && originZ >= minZ
&& s_zy * minZ - minY + c_zy >= 0.0f
&& s_yz * maxY - maxZ + c_yz <= 0.0f;
}
private boolean PPM(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX <= maxX && originY <= maxY && originZ >= minZ
&& s_xy * maxX - minY + c_xy >= 0.0f
&& s_yx * maxY - minX + c_yx >= 0.0f
&& s_zy * minZ - minY + c_zy >= 0.0f
&& s_yz * maxY - maxZ + c_yz <= 0.0f
&& s_xz * maxX - maxZ + c_xz <= 0.0f
&& s_zx * minZ - minX + c_zx >= 0.0f;
}
private boolean MMO(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originZ >= minZ && originZ <= maxZ && originX >= minX && originY >= minY
&& s_xy * minX - maxY + c_xy <= 0.0f
&& s_yx * minY - maxX + c_yx <= 0.0f;
}
private boolean OMO(float minX, float minY, float minZ, float maxX, float maxZ) {
return originY >= minY && originX >= minX && originX <= maxX && originZ >= minZ && originZ <= maxZ;
}
private boolean PMO(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originZ >= minZ && originZ <= maxZ && originX <= maxX && originY >= minY
&& s_xy * maxX - maxY + c_xy <= 0.0f
&& s_yx * minY - minX + c_yx >= 0.0f;
}
private boolean MOO(float minX, float minY, float minZ, float maxY, float maxZ) {
return originX >= minX && originY >= minY && originY <= maxY && originZ >= minZ && originZ <= maxZ;
}
private boolean POO(float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX <= maxX && originY >= minY && originY <= maxY && originZ >= minZ && originZ <= maxZ;
}
private boolean MPO(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originZ >= minZ && originZ <= maxZ && originX >= minX && originY <= maxY
&& s_xy * minX - minY + c_xy >= 0.0f
&& s_yx * maxY - maxX + c_yx <= 0.0f;
}
private boolean OPO(float minX, float minZ, float maxX, float maxY, float maxZ) {
return originY <= maxY && originX >= minX && originX <= maxX && originZ >= minZ && originZ <= maxZ;
}
private boolean PPO(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originZ >= minZ && originZ <= maxZ && originX <= maxX && originY <= maxY
&& s_xy * maxX - minY + c_xy >= 0.0f
&& s_yx * maxY - minX + c_yx >= 0.0f;
}
private boolean MMP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX >= minX && originY >= minY && originZ <= maxZ
&& s_xy * minX - maxY + c_xy <= 0.0f
&& s_yx * minY - maxX + c_yx <= 0.0f
&& s_zy * maxZ - maxY + c_zy <= 0.0f
&& s_yz * minY - minZ + c_yz >= 0.0f
&& s_xz * minX - minZ + c_xz >= 0.0f
&& s_zx * maxZ - maxX + c_zx <= 0.0f;
}
private boolean OMP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX >= minX && originX <= maxX && originY >= minY && originZ <= maxZ
&& s_zy * maxZ - maxY + c_zy <= 0.0f
&& s_yz * minY - minZ + c_yz >= 0.0f;
}
private boolean PMP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX <= maxX && originY >= minY && originZ <= maxZ
&& s_xy * maxX - maxY + c_xy <= 0.0f
&& s_yx * minY - minX + c_yx >= 0.0f
&& s_zy * maxZ - maxY + c_zy <= 0.0f
&& s_yz * minY - minZ + c_yz >= 0.0f
&& s_xz * maxX - minZ + c_xz >= 0.0f
&& s_zx * maxZ - minX + c_zx >= 0.0f;
}
private boolean MOP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originY >= minY && originY <= maxY && originX >= minX && originZ <= maxZ
&& s_xz * minX - minZ + c_xz >= 0.0f
&& s_zx * maxZ - maxX + c_zx <= 0.0f;
}
private boolean OOP(float minX, float minY, float maxX, float maxY, float maxZ) {
return originZ <= maxZ && originX >= minX && originX <= maxX && originY >= minY && originY <= maxY;
}
private boolean POP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originY >= minY && originY <= maxY && originX <= maxX && originZ <= maxZ
&& s_xz * maxX - minZ + c_xz >= 0.0f
&& s_zx * maxZ - minX + c_zx <= 0.0f;
}
private boolean MPP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX >= minX && originY <= maxY && originZ <= maxZ
&& s_xy * minX - minY + c_xy >= 0.0f
&& s_yx * maxY - maxX + c_yx <= 0.0f
&& s_zy * maxZ - minY + c_zy >= 0.0f
&& s_yz * maxY - minZ + c_yz >= 0.0f
&& s_xz * minX - minZ + c_xz >= 0.0f
&& s_zx * maxZ - maxX + c_zx <= 0.0f;
}
private boolean OPP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX >= minX && originX <= maxX && originY <= maxY && originZ <= maxZ
&& s_zy * maxZ - minY + c_zy <= 0.0f
&& s_yz * maxY - minZ + c_yz <= 0.0f;
}
private boolean PPP(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
return originX <= maxX && originY <= maxY && originZ <= maxZ
&& s_xy * maxX - minY + c_xy >= 0.0f
&& s_yx * maxY - minX + c_yx >= 0.0f
&& s_zy * maxZ - minY + c_zy >= 0.0f
&& s_yz * maxY - minZ + c_yz >= 0.0f
&& s_xz * maxX - minZ + c_xz >= 0.0f
&& s_zx * maxZ - minX + c_zx >= 0.0f;
}
}