diff --git a/engine/src/jbullet/com/jme3/bullet/control/RigidBodyControl.java b/engine/src/jbullet/com/jme3/bullet/control/RigidBodyControl.java index 23fafb274..e790dd50f 100644 --- a/engine/src/jbullet/com/jme3/bullet/control/RigidBodyControl.java +++ b/engine/src/jbullet/com/jme3/bullet/control/RigidBodyControl.java @@ -129,23 +129,9 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl } } if (mass > 0) { - Node parent = spatial.getParent(); - if (parent != null) { - spatial.removeFromParent(); - } collisionShape = CollisionShapeFactory.createDynamicMeshShape(spatial); - if (parent != null) { - parent.attachChild(spatial); - } } else { - Node parent = spatial.getParent(); - if (parent != null) { - spatial.removeFromParent(); - } collisionShape = CollisionShapeFactory.createMeshShape(spatial); - if (parent != null) { - parent.attachChild(spatial); - } } } diff --git a/engine/src/jbullet/com/jme3/bullet/util/CollisionShapeFactory.java b/engine/src/jbullet/com/jme3/bullet/util/CollisionShapeFactory.java index 8fc391f14..278a53243 100644 --- a/engine/src/jbullet/com/jme3/bullet/util/CollisionShapeFactory.java +++ b/engine/src/jbullet/com/jme3/bullet/util/CollisionShapeFactory.java @@ -40,6 +40,8 @@ import com.jme3.bullet.collision.shapes.HullCollisionShape; import com.jme3.bullet.collision.shapes.MeshCollisionShape; import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape; import com.jme3.math.Matrix3f; +import com.jme3.math.Quaternion; +import com.jme3.math.Transform; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; @@ -55,25 +57,55 @@ import java.util.LinkedList; */ public class CollisionShapeFactory { - private static CompoundCollisionShape createCompoundShape( + /** + * returns the correct transform for a collisionshape in relation + * to the ancestor for which the collisionshape is generated + * @param spat + * @param parent + * @return + */ + private static Transform getTransform(Spatial spat, Spatial parent) { + Transform shapeTransform = new Transform(); + Spatial parentNode = spat.getParent() != null ? spat.getParent() : spat; + Spatial currentSpatial = spat; + //if we have parents combine their transforms + while (parentNode != null) { + if (parent == currentSpatial) { + //real parent -> only apply scale, not transform + Transform trans = new Transform(); + trans.setScale(currentSpatial.getLocalScale()); + shapeTransform.combineWithParent(trans); + parentNode = null; + } else { + shapeTransform.combineWithParent(currentSpatial.getLocalTransform()); + parentNode = currentSpatial.getParent(); + currentSpatial = parentNode; + } + } + return shapeTransform; + } + + private static CompoundCollisionShape createCompoundShape(Node realRootNode, Node rootNode, CompoundCollisionShape shape, boolean meshAccurate, boolean dynamic) { for (Spatial spatial : rootNode.getChildren()) { if (spatial instanceof Node) { - createCompoundShape((Node) spatial, shape, meshAccurate, dynamic); + createCompoundShape(realRootNode, (Node) spatial, shape, meshAccurate, dynamic); } else if (spatial instanceof Geometry) { if (meshAccurate) { CollisionShape childShape = dynamic - ? createSingleDynamicMeshShape((Geometry) spatial) - : createSingleMeshShape((Geometry) spatial); + ? createSingleDynamicMeshShape((Geometry) spatial, realRootNode) + : createSingleMeshShape((Geometry) spatial, realRootNode); if (childShape != null) { + Transform trans = getTransform(spatial, realRootNode); shape.addChildShape(childShape, - spatial.getWorldTranslation(), - spatial.getWorldRotation().toRotationMatrix()); + trans.getTranslation(), + trans.getRotation().toRotationMatrix()); } } else { - shape.addChildShape(createSingleBoxShape(spatial), - spatial.getWorldTranslation(), - spatial.getWorldRotation().toRotationMatrix()); + Transform trans = getTransform(spatial, realRootNode); + shape.addChildShape(createSingleBoxShape(spatial, realRootNode), + trans.getTranslation(), + trans.getRotation().toRotationMatrix()); } } } @@ -82,7 +114,7 @@ public class CollisionShapeFactory { private static CompoundCollisionShape createCompoundShape( Node rootNode, CompoundCollisionShape shape, boolean meshAccurate) { - return createCompoundShape(rootNode, shape, meshAccurate, false); + return createCompoundShape(rootNode, rootNode, shape, meshAccurate, false); } /** @@ -106,30 +138,17 @@ public class CollisionShapeFactory { /** * This type of collision shape is mesh-accurate and meant for immovable "world objects". - * Examples include terrain, houses or whole shooter levels.
- * Objects with "mesh" type collision shape will not collide with each other. - * @return A MeshCollisionShape or a CompoundCollisionShape with MeshCollisionShapes as children if the supplied spatial is a Node. + * Examples include terrain, houses or whole shooter levels.
+ * Objects with "mesh" type collision shape will not collide with each other.
+ * Creates a HeightfieldCollisionShape if the supplied spatial is a TerrainQuad. + * @return A MeshCollisionShape or a CompoundCollisionShape with MeshCollisionShapes as children if the supplied spatial is a Node. A HeightieldCollisionShape if a TerrainQuad was supplied. */ public static CollisionShape createMeshShape(Spatial spatial) { if (spatial instanceof TerrainQuad) { TerrainQuad terrain = (TerrainQuad) spatial; return new HeightfieldCollisionShape(terrain.getHeightMap(), terrain.getLocalScale()); - //BELOW: the old way, keeping it here for a little bit as a reference (and so it gets into version control so I can always access it) - /*Map all = new HashMap(); - terrain.getAllTerrainPatchesWithTranslation(all, terrain.getLocalTranslation()); - - Node node = new Node(); - - for (Entry entry : all.entrySet()) { - TerrainPatch tp = entry.getKey(); - Vector3f trans = entry.getValue(); - PhysicsNode n = new PhysicsNode(new HeightfieldCollisionShape(tp.getHeightmap(), trans, tp.getLocalScale()), 0 ); - n.setLocalTranslation(trans); - node.attachChild(n); - }*/ - } else if (spatial instanceof Geometry) { - return createSingleMeshShape((Geometry) spatial); + return createSingleMeshShape((Geometry) spatial, spatial); } else if (spatial instanceof Node) { return createMeshCompoundShape((Node) spatial); } else { @@ -144,9 +163,9 @@ public class CollisionShapeFactory { */ public static CollisionShape createDynamicMeshShape(Spatial spatial) { if (spatial instanceof Geometry) { - return createSingleDynamicMeshShape((Geometry) spatial); + return createSingleDynamicMeshShape((Geometry) spatial, spatial); } else if (spatial instanceof Node) { - return createCompoundShape((Node) spatial, new CompoundCollisionShape(), true, true); + return createCompoundShape((Node) spatial, (Node) spatial, new CompoundCollisionShape(), true, true); } else { throw new IllegalArgumentException("Supplied spatial must either be Node or Geometry!"); } @@ -155,7 +174,7 @@ public class CollisionShapeFactory { public static CollisionShape createBoxShape(Spatial spatial) { if (spatial instanceof Geometry) { - return createSingleBoxShape((Geometry) spatial); + return createSingleBoxShape((Geometry) spatial, spatial); } else if (spatial instanceof Node) { return createBoxCompoundShape((Node) spatial); } else { @@ -168,11 +187,12 @@ public class CollisionShapeFactory { * Examples include terrain, houses or whole shooter levels.
* Objects with "mesh" type collision shape will not collide with each other. */ - public static MeshCollisionShape createSingleMeshShape(Geometry geom) { + private static MeshCollisionShape createSingleMeshShape(Geometry geom, Spatial parent) { Mesh mesh = geom.getMesh(); + Transform trans = getTransform(geom, parent); if (mesh != null) { MeshCollisionShape mColl = new MeshCollisionShape(mesh); - mColl.setScale(geom.getWorldScale()); + mColl.setScale(trans.getScale()); return mColl; } else { return null; @@ -184,8 +204,9 @@ public class CollisionShapeFactory { * @param spatial * @return BoxCollisionShape with the size of the spatials BoundingBox */ - public static BoxCollisionShape createSingleBoxShape(Spatial spatial) { + private static BoxCollisionShape createSingleBoxShape(Spatial spatial, Spatial parent) { spatial.setModelBound(new BoundingBox()); + //TODO: using world bound here instead of "local world" bound... BoxCollisionShape shape = new BoxCollisionShape( ((BoundingBox) spatial.getWorldBound()).getExtent(new Vector3f())); return shape; @@ -194,11 +215,12 @@ public class CollisionShapeFactory { /** * This method creates a hull collision shape for the given mesh.
*/ - public static HullCollisionShape createSingleDynamicMeshShape(Geometry geom) { + private static HullCollisionShape createSingleDynamicMeshShape(Geometry geom, Spatial parent) { Mesh mesh = geom.getMesh(); + Transform trans = getTransform(geom, parent); if (mesh != null) { HullCollisionShape dynamicShape = new HullCollisionShape(mesh); - dynamicShape.setScale(geom.getWorldScale()); + dynamicShape.setScale(trans.getScale()); return dynamicShape; } else { return null; @@ -219,5 +241,4 @@ public class CollisionShapeFactory { compoundShape.addChildShape(child, location.add(vector), rotation); } } - } diff --git a/engine/src/jbullet/com/jme3/bullet/util/Converter.java b/engine/src/jbullet/com/jme3/bullet/util/Converter.java index e65cb6c26..b10523810 100644 --- a/engine/src/jbullet/com/jme3/bullet/util/Converter.java +++ b/engine/src/jbullet/com/jme3/bullet/util/Converter.java @@ -247,6 +247,8 @@ public class Converter { for (int i = 0; i < indicesLength; i++) { jBulletIndexedMesh.triangleIndexBase.putInt(indices.get(i)); } + vertices.rewind(); + vertices.clear(); return jBulletIndexedMesh; } @@ -267,9 +269,9 @@ public class Converter { for (int i = 0; i < mesh.numVertices * 3; i++) { vertices.put(i, mesh.vertexBase.getFloat(i * 4)); } - jmeMesh.getFloatBuffer(Type.Position).clear(); jmeMesh.updateCounts(); jmeMesh.updateBound(); + jmeMesh.getFloatBuffer(Type.Position).clear(); return jmeMesh; } diff --git a/engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java b/engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java index 021d9f79e..bd04a0229 100644 --- a/engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java +++ b/engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java @@ -125,11 +125,13 @@ public class DebugShapeFactory { if(shape.getCShape() instanceof ConvexShape){ mesh=new Mesh(); mesh.setBuffer(Type.Position, 3, getVertices((ConvexShape)shape.getCShape())); + mesh.getFloatBuffer(Type.Position).clear(); } else if(shape.getCShape() instanceof ConcaveShape) { mesh=new Mesh(); mesh.setBuffer(Type.Position, 3, getVertices((ConcaveShape)shape.getCShape())); + mesh.getFloatBuffer(Type.Position).clear(); } return mesh; } diff --git a/engine/src/test/jme3test/bullet/TestCollisionShapeFactory.java b/engine/src/test/jme3test/bullet/TestCollisionShapeFactory.java new file mode 100644 index 000000000..13bdccb64 --- /dev/null +++ b/engine/src/test/jme3test/bullet/TestCollisionShapeFactory.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package jme3test.bullet; + +import com.jme3.bullet.BulletAppState; +import com.jme3.app.SimpleApplication; +import com.jme3.bullet.PhysicsSpace; +import com.jme3.bullet.control.RigidBodyControl; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.FastMath; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.shape.Box; +import com.jme3.scene.shape.Cylinder; +import com.jme3.scene.shape.Torus; + +/** + * This is a basic Test of jbullet-jme functions + * + * @author normenhansen + */ +public class TestCollisionShapeFactory extends SimpleApplication { + + private BulletAppState bulletAppState; + private Material mat1; + private Material mat2; + private Material mat3; + + public static void main(String[] args) { + TestCollisionShapeFactory app = new TestCollisionShapeFactory(); + app.start(); + } + + @Override + public void simpleInitApp() { + bulletAppState = new BulletAppState(); + stateManager.attach(bulletAppState); + bulletAppState.getPhysicsSpace().enableDebug(assetManager); + createMaterial(); + + Node node = new Node("node1"); + attachRandomGeometry(node, mat1); + randomizeTransform(node); + + Node node2 = new Node("node2"); + attachRandomGeometry(node2, mat2); + randomizeTransform(node2); + + node.attachChild(node2); + rootNode.attachChild(node); + + RigidBodyControl control = new RigidBodyControl(0); + node.addControl(control); + getPhysicsSpace().add(control); + + //test single geometry too + Geometry myGeom = new Geometry("cylinder", new Cylinder(16, 16, 0.5f, 1)); + myGeom.setMaterial(mat3); + randomizeTransform(myGeom); + rootNode.attachChild(myGeom); + RigidBodyControl control3 = new RigidBodyControl(0); + myGeom.addControl(control3); + getPhysicsSpace().add(control3); + } + + private void attachRandomGeometry(Node node, Material mat) { + Box box = new Box(0.25f, 0.25f, 0.25f); + Torus torus = new Torus(16, 16, 0.2f, 0.8f); + Geometry[] boxes = new Geometry[]{ + new Geometry("box1", box), + new Geometry("box2", box), + new Geometry("box3", box), + new Geometry("torus1", torus), + new Geometry("torus2", torus), + new Geometry("torus3", torus) + }; + for (int i = 0; i < boxes.length; i++) { + Geometry geometry = boxes[i]; + geometry.setLocalTranslation((float) Math.random() * 10 -10, (float) Math.random() * 10 -10, (float) Math.random() * 10 -10); + geometry.setLocalRotation(new Quaternion().fromAngles((float) Math.random() * FastMath.PI, (float) Math.random() * FastMath.PI, (float) Math.random() * FastMath.PI)); + geometry.setLocalScale((float) Math.random() * 10 -10, (float) Math.random() * 10 -10, (float) Math.random() * 10 -10); + geometry.setMaterial(mat); + node.attachChild(geometry); + } + } + + private void randomizeTransform(Spatial spat){ + spat.setLocalTranslation((float) Math.random() * 10, (float) Math.random() * 10, (float) Math.random() * 10); + spat.setLocalTranslation((float) Math.random() * 10, (float) Math.random() * 10, (float) Math.random() * 10); + spat.setLocalScale((float) Math.random() * 2, (float) Math.random() * 2, (float) Math.random() * 2); + } + + private void createMaterial() { + mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat1.setColor("Color", ColorRGBA.Green); + mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat2.setColor("Color", ColorRGBA.Red); + mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat3.setColor("Color", ColorRGBA.Yellow); + } + + private PhysicsSpace getPhysicsSpace() { + return bulletAppState.getPhysicsSpace(); + } +}