Home | History | Annotate | Download | only in bounding
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 package com.jme3.bounding;
     33 
     34 import com.jme3.collision.Collidable;
     35 import com.jme3.collision.CollisionResult;
     36 import com.jme3.collision.CollisionResults;
     37 import com.jme3.collision.UnsupportedCollisionException;
     38 import com.jme3.export.InputCapsule;
     39 import com.jme3.export.JmeExporter;
     40 import com.jme3.export.JmeImporter;
     41 import com.jme3.export.OutputCapsule;
     42 import com.jme3.math.*;
     43 import com.jme3.scene.Mesh;
     44 import com.jme3.util.BufferUtils;
     45 import com.jme3.util.TempVars;
     46 import java.io.IOException;
     47 import java.nio.FloatBuffer;
     48 //import com.jme.scene.TriMesh;
     49 
     50 /**
     51  * <code>BoundingBox</code> defines an axis-aligned cube that defines a
     52  * container for a group of vertices of a particular piece of geometry. This box
     53  * defines a center and extents from that center along the x, y and z axis. <br>
     54  * <br>
     55  * A typical usage is to allow the class define the center and radius by calling
     56  * either <code>containAABB</code> or <code>averagePoints</code>. A call to
     57  * <code>computeFramePoint</code> in turn calls <code>containAABB</code>.
     58  *
     59  * @author Joshua Slack
     60  * @version $Id: BoundingBox.java,v 1.50 2007/09/22 16:46:35 irrisor Exp $
     61  */
     62 public class BoundingBox extends BoundingVolume {
     63 
     64     float xExtent, yExtent, zExtent;
     65 
     66     /**
     67      * Default constructor instantiates a new <code>BoundingBox</code>
     68      * object.
     69      */
     70     public BoundingBox() {
     71     }
     72 
     73     /**
     74      * Contstructor instantiates a new <code>BoundingBox</code> object with
     75      * given specs.
     76      */
     77     public BoundingBox(Vector3f c, float x, float y, float z) {
     78         this.center.set(c);
     79         this.xExtent = x;
     80         this.yExtent = y;
     81         this.zExtent = z;
     82     }
     83 
     84     public BoundingBox(BoundingBox source) {
     85         this.center.set(source.center);
     86         this.xExtent = source.xExtent;
     87         this.yExtent = source.yExtent;
     88         this.zExtent = source.zExtent;
     89     }
     90 
     91     public BoundingBox(Vector3f min, Vector3f max) {
     92         setMinMax(min, max);
     93     }
     94 
     95     public Type getType() {
     96         return Type.AABB;
     97     }
     98 
     99     /**
    100      * <code>computeFromPoints</code> creates a new Bounding Box from a given
    101      * set of points. It uses the <code>containAABB</code> method as default.
    102      *
    103      * @param points
    104      *            the points to contain.
    105      */
    106     public void computeFromPoints(FloatBuffer points) {
    107         containAABB(points);
    108     }
    109 
    110     /**
    111      * <code>computeFromTris</code> creates a new Bounding Box from a given
    112      * set of triangles. It is used in OBBTree calculations.
    113      *
    114      * @param tris
    115      * @param start
    116      * @param end
    117      */
    118     public void computeFromTris(Triangle[] tris, int start, int end) {
    119         if (end - start <= 0) {
    120             return;
    121         }
    122 
    123         TempVars vars = TempVars.get();
    124 
    125         Vector3f min = vars.vect1.set(new Vector3f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
    126         Vector3f max = vars.vect2.set(new Vector3f(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
    127 
    128         Vector3f point;
    129         for (int i = start; i < end; i++) {
    130             point = tris[i].get(0);
    131             checkMinMax(min, max, point);
    132             point = tris[i].get(1);
    133             checkMinMax(min, max, point);
    134             point = tris[i].get(2);
    135             checkMinMax(min, max, point);
    136         }
    137 
    138         center.set(min.addLocal(max));
    139         center.multLocal(0.5f);
    140 
    141         xExtent = max.x - center.x;
    142         yExtent = max.y - center.y;
    143         zExtent = max.z - center.z;
    144 
    145         vars.release();
    146     }
    147 
    148     public void computeFromTris(int[] indices, Mesh mesh, int start, int end) {
    149         if (end - start <= 0) {
    150             return;
    151         }
    152 
    153         TempVars vars = TempVars.get();
    154 
    155         Vector3f vect1 = vars.vect1;
    156         Vector3f vect2 = vars.vect2;
    157         Triangle triangle = vars.triangle;
    158 
    159         Vector3f min = vect1.set(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
    160         Vector3f max = vect2.set(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
    161         Vector3f point;
    162 
    163         for (int i = start; i < end; i++) {
    164             mesh.getTriangle(indices[i], triangle);
    165             point = triangle.get(0);
    166             checkMinMax(min, max, point);
    167             point = triangle.get(1);
    168             checkMinMax(min, max, point);
    169             point = triangle.get(2);
    170             checkMinMax(min, max, point);
    171         }
    172 
    173         center.set(min.addLocal(max));
    174         center.multLocal(0.5f);
    175 
    176         xExtent = max.x - center.x;
    177         yExtent = max.y - center.y;
    178         zExtent = max.z - center.z;
    179 
    180         vars.release();
    181     }
    182 
    183     public static void checkMinMax(Vector3f min, Vector3f max, Vector3f point) {
    184         if (point.x < min.x) {
    185             min.x = point.x;
    186         }
    187         if (point.x > max.x) {
    188             max.x = point.x;
    189         }
    190         if (point.y < min.y) {
    191             min.y = point.y;
    192         }
    193         if (point.y > max.y) {
    194             max.y = point.y;
    195         }
    196         if (point.z < min.z) {
    197             min.z = point.z;
    198         }
    199         if (point.z > max.z) {
    200             max.z = point.z;
    201         }
    202     }
    203 
    204     /**
    205      * <code>containAABB</code> creates a minimum-volume axis-aligned bounding
    206      * box of the points, then selects the smallest enclosing sphere of the box
    207      * with the sphere centered at the boxes center.
    208      *
    209      * @param points
    210      *            the list of points.
    211      */
    212     public void containAABB(FloatBuffer points) {
    213         if (points == null) {
    214             return;
    215         }
    216 
    217         points.rewind();
    218         if (points.remaining() <= 2) // we need at least a 3 float vector
    219         {
    220             return;
    221         }
    222 
    223         TempVars vars = TempVars.get();
    224 
    225         BufferUtils.populateFromBuffer(vars.vect1, points, 0);
    226         float minX = vars.vect1.x, minY = vars.vect1.y, minZ = vars.vect1.z;
    227         float maxX = vars.vect1.x, maxY = vars.vect1.y, maxZ = vars.vect1.z;
    228 
    229         for (int i = 1, len = points.remaining() / 3; i < len; i++) {
    230             BufferUtils.populateFromBuffer(vars.vect1, points, i);
    231 
    232             if (vars.vect1.x < minX) {
    233                 minX = vars.vect1.x;
    234             } else if (vars.vect1.x > maxX) {
    235                 maxX = vars.vect1.x;
    236             }
    237 
    238             if (vars.vect1.y < minY) {
    239                 minY = vars.vect1.y;
    240             } else if (vars.vect1.y > maxY) {
    241                 maxY = vars.vect1.y;
    242             }
    243 
    244             if (vars.vect1.z < minZ) {
    245                 minZ = vars.vect1.z;
    246             } else if (vars.vect1.z > maxZ) {
    247                 maxZ = vars.vect1.z;
    248             }
    249         }
    250 
    251         vars.release();
    252 
    253         center.set(minX + maxX, minY + maxY, minZ + maxZ);
    254         center.multLocal(0.5f);
    255 
    256         xExtent = maxX - center.x;
    257         yExtent = maxY - center.y;
    258         zExtent = maxZ - center.z;
    259     }
    260 
    261     /**
    262      * <code>transform</code> modifies the center of the box to reflect the
    263      * change made via a rotation, translation and scale.
    264      *
    265      * @param trans
    266      *            the transform to apply
    267      * @param store
    268      *            box to store result in
    269      */
    270     public BoundingVolume transform(Transform trans, BoundingVolume store) {
    271 
    272         BoundingBox box;
    273         if (store == null || store.getType() != Type.AABB) {
    274             box = new BoundingBox();
    275         } else {
    276             box = (BoundingBox) store;
    277         }
    278 
    279         center.mult(trans.getScale(), box.center);
    280         trans.getRotation().mult(box.center, box.center);
    281         box.center.addLocal(trans.getTranslation());
    282 
    283         TempVars vars = TempVars.get();
    284 
    285         Matrix3f transMatrix = vars.tempMat3;
    286         transMatrix.set(trans.getRotation());
    287         // Make the rotation matrix all positive to get the maximum x/y/z extent
    288         transMatrix.absoluteLocal();
    289 
    290         Vector3f scale = trans.getScale();
    291         vars.vect1.set(xExtent * scale.x, yExtent * scale.y, zExtent * scale.z);
    292         transMatrix.mult(vars.vect1, vars.vect2);
    293         // Assign the biggest rotations after scales.
    294         box.xExtent = FastMath.abs(vars.vect2.getX());
    295         box.yExtent = FastMath.abs(vars.vect2.getY());
    296         box.zExtent = FastMath.abs(vars.vect2.getZ());
    297 
    298         vars.release();
    299 
    300         return box;
    301     }
    302 
    303     public BoundingVolume transform(Matrix4f trans, BoundingVolume store) {
    304         BoundingBox box;
    305         if (store == null || store.getType() != Type.AABB) {
    306             box = new BoundingBox();
    307         } else {
    308             box = (BoundingBox) store;
    309         }
    310         TempVars vars = TempVars.get();
    311 
    312 
    313         float w = trans.multProj(center, box.center);
    314         box.center.divideLocal(w);
    315 
    316         Matrix3f transMatrix = vars.tempMat3;
    317         trans.toRotationMatrix(transMatrix);
    318 
    319         // Make the rotation matrix all positive to get the maximum x/y/z extent
    320         transMatrix.absoluteLocal();
    321 
    322         vars.vect1.set(xExtent, yExtent, zExtent);
    323         transMatrix.mult(vars.vect1, vars.vect1);
    324 
    325         // Assign the biggest rotations after scales.
    326         box.xExtent = FastMath.abs(vars.vect1.getX());
    327         box.yExtent = FastMath.abs(vars.vect1.getY());
    328         box.zExtent = FastMath.abs(vars.vect1.getZ());
    329 
    330         vars.release();
    331 
    332         return box;
    333     }
    334 
    335     /**
    336      * <code>whichSide</code> takes a plane (typically provided by a view
    337      * frustum) to determine which side this bound is on.
    338      *
    339      * @param plane
    340      *            the plane to check against.
    341      */
    342     public Plane.Side whichSide(Plane plane) {
    343         float radius = FastMath.abs(xExtent * plane.getNormal().getX())
    344                 + FastMath.abs(yExtent * plane.getNormal().getY())
    345                 + FastMath.abs(zExtent * plane.getNormal().getZ());
    346 
    347         float distance = plane.pseudoDistance(center);
    348 
    349         //changed to < and > to prevent floating point precision problems
    350         if (distance < -radius) {
    351             return Plane.Side.Negative;
    352         } else if (distance > radius) {
    353             return Plane.Side.Positive;
    354         } else {
    355             return Plane.Side.None;
    356         }
    357     }
    358 
    359     /**
    360      * <code>merge</code> combines this sphere with a second bounding sphere.
    361      * This new sphere contains both bounding spheres and is returned.
    362      *
    363      * @param volume
    364      *            the sphere to combine with this sphere.
    365      * @return the new sphere
    366      */
    367     public BoundingVolume merge(BoundingVolume volume) {
    368         if (volume == null) {
    369             return this;
    370         }
    371 
    372         switch (volume.getType()) {
    373             case AABB: {
    374                 BoundingBox vBox = (BoundingBox) volume;
    375                 return merge(vBox.center, vBox.xExtent, vBox.yExtent,
    376                         vBox.zExtent, new BoundingBox(new Vector3f(0, 0, 0), 0,
    377                         0, 0));
    378             }
    379 
    380             case Sphere: {
    381                 BoundingSphere vSphere = (BoundingSphere) volume;
    382                 return merge(vSphere.center, vSphere.radius, vSphere.radius,
    383                         vSphere.radius, new BoundingBox(new Vector3f(0, 0, 0),
    384                         0, 0, 0));
    385             }
    386 
    387 //            case OBB: {
    388 //                OrientedBoundingBox box = (OrientedBoundingBox) volume;
    389 //                BoundingBox rVal = (BoundingBox) this.clone(null);
    390 //                return rVal.mergeOBB(box);
    391 //            }
    392 
    393             default:
    394                 return null;
    395         }
    396     }
    397 
    398     /**
    399      * <code>mergeLocal</code> combines this sphere with a second bounding
    400      * sphere locally. Altering this sphere to contain both the original and the
    401      * additional sphere volumes;
    402      *
    403      * @param volume
    404      *            the sphere to combine with this sphere.
    405      * @return this
    406      */
    407     public BoundingVolume mergeLocal(BoundingVolume volume) {
    408         if (volume == null) {
    409             return this;
    410         }
    411 
    412         switch (volume.getType()) {
    413             case AABB: {
    414                 BoundingBox vBox = (BoundingBox) volume;
    415                 return merge(vBox.center, vBox.xExtent, vBox.yExtent,
    416                         vBox.zExtent, this);
    417             }
    418 
    419             case Sphere: {
    420                 BoundingSphere vSphere = (BoundingSphere) volume;
    421                 return merge(vSphere.center, vSphere.radius, vSphere.radius,
    422                         vSphere.radius, this);
    423             }
    424 
    425 //            case OBB: {
    426 //                return mergeOBB((OrientedBoundingBox) volume);
    427 //            }
    428 
    429             default:
    430                 return null;
    431         }
    432     }
    433 
    434     /**
    435      * Merges this AABB with the given OBB.
    436      *
    437      * @param volume
    438      *            the OBB to merge this AABB with.
    439      * @return This AABB extended to fit the given OBB.
    440      */
    441 //    private BoundingBox mergeOBB(OrientedBoundingBox volume) {
    442 //        if (!volume.correctCorners)
    443 //            volume.computeCorners();
    444 //
    445 //        TempVars vars = TempVars.get();
    446 //        Vector3f min = vars.compVect1.set(center.x - xExtent, center.y - yExtent,
    447 //                center.z - zExtent);
    448 //        Vector3f max = vars.compVect2.set(center.x + xExtent, center.y + yExtent,
    449 //                center.z + zExtent);
    450 //
    451 //        for (int i = 1; i < volume.vectorStore.length; i++) {
    452 //            Vector3f temp = volume.vectorStore[i];
    453 //            if (temp.x < min.x)
    454 //                min.x = temp.x;
    455 //            else if (temp.x > max.x)
    456 //                max.x = temp.x;
    457 //
    458 //            if (temp.y < min.y)
    459 //                min.y = temp.y;
    460 //            else if (temp.y > max.y)
    461 //                max.y = temp.y;
    462 //
    463 //            if (temp.z < min.z)
    464 //                min.z = temp.z;
    465 //            else if (temp.z > max.z)
    466 //                max.z = temp.z;
    467 //        }
    468 //
    469 //        center.set(min.addLocal(max));
    470 //        center.multLocal(0.5f);
    471 //
    472 //        xExtent = max.x - center.x;
    473 //        yExtent = max.y - center.y;
    474 //        zExtent = max.z - center.z;
    475 //        return this;
    476 //    }
    477     /**
    478      * <code>merge</code> combines this bounding box with another box which is
    479      * defined by the center, x, y, z extents.
    480      *
    481      * @param boxCenter
    482      *            the center of the box to merge with
    483      * @param boxX
    484      *            the x extent of the box to merge with.
    485      * @param boxY
    486      *            the y extent of the box to merge with.
    487      * @param boxZ
    488      *            the z extent of the box to merge with.
    489      * @param rVal
    490      *            the resulting merged box.
    491      * @return the resulting merged box.
    492      */
    493     private BoundingBox merge(Vector3f boxCenter, float boxX, float boxY,
    494             float boxZ, BoundingBox rVal) {
    495 
    496         TempVars vars = TempVars.get();
    497 
    498         vars.vect1.x = center.x - xExtent;
    499         if (vars.vect1.x > boxCenter.x - boxX) {
    500             vars.vect1.x = boxCenter.x - boxX;
    501         }
    502         vars.vect1.y = center.y - yExtent;
    503         if (vars.vect1.y > boxCenter.y - boxY) {
    504             vars.vect1.y = boxCenter.y - boxY;
    505         }
    506         vars.vect1.z = center.z - zExtent;
    507         if (vars.vect1.z > boxCenter.z - boxZ) {
    508             vars.vect1.z = boxCenter.z - boxZ;
    509         }
    510 
    511         vars.vect2.x = center.x + xExtent;
    512         if (vars.vect2.x < boxCenter.x + boxX) {
    513             vars.vect2.x = boxCenter.x + boxX;
    514         }
    515         vars.vect2.y = center.y + yExtent;
    516         if (vars.vect2.y < boxCenter.y + boxY) {
    517             vars.vect2.y = boxCenter.y + boxY;
    518         }
    519         vars.vect2.z = center.z + zExtent;
    520         if (vars.vect2.z < boxCenter.z + boxZ) {
    521             vars.vect2.z = boxCenter.z + boxZ;
    522         }
    523 
    524         center.set(vars.vect2).addLocal(vars.vect1).multLocal(0.5f);
    525 
    526         xExtent = vars.vect2.x - center.x;
    527         yExtent = vars.vect2.y - center.y;
    528         zExtent = vars.vect2.z - center.z;
    529 
    530         vars.release();
    531 
    532         return rVal;
    533     }
    534 
    535     /**
    536      * <code>clone</code> creates a new BoundingBox object containing the same
    537      * data as this one.
    538      *
    539      * @param store
    540      *            where to store the cloned information. if null or wrong class,
    541      *            a new store is created.
    542      * @return the new BoundingBox
    543      */
    544     public BoundingVolume clone(BoundingVolume store) {
    545         if (store != null && store.getType() == Type.AABB) {
    546             BoundingBox rVal = (BoundingBox) store;
    547             rVal.center.set(center);
    548             rVal.xExtent = xExtent;
    549             rVal.yExtent = yExtent;
    550             rVal.zExtent = zExtent;
    551             rVal.checkPlane = checkPlane;
    552             return rVal;
    553         }
    554 
    555         BoundingBox rVal = new BoundingBox(center.clone(),
    556                 xExtent, yExtent, zExtent);
    557         return rVal;
    558     }
    559 
    560     /**
    561      * <code>toString</code> returns the string representation of this object.
    562      * The form is: "Radius: RRR.SSSS Center: <Vector>".
    563      *
    564      * @return the string representation of this.
    565      */
    566     @Override
    567     public String toString() {
    568         return getClass().getSimpleName() + " [Center: " + center + "  xExtent: "
    569                 + xExtent + "  yExtent: " + yExtent + "  zExtent: " + zExtent
    570                 + "]";
    571     }
    572 
    573     /**
    574      * intersects determines if this Bounding Box intersects with another given
    575      * bounding volume. If so, true is returned, otherwise, false is returned.
    576      *
    577      * @see BoundingVolume#intersects(com.jme3.bounding.BoundingVolume)
    578      */
    579     public boolean intersects(BoundingVolume bv) {
    580         return bv.intersectsBoundingBox(this);
    581     }
    582 
    583     /**
    584      * determines if this bounding box intersects a given bounding sphere.
    585      *
    586      * @see BoundingVolume#intersectsSphere(com.jme3.bounding.BoundingSphere)
    587      */
    588     public boolean intersectsSphere(BoundingSphere bs) {
    589         assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center);
    590 
    591         if (FastMath.abs(center.x - bs.center.x) < bs.getRadius()
    592                 + xExtent
    593                 && FastMath.abs(center.y - bs.center.y) < bs.getRadius()
    594                 + yExtent
    595                 && FastMath.abs(center.z - bs.center.z) < bs.getRadius()
    596                 + zExtent) {
    597             return true;
    598         }
    599 
    600         return false;
    601     }
    602 
    603     /**
    604      * determines if this bounding box intersects a given bounding box. If the
    605      * two boxes intersect in any way, true is returned. Otherwise, false is
    606      * returned.
    607      *
    608      * @see BoundingVolume#intersectsBoundingBox(com.jme3.bounding.BoundingBox)
    609      */
    610     public boolean intersectsBoundingBox(BoundingBox bb) {
    611         assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center);
    612 
    613         if (center.x + xExtent < bb.center.x - bb.xExtent
    614                 || center.x - xExtent > bb.center.x + bb.xExtent) {
    615             return false;
    616         } else if (center.y + yExtent < bb.center.y - bb.yExtent
    617                 || center.y - yExtent > bb.center.y + bb.yExtent) {
    618             return false;
    619         } else if (center.z + zExtent < bb.center.z - bb.zExtent
    620                 || center.z - zExtent > bb.center.z + bb.zExtent) {
    621             return false;
    622         } else {
    623             return true;
    624         }
    625     }
    626 
    627     /**
    628      * determines if this bounding box intersects with a given oriented bounding
    629      * box.
    630      *
    631      * @see com.jme.bounding.BoundingVolume#intersectsOrientedBoundingBox(com.jme.bounding.OrientedBoundingBox)
    632      */
    633 //    public boolean intersectsOrientedBoundingBox(OrientedBoundingBox obb) {
    634 //        return obb.intersectsBoundingBox(this);
    635 //    }
    636     /**
    637      * determines if this bounding box intersects with a given ray object. If an
    638      * intersection has occurred, true is returned, otherwise false is returned.
    639      *
    640      * @see BoundingVolume#intersects(com.jme3.math.Ray)
    641      */
    642     public boolean intersects(Ray ray) {
    643         assert Vector3f.isValidVector(center);
    644 
    645         float rhs;
    646 
    647         TempVars vars = TempVars.get();
    648 
    649         Vector3f diff = ray.origin.subtract(getCenter(vars.vect2), vars.vect1);
    650 
    651         final float[] fWdU = vars.fWdU;
    652         final float[] fAWdU = vars.fAWdU;
    653         final float[] fDdU = vars.fDdU;
    654         final float[] fADdU = vars.fADdU;
    655         final float[] fAWxDdU = vars.fAWxDdU;
    656 
    657         fWdU[0] = ray.getDirection().dot(Vector3f.UNIT_X);
    658         fAWdU[0] = FastMath.abs(fWdU[0]);
    659         fDdU[0] = diff.dot(Vector3f.UNIT_X);
    660         fADdU[0] = FastMath.abs(fDdU[0]);
    661         if (fADdU[0] > xExtent && fDdU[0] * fWdU[0] >= 0.0) {
    662             vars.release();
    663             return false;
    664         }
    665 
    666         fWdU[1] = ray.getDirection().dot(Vector3f.UNIT_Y);
    667         fAWdU[1] = FastMath.abs(fWdU[1]);
    668         fDdU[1] = diff.dot(Vector3f.UNIT_Y);
    669         fADdU[1] = FastMath.abs(fDdU[1]);
    670         if (fADdU[1] > yExtent && fDdU[1] * fWdU[1] >= 0.0) {
    671             vars.release();
    672             return false;
    673         }
    674 
    675         fWdU[2] = ray.getDirection().dot(Vector3f.UNIT_Z);
    676         fAWdU[2] = FastMath.abs(fWdU[2]);
    677         fDdU[2] = diff.dot(Vector3f.UNIT_Z);
    678         fADdU[2] = FastMath.abs(fDdU[2]);
    679         if (fADdU[2] > zExtent && fDdU[2] * fWdU[2] >= 0.0) {
    680             vars.release();
    681             return false;
    682         }
    683 
    684         Vector3f wCrossD = ray.getDirection().cross(diff, vars.vect2);
    685 
    686         fAWxDdU[0] = FastMath.abs(wCrossD.dot(Vector3f.UNIT_X));
    687         rhs = yExtent * fAWdU[2] + zExtent * fAWdU[1];
    688         if (fAWxDdU[0] > rhs) {
    689             vars.release();
    690             return false;
    691         }
    692 
    693         fAWxDdU[1] = FastMath.abs(wCrossD.dot(Vector3f.UNIT_Y));
    694         rhs = xExtent * fAWdU[2] + zExtent * fAWdU[0];
    695         if (fAWxDdU[1] > rhs) {
    696             vars.release();
    697             return false;
    698         }
    699 
    700         fAWxDdU[2] = FastMath.abs(wCrossD.dot(Vector3f.UNIT_Z));
    701         rhs = xExtent * fAWdU[1] + yExtent * fAWdU[0];
    702         if (fAWxDdU[2] > rhs) {
    703             vars.release();
    704             return false;
    705         }
    706 
    707         vars.release();
    708         return true;
    709     }
    710 
    711     /**
    712      * @see com.jme.bounding.BoundingVolume#intersectsWhere(com.jme.math.Ray)
    713      */
    714     private int collideWithRay(Ray ray, CollisionResults results) {
    715         TempVars vars = TempVars.get();
    716 
    717         Vector3f diff = vars.vect1.set(ray.origin).subtractLocal(center);
    718         Vector3f direction = vars.vect2.set(ray.direction);
    719 
    720         float[] t = {0f, Float.POSITIVE_INFINITY};
    721 
    722         float saveT0 = t[0], saveT1 = t[1];
    723         boolean notEntirelyClipped = clip(+direction.x, -diff.x - xExtent, t)
    724                 && clip(-direction.x, +diff.x - xExtent, t)
    725                 && clip(+direction.y, -diff.y - yExtent, t)
    726                 && clip(-direction.y, +diff.y - yExtent, t)
    727                 && clip(+direction.z, -diff.z - zExtent, t)
    728                 && clip(-direction.z, +diff.z - zExtent, t);
    729         vars.release();
    730 
    731         if (notEntirelyClipped && (t[0] != saveT0 || t[1] != saveT1)) {
    732             if (t[1] > t[0]) {
    733                 float[] distances = t;
    734                 Vector3f[] points = new Vector3f[]{
    735                     new Vector3f(ray.direction).multLocal(distances[0]).addLocal(ray.origin),
    736                     new Vector3f(ray.direction).multLocal(distances[1]).addLocal(ray.origin)
    737                 };
    738 
    739                 CollisionResult result = new CollisionResult(points[0], distances[0]);
    740                 results.addCollision(result);
    741                 result = new CollisionResult(points[1], distances[1]);
    742                 results.addCollision(result);
    743                 return 2;
    744             }
    745 
    746             Vector3f point = new Vector3f(ray.direction).multLocal(t[0]).addLocal(ray.origin);
    747             CollisionResult result = new CollisionResult(point, t[0]);
    748             results.addCollision(result);
    749             return 1;
    750         }
    751         return 0;
    752     }
    753 
    754     public int collideWith(Collidable other, CollisionResults results) {
    755         if (other instanceof Ray) {
    756             Ray ray = (Ray) other;
    757             return collideWithRay(ray, results);
    758         } else if (other instanceof Triangle) {
    759             Triangle t = (Triangle) other;
    760             if (intersects(t.get1(), t.get2(), t.get3())) {
    761                 CollisionResult r = new CollisionResult();
    762                 results.addCollision(r);
    763                 return 1;
    764             }
    765             return 0;
    766         } else {
    767             throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName());
    768         }
    769     }
    770 
    771     /**
    772      * C code ported from <a href="http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt">
    773      * http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt</a>
    774      *
    775      * @param v1 The first point in the triangle
    776      * @param v2 The second point in the triangle
    777      * @param v3 The third point in the triangle
    778      * @return True if the bounding box intersects the triangle, false
    779      * otherwise.
    780      */
    781     public boolean intersects(Vector3f v1, Vector3f v2, Vector3f v3) {
    782         return Intersection.intersect(this, v1, v2, v3);
    783     }
    784 
    785     @Override
    786     public boolean contains(Vector3f point) {
    787         return FastMath.abs(center.x - point.x) < xExtent
    788                 && FastMath.abs(center.y - point.y) < yExtent
    789                 && FastMath.abs(center.z - point.z) < zExtent;
    790     }
    791 
    792     @Override
    793     public boolean intersects(Vector3f point) {
    794         return FastMath.abs(center.x - point.x) <= xExtent
    795                 && FastMath.abs(center.y - point.y) <= yExtent
    796                 && FastMath.abs(center.z - point.z) <= zExtent;
    797     }
    798 
    799     public float distanceToEdge(Vector3f point) {
    800         // compute coordinates of point in box coordinate system
    801         TempVars vars= TempVars.get();
    802         Vector3f closest = vars.vect1;
    803 
    804         point.subtract(center,closest);
    805 
    806         // project test point onto box
    807         float sqrDistance = 0.0f;
    808         float delta;
    809 
    810         if (closest.x < -xExtent) {
    811             delta = closest.x + xExtent;
    812             sqrDistance += delta * delta;
    813             closest.x = -xExtent;
    814         } else if (closest.x > xExtent) {
    815             delta = closest.x - xExtent;
    816             sqrDistance += delta * delta;
    817             closest.x = xExtent;
    818         }
    819 
    820         if (closest.y < -yExtent) {
    821             delta = closest.y + yExtent;
    822             sqrDistance += delta * delta;
    823             closest.y = -yExtent;
    824         } else if (closest.y > yExtent) {
    825             delta = closest.y - yExtent;
    826             sqrDistance += delta * delta;
    827             closest.y = yExtent;
    828         }
    829 
    830         if (closest.z < -zExtent) {
    831             delta = closest.z + zExtent;
    832             sqrDistance += delta * delta;
    833             closest.z = -zExtent;
    834         } else if (closest.z > zExtent) {
    835             delta = closest.z - zExtent;
    836             sqrDistance += delta * delta;
    837             closest.z = zExtent;
    838         }
    839 
    840         vars.release();
    841         return FastMath.sqrt(sqrDistance);
    842     }
    843 
    844     /**
    845      * <code>clip</code> determines if a line segment intersects the current
    846      * test plane.
    847      *
    848      * @param denom
    849      *            the denominator of the line segment.
    850      * @param numer
    851      *            the numerator of the line segment.
    852      * @param t
    853      *            test values of the plane.
    854      * @return true if the line segment intersects the plane, false otherwise.
    855      */
    856     private boolean clip(float denom, float numer, float[] t) {
    857         // Return value is 'true' if line segment intersects the current test
    858         // plane. Otherwise 'false' is returned in which case the line segment
    859         // is entirely clipped.
    860         if (denom > 0.0f) {
    861             if (numer > denom * t[1]) {
    862                 return false;
    863             }
    864             if (numer > denom * t[0]) {
    865                 t[0] = numer / denom;
    866             }
    867             return true;
    868         } else if (denom < 0.0f) {
    869             if (numer > denom * t[0]) {
    870                 return false;
    871             }
    872             if (numer > denom * t[1]) {
    873                 t[1] = numer / denom;
    874             }
    875             return true;
    876         } else {
    877             return numer <= 0.0;
    878         }
    879     }
    880 
    881     /**
    882      * Query extent.
    883      *
    884      * @param store
    885      *            where extent gets stored - null to return a new vector
    886      * @return store / new vector
    887      */
    888     public Vector3f getExtent(Vector3f store) {
    889         if (store == null) {
    890             store = new Vector3f();
    891         }
    892         store.set(xExtent, yExtent, zExtent);
    893         return store;
    894     }
    895 
    896     public float getXExtent() {
    897         return xExtent;
    898     }
    899 
    900     public float getYExtent() {
    901         return yExtent;
    902     }
    903 
    904     public float getZExtent() {
    905         return zExtent;
    906     }
    907 
    908     public void setXExtent(float xExtent) {
    909         if (xExtent < 0) {
    910             throw new IllegalArgumentException();
    911         }
    912 
    913         this.xExtent = xExtent;
    914     }
    915 
    916     public void setYExtent(float yExtent) {
    917         if (yExtent < 0) {
    918             throw new IllegalArgumentException();
    919         }
    920 
    921         this.yExtent = yExtent;
    922     }
    923 
    924     public void setZExtent(float zExtent) {
    925         if (zExtent < 0) {
    926             throw new IllegalArgumentException();
    927         }
    928 
    929         this.zExtent = zExtent;
    930     }
    931 
    932     public Vector3f getMin(Vector3f store) {
    933         if (store == null) {
    934             store = new Vector3f();
    935         }
    936         store.set(center).subtractLocal(xExtent, yExtent, zExtent);
    937         return store;
    938     }
    939 
    940     public Vector3f getMax(Vector3f store) {
    941         if (store == null) {
    942             store = new Vector3f();
    943         }
    944         store.set(center).addLocal(xExtent, yExtent, zExtent);
    945         return store;
    946     }
    947 
    948     public void setMinMax(Vector3f min, Vector3f max) {
    949         this.center.set(max).addLocal(min).multLocal(0.5f);
    950         xExtent = FastMath.abs(max.x - center.x);
    951         yExtent = FastMath.abs(max.y - center.y);
    952         zExtent = FastMath.abs(max.z - center.z);
    953     }
    954 
    955     @Override
    956     public void write(JmeExporter e) throws IOException {
    957         super.write(e);
    958         OutputCapsule capsule = e.getCapsule(this);
    959         capsule.write(xExtent, "xExtent", 0);
    960         capsule.write(yExtent, "yExtent", 0);
    961         capsule.write(zExtent, "zExtent", 0);
    962     }
    963 
    964     @Override
    965     public void read(JmeImporter e) throws IOException {
    966         super.read(e);
    967         InputCapsule capsule = e.getCapsule(this);
    968         xExtent = capsule.readFloat("xExtent", 0);
    969         yExtent = capsule.readFloat("yExtent", 0);
    970         zExtent = capsule.readFloat("zExtent", 0);
    971     }
    972 
    973     @Override
    974     public float getVolume() {
    975         return (8 * xExtent * yExtent * zExtent);
    976     }
    977 }
    978