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      */
BoundingBox()70     public BoundingBox() {
71     }
72 
73     /**
74      * Contstructor instantiates a new <code>BoundingBox</code> object with
75      * given specs.
76      */
BoundingBox(Vector3f c, float x, float y, float z)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 
BoundingBox(BoundingBox source)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 
BoundingBox(Vector3f min, Vector3f max)91     public BoundingBox(Vector3f min, Vector3f max) {
92         setMinMax(min, max);
93     }
94 
getType()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      */
computeFromPoints(FloatBuffer points)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      */
computeFromTris(Triangle[] tris, int start, int end)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 
computeFromTris(int[] indices, Mesh mesh, int start, int end)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 
checkMinMax(Vector3f min, Vector3f max, Vector3f point)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      */
containAABB(FloatBuffer points)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      */
transform(Transform trans, BoundingVolume store)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 
transform(Matrix4f trans, BoundingVolume store)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      */
whichSide(Plane plane)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      */
merge(BoundingVolume volume)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      */
mergeLocal(BoundingVolume volume)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      */
merge(Vector3f boxCenter, float boxX, float boxY, float boxZ, BoundingBox rVal)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      */
clone(BoundingVolume store)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
toString()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      */
intersects(BoundingVolume bv)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      */
intersectsSphere(BoundingSphere bs)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      */
intersectsBoundingBox(BoundingBox bb)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      */
intersects(Ray ray)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      */
collideWithRay(Ray ray, CollisionResults results)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 
collideWith(Collidable other, CollisionResults results)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      */
intersects(Vector3f v1, Vector3f v2, Vector3f v3)781     public boolean intersects(Vector3f v1, Vector3f v2, Vector3f v3) {
782         return Intersection.intersect(this, v1, v2, v3);
783     }
784 
785     @Override
contains(Vector3f point)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
intersects(Vector3f point)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 
distanceToEdge(Vector3f point)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      */
clip(float denom, float numer, float[] t)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      */
getExtent(Vector3f store)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 
getXExtent()896     public float getXExtent() {
897         return xExtent;
898     }
899 
getYExtent()900     public float getYExtent() {
901         return yExtent;
902     }
903 
getZExtent()904     public float getZExtent() {
905         return zExtent;
906     }
907 
setXExtent(float xExtent)908     public void setXExtent(float xExtent) {
909         if (xExtent < 0) {
910             throw new IllegalArgumentException();
911         }
912 
913         this.xExtent = xExtent;
914     }
915 
setYExtent(float yExtent)916     public void setYExtent(float yExtent) {
917         if (yExtent < 0) {
918             throw new IllegalArgumentException();
919         }
920 
921         this.yExtent = yExtent;
922     }
923 
setZExtent(float zExtent)924     public void setZExtent(float zExtent) {
925         if (zExtent < 0) {
926             throw new IllegalArgumentException();
927         }
928 
929         this.zExtent = zExtent;
930     }
931 
getMin(Vector3f store)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 
getMax(Vector3f store)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 
setMinMax(Vector3f min, Vector3f max)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
write(JmeExporter e)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
read(JmeImporter e)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
getVolume()974     public float getVolume() {
975         return (8 * xExtent * yExtent * zExtent);
976     }
977 }
978