CollisionShapeFactory.java 11 KB

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