/* * The MIT License * * Copyright (c) 2015-2021 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 com.jozufozu.flywheel.repack.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; } }