Home | History | Annotate | Download | only in util
      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.bullet.util;
     33 
     34 import com.jme3.bounding.BoundingBox;
     35 import com.jme3.bullet.collision.shapes.*;
     36 import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape;
     37 import com.jme3.math.Matrix3f;
     38 import com.jme3.math.Transform;
     39 import com.jme3.math.Vector3f;
     40 import com.jme3.scene.*;
     41 import com.jme3.terrain.geomipmap.TerrainPatch;
     42 import com.jme3.terrain.geomipmap.TerrainQuad;
     43 import java.util.Iterator;
     44 import java.util.LinkedList;
     45 
     46 /**
     47  *
     48  * @author normenhansen, tim8dev
     49  */
     50 public class CollisionShapeFactory {
     51 
     52     /**
     53      * returns the correct transform for a collisionshape in relation
     54      * to the ancestor for which the collisionshape is generated
     55      * @param spat
     56      * @param parent
     57      * @return
     58      */
     59     private static Transform getTransform(Spatial spat, Spatial parent) {
     60         Transform shapeTransform = new Transform();
     61         Spatial parentNode = spat.getParent() != null ? spat.getParent() : spat;
     62         Spatial currentSpatial = spat;
     63         //if we have parents combine their transforms
     64         while (parentNode != null) {
     65             if (parent == currentSpatial) {
     66                 //real parent -> only apply scale, not transform
     67                 Transform trans = new Transform();
     68                 trans.setScale(currentSpatial.getLocalScale());
     69                 shapeTransform.combineWithParent(trans);
     70                 parentNode = null;
     71             } else {
     72                 shapeTransform.combineWithParent(currentSpatial.getLocalTransform());
     73                 parentNode = currentSpatial.getParent();
     74                 currentSpatial = parentNode;
     75             }
     76         }
     77         return shapeTransform;
     78     }
     79 
     80     private static CompoundCollisionShape createCompoundShape(Node realRootNode,
     81             Node rootNode, CompoundCollisionShape shape, boolean meshAccurate, boolean dynamic) {
     82         for (Spatial spatial : rootNode.getChildren()) {
     83             if (spatial instanceof TerrainQuad) {
     84                 Boolean bool = spatial.getUserData(UserData.JME_PHYSICSIGNORE);
     85                 if (bool != null && bool.booleanValue()) {
     86                     continue; // go to the next child in the loop
     87                 }
     88                 TerrainQuad terrain = (TerrainQuad) spatial;
     89                 Transform trans = getTransform(spatial, realRootNode);
     90                 shape.addChildShape(new HeightfieldCollisionShape(terrain.getHeightMap(), trans.getScale()),
     91                         trans.getTranslation(),
     92                         trans.getRotation().toRotationMatrix());
     93             } else if (spatial instanceof Node) {
     94                 createCompoundShape(realRootNode, (Node) spatial, shape, meshAccurate, dynamic);
     95             } else if (spatial instanceof TerrainPatch) {
     96                 Boolean bool = spatial.getUserData(UserData.JME_PHYSICSIGNORE);
     97                 if (bool != null && bool.booleanValue()) {
     98                     continue; // go to the next child in the loop
     99                 }
    100                 TerrainPatch terrain = (TerrainPatch) spatial;
    101                 Transform trans = getTransform(spatial, realRootNode);
    102                 shape.addChildShape(new HeightfieldCollisionShape(terrain.getHeightMap(), terrain.getLocalScale()),
    103                         trans.getTranslation(),
    104                         trans.getRotation().toRotationMatrix());
    105             } else if (spatial instanceof Geometry) {
    106                 Boolean bool = spatial.getUserData(UserData.JME_PHYSICSIGNORE);
    107                 if (bool != null && bool.booleanValue()) {
    108                     continue; // go to the next child in the loop
    109                 }
    110 
    111                 if (meshAccurate) {
    112                     CollisionShape childShape = dynamic
    113                             ? createSingleDynamicMeshShape((Geometry) spatial, realRootNode)
    114                             : createSingleMeshShape((Geometry) spatial, realRootNode);
    115                     if (childShape != null) {
    116                         Transform trans = getTransform(spatial, realRootNode);
    117                         shape.addChildShape(childShape,
    118                                 trans.getTranslation(),
    119                                 trans.getRotation().toRotationMatrix());
    120                     }
    121                 } else {
    122                     Transform trans = getTransform(spatial, realRootNode);
    123                     shape.addChildShape(createSingleBoxShape(spatial, realRootNode),
    124                             trans.getTranslation(),
    125                             trans.getRotation().toRotationMatrix());
    126                 }
    127             }
    128         }
    129         return shape;
    130     }
    131 
    132     private static CompoundCollisionShape createCompoundShape(
    133             Node rootNode, CompoundCollisionShape shape, boolean meshAccurate) {
    134         return createCompoundShape(rootNode, rootNode, shape, meshAccurate, false);
    135     }
    136 
    137     /**
    138      * This type of collision shape is mesh-accurate and meant for immovable "world objects".
    139      * Examples include terrain, houses or whole shooter levels.<br>
    140      * Objects with "mesh" type collision shape will not collide with each other.
    141      */
    142     private static CompoundCollisionShape createMeshCompoundShape(Node rootNode) {
    143         return createCompoundShape(rootNode, new CompoundCollisionShape(), true);
    144     }
    145 
    146     /**
    147      * This type of collision shape creates a CompoundShape made out of boxes that
    148      * are based on the bounds of the Geometries  in the tree.
    149      * @param rootNode
    150      * @return
    151      */
    152     private static CompoundCollisionShape createBoxCompoundShape(Node rootNode) {
    153         return createCompoundShape(rootNode, new CompoundCollisionShape(), false);
    154     }
    155 
    156     /**
    157      * This type of collision shape is mesh-accurate and meant for immovable "world objects".
    158      * Examples include terrain, houses or whole shooter levels.<br/>
    159      * Objects with "mesh" type collision shape will not collide with each other.<br/>
    160      * Creates a HeightfieldCollisionShape if the supplied spatial is a TerrainQuad.
    161      * @return A MeshCollisionShape or a CompoundCollisionShape with MeshCollisionShapes as children if the supplied spatial is a Node. A HeightieldCollisionShape if a TerrainQuad was supplied.
    162      */
    163     public static CollisionShape createMeshShape(Spatial spatial) {
    164         if (spatial instanceof TerrainQuad) {
    165             TerrainQuad terrain = (TerrainQuad) spatial;
    166             return new HeightfieldCollisionShape(terrain.getHeightMap(), terrain.getLocalScale());
    167         } else if (spatial instanceof TerrainPatch) {
    168             TerrainPatch terrain = (TerrainPatch) spatial;
    169             return new HeightfieldCollisionShape(terrain.getHeightMap(), terrain.getLocalScale());
    170         } else if (spatial instanceof Geometry) {
    171             return createSingleMeshShape((Geometry) spatial, spatial);
    172         } else if (spatial instanceof Node) {
    173             return createMeshCompoundShape((Node) spatial);
    174         } else {
    175             throw new IllegalArgumentException("Supplied spatial must either be Node or Geometry!");
    176         }
    177     }
    178 
    179     /**
    180      * This method creates a hull shape for the given Spatial.<br>
    181      * If you want to have mesh-accurate dynamic shapes (CPU intense!!!) use GImpact shapes, its probably best to do so with a low-poly version of your model.
    182      * @return A HullCollisionShape or a CompoundCollisionShape with HullCollisionShapes as children if the supplied spatial is a Node.
    183      */
    184     public static CollisionShape createDynamicMeshShape(Spatial spatial) {
    185         if (spatial instanceof Geometry) {
    186             return createSingleDynamicMeshShape((Geometry) spatial, spatial);
    187         } else if (spatial instanceof Node) {
    188             return createCompoundShape((Node) spatial, (Node) spatial, new CompoundCollisionShape(), true, true);
    189         } else {
    190             throw new IllegalArgumentException("Supplied spatial must either be Node or Geometry!");
    191         }
    192 
    193     }
    194 
    195     public static CollisionShape createBoxShape(Spatial spatial) {
    196         if (spatial instanceof Geometry) {
    197             return createSingleBoxShape((Geometry) spatial, spatial);
    198         } else if (spatial instanceof Node) {
    199             return createBoxCompoundShape((Node) spatial);
    200         } else {
    201             throw new IllegalArgumentException("Supplied spatial must either be Node or Geometry!");
    202         }
    203     }
    204 
    205     /**
    206      * This type of collision shape is mesh-accurate and meant for immovable "world objects".
    207      * Examples include terrain, houses or whole shooter levels.<br>
    208      * Objects with "mesh" type collision shape will not collide with each other.
    209      */
    210     private static MeshCollisionShape createSingleMeshShape(Geometry geom, Spatial parent) {
    211         Mesh mesh = geom.getMesh();
    212         Transform trans = getTransform(geom, parent);
    213         if (mesh != null) {
    214             MeshCollisionShape mColl = new MeshCollisionShape(mesh);
    215             mColl.setScale(trans.getScale());
    216             return mColl;
    217         } else {
    218             return null;
    219         }
    220     }
    221 
    222     /**
    223      * Uses the bounding box of the supplied spatial to create a BoxCollisionShape
    224      * @param spatial
    225      * @return BoxCollisionShape with the size of the spatials BoundingBox
    226      */
    227     private static BoxCollisionShape createSingleBoxShape(Spatial spatial, Spatial parent) {
    228         spatial.setModelBound(new BoundingBox());
    229         //TODO: using world bound here instead of "local world" bound...
    230         BoxCollisionShape shape = new BoxCollisionShape(
    231                 ((BoundingBox) spatial.getWorldBound()).getExtent(new Vector3f()));
    232         return shape;
    233     }
    234 
    235     /**
    236      * This method creates a hull collision shape for the given mesh.<br>
    237      */
    238     private static HullCollisionShape createSingleDynamicMeshShape(Geometry geom, Spatial parent) {
    239         Mesh mesh = geom.getMesh();
    240         Transform trans = getTransform(geom, parent);
    241         if (mesh != null) {
    242             HullCollisionShape dynamicShape = new HullCollisionShape(mesh);
    243             dynamicShape.setScale(trans.getScale());
    244             return dynamicShape;
    245         } else {
    246             return null;
    247         }
    248     }
    249 
    250     /**
    251      * This method moves each child shape of a compound shape by the given vector
    252      * @param vector
    253      */
    254     public static void shiftCompoundShapeContents(CompoundCollisionShape compoundShape, Vector3f vector) {
    255         for (Iterator<ChildCollisionShape> it = new LinkedList(compoundShape.getChildren()).iterator(); it.hasNext();) {
    256             ChildCollisionShape childCollisionShape = it.next();
    257             CollisionShape child = childCollisionShape.shape;
    258             Vector3f location = childCollisionShape.location;
    259             Matrix3f rotation = childCollisionShape.rotation;
    260             compoundShape.removeChildShape(child);
    261             compoundShape.addChildShape(child, location.add(vector), rotation);
    262         }
    263     }
    264 }
    265