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 */
32package com.jme3.bounding;
33
34import com.jme3.collision.Collidable;
35import com.jme3.collision.CollisionResult;
36import com.jme3.collision.CollisionResults;
37import com.jme3.collision.UnsupportedCollisionException;
38import com.jme3.export.InputCapsule;
39import com.jme3.export.JmeExporter;
40import com.jme3.export.JmeImporter;
41import com.jme3.export.OutputCapsule;
42import com.jme3.math.*;
43import com.jme3.scene.Mesh;
44import com.jme3.util.BufferUtils;
45import com.jme3.util.TempVars;
46import java.io.IOException;
47import 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 */
62public 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