com.jme3.bounding.BoundingVolume Java Examples

The following examples show how to use com.jme3.bounding.BoundingVolume. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: SceneToolController.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 6 votes vote down vote up
protected void attachBoxSelection(Spatial geom) {
    BoundingVolume bound = geom.getWorldBound();
    if (bound instanceof BoundingBox) {
        BoundingBox bbox = (BoundingBox) bound;
        Vector3f extent = new Vector3f();
        bbox.getExtent(extent);
        WireBox wireBox = new WireBox();
        wireBox.fromBoundingBox(bbox);
        selctionShapeOffset.set(bbox.getCenter()).subtractLocal(geom.getWorldTranslation());
        Geometry selectionGeometry = new Geometry("selection_geometry_sceneviewer", wireBox);
        selectionGeometry.setMaterial(blueMat);
        selectionGeometry.setLocalTransform(geom.getWorldTransform());
        selectionGeometry.setLocalTranslation(bbox.getCenter());
        toolsNode.attachChild(selectionGeometry);
        selectionShape = selectionGeometry;

    }
}
 
Example #2
Source File: TerrainQuad.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 6 votes vote down vote up
@Override
public int collideWith(Collidable other, CollisionResults results){
    int total = 0;

    if (other instanceof Ray)
        return collideWithRay((Ray)other, results);

    // if it didn't collide with this bbox, return
    if (other instanceof BoundingVolume)
        if (!this.getWorldBound().intersects((BoundingVolume)other))
            return total;

    for (Spatial child : children){
        total += child.collideWith(other, results);
    }
    return total;
}
 
Example #3
Source File: TerrainPatch.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 6 votes vote down vote up
@Override
public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
    if (refreshFlags != 0)
        throw new IllegalStateException("Scene graph must be updated" +
                                        " before checking collision");

    if (other instanceof BoundingVolume)
        if (!getWorldBound().intersects((BoundingVolume)other))
            return 0;
    
    if(other instanceof Ray)
        return collideWithRay((Ray)other, results);
    else if (other instanceof BoundingVolume)
        return collideWithBoundingVolume((BoundingVolume)other, results);
    else {
        throw new UnsupportedCollisionException("TerrainPatch cannnot collide with "+other.getClass().getName());
    }
}
 
Example #4
Source File: Ray.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 6 votes vote down vote up
public int collideWith(Collidable other, CollisionResults results) {
    if (other instanceof BoundingVolume) {
        BoundingVolume bv = (BoundingVolume) other;
        return bv.collideWith(this, results);
    } else if (other instanceof AbstractTriangle) {
        AbstractTriangle tri = (AbstractTriangle) other;
        float d = intersects(tri.get1(), tri.get2(), tri.get3());
        if (Float.isInfinite(d) || Float.isNaN(d)) {
            return 0;
        }

        Vector3f point = new Vector3f(direction).multLocal(d).addLocal(origin);
        results.addCollision(new CollisionResult(point, d));
        return 1;
    } else {
        throw new UnsupportedCollisionException();
    }
}
 
Example #5
Source File: Ray.java    From jmonkeyengine with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
@Override
public int collideWith(Collidable other, CollisionResults results) {
    if (other instanceof BoundingVolume) {
        BoundingVolume bv = (BoundingVolume) other;
        return bv.collideWith(this, results);
    } else if (other instanceof AbstractTriangle) {
        AbstractTriangle tri = (AbstractTriangle) other;
        float d = intersects(tri.get1(), tri.get2(), tri.get3());
        if (Float.isInfinite(d) || Float.isNaN(d)) {
            return 0;
        }

        Vector3f point = new Vector3f(direction).multLocal(d).addLocal(origin);
        results.addCollision(new CollisionResult(point, d));
        return 1;
    } else {
        throw new UnsupportedCollisionException();
    }
}
 
Example #6
Source File: BIHTree.java    From jmonkeyengine with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
@Override
public int collideWith(Collidable other,
        Matrix4f worldMatrix,
        BoundingVolume worldBound,
        CollisionResults results) {

    if (other instanceof Ray) {
        Ray ray = (Ray) other;
        return collideWithRay(ray, worldMatrix, worldBound, results);
    } else if (other instanceof BoundingVolume) {
        BoundingVolume bv = (BoundingVolume) other;
        return collideWithBoundingVolume(bv, worldMatrix, results);
    } else {
        throw new UnsupportedCollisionException("Collidable:" + other);
    }
}
 
Example #7
Source File: TerrainQuad.java    From jmonkeyengine with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
@Override
public int collideWith(Collidable other, CollisionResults results){
    int total = 0;

    if (other instanceof Ray)
        return collideWithRay((Ray)other, results);

    // if it didn't collide with this bbox, return
    if (other instanceof BoundingVolume)
        if (!this.getWorldBound().intersects((BoundingVolume)other))
            return total;

    for (Spatial child : children){
        total += child.collideWith(other, results);
    }
    return total;
}
 
Example #8
Source File: TerrainPatch.java    From jmonkeyengine with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
@Override
public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
    if (refreshFlags != 0)
        throw new IllegalStateException("Scene graph must be updated" +
                                        " before checking collision");

    if (other instanceof BoundingVolume)
        if (!getWorldBound().intersects((BoundingVolume)other))
            return 0;

    if(other instanceof Ray)
        return collideWithRay((Ray)other, results);
    else if (other instanceof BoundingVolume)
        return collideWithBoundingVolume((BoundingVolume)other, results);
    else {
        throw new UnsupportedCollisionException("TerrainPatch cannot collide with "+other.getClass().getName());
    }
}
 
Example #9
Source File: PickEventSession.java    From Lemur with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
/**
 *  Returns the min and max world z values for the specified
 *  spatial. 
 */
protected float[] getZBounds( Spatial s ) {
    BoundingVolume bv = s.getWorldBound();
    if( bv == null ) {
        // JME returns null for empty nodes
        return new float[] {0, 1}; 
    }
    Vector3f center = bv.getCenter();
    if( bv instanceof BoundingBox ) {
        BoundingBox bb = (BoundingBox)bv;
        return new float[] { center.z - bb.getZExtent(), center.z + bb.getZExtent() };
    } else if( bv instanceof BoundingSphere ) {
        BoundingSphere bs = (BoundingSphere)bv;
        return new float[] { center.z - bs.getRadius(), center.z + bs.getRadius() };
    } else {
        throw new UnsupportedOperationException("Bounding volume type not supported for:" + bv);
    }        
}
 
Example #10
Source File: InstancedGeometry.java    From jmonkeyengine with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
@Override
protected void updateWorldBound() {
    refreshFlags &= ~RF_BOUND;
    BoundingVolume resultBound = null;

    for (int i = 0; i < firstUnusedIndex; i++) {
        Geometry geom = geometries[i];

        if (geom != null) {
            if (resultBound != null) {
                // merge current world bound with child world bound
                resultBound.mergeLocal(geom.getWorldBound());
            } else {
                // set world bound to first non-null child world bound
                if (geom.getWorldBound() != null) {
                    resultBound = geom.getWorldBound().clone(this.worldBound);
                }
            }
        }
    }

    this.worldBound = resultBound;
}
 
Example #11
Source File: Spatial.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 6 votes vote down vote up
public void read(JmeImporter im) throws IOException {
    InputCapsule ic = im.getCapsule(this);

    name = ic.readString("name", null);
    worldBound = (BoundingVolume) ic.readSavable("world_bound", null);
    cullHint = ic.readEnum("cull_mode", CullHint.class, CullHint.Inherit);
    batchHint = ic.readEnum("batch_hint", BatchHint.class, BatchHint.Inherit);
    queueBucket = ic.readEnum("queue", RenderQueue.Bucket.class,
            RenderQueue.Bucket.Inherit);
    shadowMode = ic.readEnum("shadow_mode", ShadowMode.class,
            ShadowMode.Inherit);

    localTransform = (Transform) ic.readSavable("transform", Transform.IDENTITY);

    localLights = (LightList) ic.readSavable("lights", null);
    localLights.setOwner(this);

    //changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split
    //the AnimControl creates the SkeletonControl for old files and add it to the spatial.
    //The SkeletonControl must be the last in the stack so we add the list of all other control before it.
    //When backward compatibility won't be needed anymore this can be replaced by : 
    //controls = ic.readSavableArrayList("controlsList", null));
    controls.addAll(0, ic.readSavableArrayList("controlsList", null));

    userData = (HashMap<String, Savable>) ic.readStringSavableMap("user_data", null);
}
 
Example #12
Source File: Mesh.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 6 votes vote down vote up
/**
 * Handles collision detection, internal use only.
 * User code should only use collideWith() on scene
 * graph elements such as {@link Spatial}s.
 */
public int collideWith(Collidable other, 
                       Matrix4f worldMatrix,
                       BoundingVolume worldBound,
                       CollisionResults results){

    if (getVertexCount() == 0) {
        return 0;
    }
    
    if (collisionTree == null){
        createCollisionData();
    }
    
    return collisionTree.collideWith(other, worldMatrix, worldBound, results);
}
 
Example #13
Source File: Node.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 5 votes vote down vote up
@Override
public void setModelBound(BoundingVolume modelBound) {
    if(children != null) {
        for (Spatial child : children.getArray()) {
            child.setModelBound(modelBound != null ? modelBound.clone(null) : null);
        }
    }
}
 
Example #14
Source File: Camera.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 5 votes vote down vote up
/**
     * <code>contains</code> tests a bounding volume against the planes of the
     * camera's frustum. The frustums planes are set such that the normals all
     * face in towards the viewable scene. Therefore, if the bounding volume is
     * on the negative side of the plane is can be culled out.
     *
     * NOTE: This method is used internally for culling, for public usage,
     * the plane state of the bounding volume must be saved and restored, e.g:
     * <code>BoundingVolume bv;<br/>
     * Camera c;<br/>
     * int planeState = bv.getPlaneState();<br/>
     * bv.setPlaneState(0);<br/>
     * c.contains(bv);<br/>
     * bv.setPlaneState(plateState);<br/>
     * </code>
     *
     * @param bound the bound to check for culling
     * @return See enums in <code>FrustumIntersect</code>
     */
    public FrustumIntersect contains(BoundingVolume bound) {
        if (bound == null) {
            return FrustumIntersect.Inside;
        }

        int mask;
        FrustumIntersect rVal = FrustumIntersect.Inside;

        for (int planeCounter = FRUSTUM_PLANES; planeCounter >= 0; planeCounter--) {
            if (planeCounter == bound.getCheckPlane()) {
                continue; // we have already checked this plane at first iteration
            }
            int planeId = (planeCounter == FRUSTUM_PLANES) ? bound.getCheckPlane() : planeCounter;
//            int planeId = planeCounter;

            mask = 1 << (planeId);
            if ((planeState & mask) == 0) {
                Plane.Side side = bound.whichSide(worldPlane[planeId]);

                if (side == Plane.Side.Negative) {
                    //object is outside of frustum
                    bound.setCheckPlane(planeId);
                    return FrustumIntersect.Outside;
                } else if (side == Plane.Side.Positive) {
                    //object is visible on *this* plane, so mark this plane
                    //so that we don't check it for sub nodes.
                    planeState |= mask;
                } else {
                    rVal = FrustumIntersect.Intersects;
                }
            }
        }

        return rVal;
    }
 
Example #15
Source File: LodControl.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 5 votes vote down vote up
protected void controlRender(RenderManager rm, ViewPort vp){
    BoundingVolume bv = spatial.getWorldBound();

    Camera cam = vp.getCamera();
    float atanNH = FastMath.atan(cam.getFrustumNear() * cam.getFrustumTop());
    float ratio = (FastMath.PI / (8f * atanNH));
    float newDistance = bv.distanceTo(vp.getCamera().getLocation()) / ratio;
    int level;

    if (Math.abs(newDistance - lastDistance) <= distTolerance)
        level = lastLevel; // we haven't moved relative to the model, send the old measurement back.
    else if (lastDistance > newDistance && lastLevel == 0)
        level = lastLevel; // we're already at the lowest setting and we just got closer to the model, no need to keep trying.
    else if (lastDistance < newDistance && lastLevel == numLevels - 1)
        level = lastLevel; // we're already at the highest setting and we just got further from the model, no need to keep trying.
    else{
        lastDistance = newDistance;

        // estimate area of polygon via bounding volume
        float area = AreaUtils.calcScreenArea(bv, lastDistance, cam.getWidth());
        float trisToDraw = area * trisPerPixel;
        level = numLevels - 1;
        for (int i = numLevels; --i >= 0;){
            if (trisToDraw - numTris[i] < 0){
                break;
            }
            level = i;
        }
        lastLevel = level;
    }

    spatial.setLodLevel(level);
}
 
Example #16
Source File: SpotLight.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 5 votes vote down vote up
@Override
protected void computeLastDistance(Spatial owner) {
    if (owner.getWorldBound() != null) {
        BoundingVolume bv = owner.getWorldBound();
        lastDistance = bv.distanceSquaredTo(position);
    } else {
        lastDistance = owner.getWorldTranslation().distanceSquared(position);
    }
}
 
Example #17
Source File: PointLight.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 5 votes vote down vote up
@Override
public void computeLastDistance(Spatial owner) {
    if (owner.getWorldBound() != null) {
        BoundingVolume bv = owner.getWorldBound();
        lastDistance = bv.distanceSquaredTo(position);
    } else {
        lastDistance = owner.getWorldTranslation().distanceSquared(position);
    }
}
 
Example #18
Source File: Geometry.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 5 votes vote down vote up
/**
 * Sets the model bound to use for this geometry.
 * This alters the bound used on the mesh as well via
 * {@link Mesh#setBound(com.jme3.bounding.BoundingVolume) } and
 * forces the world bounding volume to be recomputed.
 * 
 * @param modelBound The model bound to set
 */
@Override
public void setModelBound(BoundingVolume modelBound) {
    this.worldBound = null;
    mesh.setBound(modelBound);
    setBoundRefresh();

    // NOTE: Calling updateModelBound() would cause the mesh
    // to recompute the bound based on the geometry thus making
    // this call useless!
    //updateModelBound();
}
 
Example #19
Source File: PMDMesh.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 5 votes vote down vote up
@Override
public BoundingVolume getBound() {
    BoundingBox bb = (BoundingBox)super.getBound();
    BoundingBox bb2 = new BoundingBox(bb.getCenter(), bb.getXExtent()*2, bb.getYExtent()*2,
            bb.getZExtent()*2);
    BoundingBox bb3 = new BoundingBox(bb.getCenter().ZERO,5,5,5);
    return bound;
}
 
Example #20
Source File: ShadowUtil.java    From jmonkeyengine with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
/**
 * Compute bounds of a geomList
 * @param list
 * @param mat
 * @return a new instance
 */
public static BoundingBox computeUnionBound(GeometryList list, Matrix4f mat) {
    BoundingBox bbox = new BoundingBox();
    TempVars tempv = TempVars.get();
    for (int i = 0; i < list.size(); i++) {
        BoundingVolume vol = list.get(i).getWorldBound();
        BoundingVolume store = vol.transform(mat, tempv.bbox);
        //Nehon : prevent NaN and infinity values to screw the final bounding box
        if (!Float.isNaN(store.getCenter().x) && !Float.isInfinite(store.getCenter().x)) {
            bbox.mergeLocal(store);
        }
    }
    tempv.release();
    return bbox;
}
 
Example #21
Source File: Mesh.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 5 votes vote down vote up
public void read(JmeImporter im) throws IOException {
        InputCapsule in = im.getCapsule(this);
        meshBound = (BoundingVolume) in.readSavable("modelBound", null);
        vertCount = in.readInt("vertCount", -1);
        elementCount = in.readInt("elementCount", -1);
        maxNumWeights = in.readInt("max_num_weights", -1);
        mode = in.readEnum("mode", Mode.class, Mode.Triangles);
        elementLengths = in.readIntArray("elementLengths", null);
        modeStart = in.readIntArray("modeStart", null);
        collisionTree = (BIHTree) in.readSavable("collisionTree", null);
        elementLengths = in.readIntArray("elementLengths", null);
        modeStart = in.readIntArray("modeStart", null);
        pointSize = in.readFloat("pointSize", 1f);

//        in.readStringSavableMap("buffers", null);
        buffers = (IntMap<VertexBuffer>) in.readIntSavableMap("buffers", null);
        for (Entry<VertexBuffer> entry : buffers){
            buffersList.add(entry.getValue());
        }
        
        //creating hw animation buffers empty so that they are put in the cache
        if(isAnimated()){
            VertexBuffer hwBoneIndex = new VertexBuffer(Type.HWBoneIndex);
            hwBoneIndex.setUsage(Usage.CpuOnly);
            setBuffer(hwBoneIndex);
            VertexBuffer hwBoneWeight = new VertexBuffer(Type.HWBoneWeight);
            hwBoneWeight.setUsage(Usage.CpuOnly);
            setBuffer(hwBoneWeight);
        }
        
        Savable[] lodLevelsSavable = in.readSavableArray("lodLevels", null);
        if (lodLevelsSavable != null) {
            lodLevels = new VertexBuffer[lodLevelsSavable.length];
            System.arraycopy( lodLevelsSavable, 0, lodLevels, 0, lodLevels.length);
        }
    }
 
Example #22
Source File: TerrainPatch.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 5 votes vote down vote up
private int collideWithBoundingVolume(BoundingVolume boundingVolume, CollisionResults results) {
    if (boundingVolume instanceof BoundingBox)
        return collideWithBoundingBox((BoundingBox)boundingVolume, results);
    else if(boundingVolume instanceof BoundingSphere) {
        BoundingSphere sphere = (BoundingSphere) boundingVolume;
        BoundingBox bbox = new BoundingBox(boundingVolume.getCenter().clone(), sphere.getRadius(),
                                                       sphere.getRadius(),
                                                       sphere.getRadius());
        return collideWithBoundingBox(bbox, results);
    }
    return 0;
}
 
Example #23
Source File: ShadowUtil.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 5 votes vote down vote up
/**
 * Computes the bounds of multiple bounding volumes
 * @param bv
 * @return 
 */
public static BoundingBox computeUnionBound(List<BoundingVolume> bv) {
    BoundingBox bbox = new BoundingBox();
    for (int i = 0; i < bv.size(); i++) {
        BoundingVolume vol = bv.get(i);
        bbox.mergeLocal(vol);
    }
    return bbox;
}
 
Example #24
Source File: ShadowUtil.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 5 votes vote down vote up
/**
 * Compute bounds of a geomList
 * @param list
 * @param mat
 * @return 
 */
public static BoundingBox computeUnionBound(GeometryList list, Matrix4f mat) {
    BoundingBox bbox = new BoundingBox();
    BoundingVolume store = null;
    for (int i = 0; i < list.size(); i++) {
        BoundingVolume vol = list.get(i).getWorldBound();
        store = vol.clone().transform(mat, null);
        //Nehon : prevent NaN and infinity values to screw the final bounding box
        if (store.getCenter().x != Float.NaN && store.getCenter().x != Float.POSITIVE_INFINITY && store.getCenter().x != Float.NEGATIVE_INFINITY) {
            bbox.mergeLocal(store);
        }
    }
    return bbox;
}
 
Example #25
Source File: ShadowUtil.java    From MikuMikuStudio with BSD 2-Clause "Simplified" License 5 votes vote down vote up
/**
 * Compute bounds of a geomList
 * @param list
 * @param transform
 * @return 
 */
public static BoundingBox computeUnionBound(GeometryList list, Transform transform) {
    BoundingBox bbox = new BoundingBox();
    for (int i = 0; i < list.size(); i++) {
        BoundingVolume vol = list.get(i).getWorldBound();
        BoundingVolume newVol = vol.transform(transform);
        //Nehon : prevent NaN and infinity values to screw the final bounding box
        if (newVol.getCenter().x != Float.NaN && newVol.getCenter().x != Float.POSITIVE_INFINITY && newVol.getCenter().x != Float.NEGATIVE_INFINITY) {
            bbox.mergeLocal(newVol);
        }
    }
    return bbox;
}
 
Example #26
Source File: ShapeBoundsTest.java    From jmonkeyengine with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
private void testVertices(Geometry geometry) {
    BoundingVolume bv = geometry.getWorldBound();
    Assert.assertNotNull(bv);

    for (int e = 0; e < geometry.getVertexCount(); e++) {
        float x = (Float) geometry.getMesh().getBuffer(VertexBuffer.Type.Position).getElementComponent(e, 0);
        float y = (Float) geometry.getMesh().getBuffer(VertexBuffer.Type.Position).getElementComponent(e, 1);
        float z = (Float) geometry.getMesh().getBuffer(VertexBuffer.Type.Position).getElementComponent(e, 2);
        Vector3f vertex = new Vector3f(x, y, z);
        Assert.assertTrue("Vertex outside world bound: " + vertex, bv.intersects(vertex));
    }
}
 
Example #27
Source File: ShapeBoundsTest.java    From jmonkeyengine with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
private void testBounds(Geometry geometry) {
    BoundingVolume bv = geometry.getWorldBound();

    if (bv instanceof BoundingBox) {
        BoundingBox bb = (BoundingBox) bv;
        Assert.assertTrue(bb.getXExtent() > 0 && bb.getYExtent() > 0 && bb.getZExtent() > 0);
    } else if (bv instanceof BoundingSphere) {
        BoundingSphere bs = (BoundingSphere) bv;
        Assert.assertTrue(bs.getRadius() > 1f);
    }

    testVertices(geometry);
}
 
Example #28
Source File: SpawnToolControl.java    From jmonkeybuilder with Apache License 2.0 5 votes vote down vote up
protected BoundingVolume addPadding(@NotNull BoundingVolume boundingVolume, @NotNull Vector3f padding) {

        if (boundingVolume instanceof BoundingBox) {
            var box = (BoundingBox) boundingVolume;
            var xExtent = box.getXExtent() + padding.getX();
            var yExtent = box.getYExtent() + padding.getY();
            var zExtent = box.getZExtent() + padding.getZ();
            return new BoundingBox(box.getCenter(), xExtent, yExtent, zExtent);
        }

        return boundingVolume;
    }
 
Example #29
Source File: ShadowUtil.java    From jmonkeyengine with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
/**
 * Computes the bounds of multiple bounding volumes
 *
 * @param bv
 * @return a new instance
 */
public static BoundingBox computeUnionBound(List<BoundingVolume> bv) {
    BoundingBox bbox = new BoundingBox();
    for (int i = 0; i < bv.size(); i++) {
        BoundingVolume vol = bv.get(i);
        bbox.mergeLocal(vol);
    }
    return bbox;
}
 
Example #30
Source File: ShapeBoundsTest.java    From jmonkeyengine with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
@Test
public void testQuad() {
    Quad shape = new Quad(64, 16);
    Geometry geometry = new Geometry("geom", shape);
    BoundingVolume bv = geometry.getWorldBound();
    BoundingBox bb = (BoundingBox) bv;
    //Quad z extent 0 is normal, so not using testBounds() here.
    Assert.assertTrue(bb.getXExtent() > 0 && bb.getYExtent() > 0);
    testVertices(geometry);
}