(Solved) Rigid body disappears on contact

Hey monkeys. I’m having an issue with my physics: I can place objects down and add them to a node. Then I use Geometry Batching on that node, and I create a static rigid body of that object and add it to the physics space. The character control interacts with it fine, but if a dynamic rigid body comes into contact with it it just disappears. I wrote up a test case to show this… I pretty much modified TestWalkingChar, so if you’re hooked up to the testdata jar then the assets and everything should work. Left click to add a ninja to the scene, and the monkey head should roll towards it and disappear (the monkey head should disappear, not the ninja).



[java]

package Test;



import com.jme3.animation.AnimChannel;

import com.jme3.animation.AnimControl;

import com.jme3.animation.AnimEventListener;

import com.jme3.animation.LoopMode;

import com.jme3.app.SimpleApplication;

import com.jme3.bullet.BulletAppState;

import com.jme3.bullet.PhysicsSpace;

import com.jme3.bullet.collision.PhysicsCollisionEvent;

import com.jme3.bullet.collision.PhysicsCollisionListener;

import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;

import com.jme3.bullet.collision.shapes.SphereCollisionShape;

import com.jme3.bullet.control.CharacterControl;

import com.jme3.bullet.control.RigidBodyControl;

import com.jme3.bullet.util.CollisionShapeFactory;

import com.jme3.collision.CollisionResults;

import com.jme3.effect.ParticleEmitter;

import com.jme3.effect.ParticleMesh.Type;

import com.jme3.effect.shapes.EmitterSphereShape;

import com.jme3.input.ChaseCamera;

import com.jme3.input.KeyInput;

import com.jme3.input.MouseInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.input.controls.MouseButtonTrigger;

import com.jme3.light.DirectionalLight;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Ray;

import com.jme3.math.Vector2f;

import com.jme3.math.Vector3f;

import com.jme3.post.FilterPostProcessor;

import com.jme3.post.filters.BloomFilter;

import com.jme3.renderer.Camera;

import com.jme3.renderer.queue.RenderQueue.ShadowMode;

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.Sphere;

import com.jme3.scene.shape.Sphere.TextureMode;

import com.jme3.terrain.geomipmap.TerrainLodControl;

import com.jme3.terrain.geomipmap.TerrainQuad;

import com.jme3.terrain.heightmap.AbstractHeightMap;

import com.jme3.terrain.heightmap.ImageBasedHeightMap;

import com.jme3.texture.Texture;

import com.jme3.texture.Texture.WrapMode;

import com.jme3.util.SkyFactory;

import java.util.ArrayList;

import java.util.List;

import jme3tools.optimize.GeometryBatchFactory;



/**

  • A walking animated character followed by a 3rd person camera on a terrain with LOD.
  • @author normenhansen

    */

    public class TestWalkingChar extends SimpleApplication implements ActionListener, PhysicsCollisionListener, AnimEventListener {



    private BulletAppState bulletAppState;

    CharacterControl character;

    Node model;

    Vector3f walkDirection = new Vector3f();

    TerrainQuad terrain;

    RigidBodyControl terrainPhysicsNode;

    RigidBodyControl bulletRBC;

    Material matRock;

    Material matBullet;

    AnimChannel animationChannel;

    AnimChannel shootingChannel;

    AnimControl animationControl;

    float airTime = 0;

    boolean left = false, right = false, up = false, down = false;

    ChaseCamera chaseCam;

    Sphere bullet;

    SphereCollisionShape bulletCollisionShape;

    ParticleEmitter effect;

    Box brick;

    float bLength = 0.8f;

    float bWidth = 0.4f;

    float bHeight = 0.4f;

    FilterPostProcessor fpp;

    Vector3f targetLoc;

    boolean targetChosen;

    Node boxNode;

    RigidBodyControl boxRBC;











    public static void main(String[] args) {

    TestWalkingChar app = new TestWalkingChar();

    app.start();

    }



    @Override

    public void simpleInitApp() {

    bulletAppState = new BulletAppState();

    bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);

    stateManager.attach(bulletAppState);

    setupKeys();

    createLight();

    createSky();

    createTerrain();

    createBoxNode();

    createCharacter();

    createBullet();

    setupChaseCamera();

    setupAnimationController();

    setupFilter();

    }



    private void setupFilter() {

    FilterPostProcessor fpp = new FilterPostProcessor(assetManager);

    BloomFilter bloom = new BloomFilter(BloomFilter.GlowMode.Objects);

    fpp.addFilter(bloom);

    viewPort.addProcessor(fpp);

    }



    private PhysicsSpace getPhysicsSpace() {

    return bulletAppState.getPhysicsSpace();

    }



    private void setupKeys() {

    inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));

    inputManager.addListener(this, "wireframe");

    inputManager.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A));

    inputManager.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D));

    inputManager.addMapping("CharUp", new KeyTrigger(KeyInput.KEY_W));

    inputManager.addMapping("CharDown", new KeyTrigger(KeyInput.KEY_S));

    inputManager.addMapping("CharSpace", new KeyTrigger(KeyInput.KEY_RETURN));

    inputManager.addMapping("CharShoot", new KeyTrigger(KeyInput.KEY_SPACE));

    inputManager.addMapping("ShootRay", new MouseButtonTrigger(MouseInput.BUTTON_LEFT) );

    inputManager.addListener(this, "CharLeft");

    inputManager.addListener(this, "CharRight");

    inputManager.addListener(this, "CharUp");

    inputManager.addListener(this, "CharDown");

    inputManager.addListener(this, "CharSpace");

    inputManager.addListener(this, "CharShoot");

    inputManager.addListener(this, "ShootRay");

    }





    private void createLight() {

    Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();

    DirectionalLight dl = new DirectionalLight();

    dl.setDirection(direction);

    dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));

    rootNode.addLight(dl);

    }



    private void createSky() {

    rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/BrightSky.dds", false));

    }



    private void createTerrain() {

    matRock = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");

    matRock.setBoolean("useTriPlanarMapping", false);

    matRock.setBoolean("WardIso", true);

    matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));

    Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");

    Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");

    grass.setWrap(WrapMode.Repeat);

    matRock.setTexture("DiffuseMap", grass);

    matRock.setFloat("DiffuseMap_0_scale", 64);

    Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");

    dirt.setWrap(WrapMode.Repeat);

    matRock.setTexture("DiffuseMap_1", dirt);

    matRock.setFloat("DiffuseMap_1_scale", 16);

    Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");

    rock.setWrap(WrapMode.Repeat);

    matRock.setTexture("DiffuseMap_2", rock);

    matRock.setFloat("DiffuseMap_2_scale", 128);

    Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");

    normalMap0.setWrap(WrapMode.Repeat);

    Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");

    normalMap1.setWrap(WrapMode.Repeat);

    Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");

    normalMap2.setWrap(WrapMode.Repeat);

    matRock.setTexture("NormalMap", normalMap0);

    matRock.setTexture("NormalMap_1", normalMap2);

    matRock.setTexture("NormalMap_2", normalMap2);



    AbstractHeightMap heightmap = null;

    try {

    heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f);

    heightmap.load();



    } catch (Exception e) {

    e.printStackTrace();

    }



    terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());

    List<Camera> cameras = new ArrayList<Camera>();

    cameras.add(getCamera());

    TerrainLodControl control = new TerrainLodControl(terrain, cameras);

    terrain.addControl(control);

    terrain.setMaterial(matRock);

    terrain.setLocalScale(new Vector3f(2, 2, 2));

    terrain.setName("terrain");



    terrainPhysicsNode = new RigidBodyControl(CollisionShapeFactory.createMeshShape(terrain), 0);

    terrain.addControl(terrainPhysicsNode);

    rootNode.attachChild(terrain);

    getPhysicsSpace().add(terrainPhysicsNode);

    }



    private void createBoxNode(){

    boxNode=new Node();



    boxRBC=new RigidBodyControl(CollisionShapeFactory.createMeshShape(boxNode), 0);

    boxNode.addControl(boxRBC);

    rootNode.attachChild(boxNode);

    getPhysicsSpace().add(boxRBC);

    }





    private void newBox(Vector3f loc){

    Box b = new Box(1,1,1);

    Spatial boxy = assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");

    boxy.scale(0.05f);

    boxy.setLocalTranslation(loc);

    boxNode.attachChild(boxy);

    getPhysicsSpace().remove(boxRBC);

    boxRBC.setCollisionShape(CollisionShapeFactory.createMeshShape(boxNode));

    getPhysicsSpace().add(boxRBC);

    GeometryBatchFactory.optimize(boxNode);

    }



    private void createCharacter() {

    CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f);

    character = new CharacterControl(capsule, 0.01f);

    model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");

    //model.setLocalScale(0.5f);

    model.addControl(character);

    character.setPhysicsLocation(new Vector3f(-140, 15, -10));

    rootNode.attachChild(model);

    getPhysicsSpace().add(character);

    }

    private void createBullet(){

    Spatial bullet= assetManager.loadModel("Models/MonkeyHead/MonkeyHead.mesh.xml");

    bulletCollisionShape = new SphereCollisionShape(1f);

    bulletRBC = new RigidBodyControl(bulletCollisionShape, 100);

    bullet.addControl(bulletRBC);

    rootNode.attachChild(bullet);

    getPhysicsSpace().add(bulletRBC);

    bulletRBC.setPhysicsLocation(new Vector3f(character.getPhysicsLocation().add(10,0,0)));

    }

    private void setupChaseCamera() {

    flyCam.setEnabled(false);

    chaseCam = new ChaseCamera(cam, model, inputManager);

    }



    private void setupAnimationController() {

    animationControl = model.getControl(AnimControl.class);

    animationControl.addListener(this);

    animationChannel = animationControl.createChannel();

    shootingChannel = animationControl.createChannel();

    shootingChannel.addBone(animationControl.getSkeleton().getBone("uparm.right"));

    shootingChannel.addBone(animationControl.getSkeleton().getBone("arm.right"));

    shootingChannel.addBone(animationControl.getSkeleton().getBone("hand.right"));

    }



    @Override

    public void simpleUpdate(float tpf) {

    Vector3f camDir = cam.getDirection().clone().multLocal(0.5f);

    Vector3f camLeft = cam.getLeft().clone().multLocal(0.5f);

    camDir.y = 0;

    camLeft.y = 0;

    walkDirection.set(0, 0, 0);

    if (left) {

    walkDirection.addLocal(camLeft);

    }

    if (right) {

    walkDirection.addLocal(camLeft.negate());

    }

    if (up) {

    walkDirection.addLocal(camDir);

    }

    if (down) {

    walkDirection.addLocal(camDir.negate());

    }

    if (!character.onGround()) {

    airTime = airTime + tpf;

    } else {

    airTime = 0;

    }

    if (walkDirection.length() == 0) {

    if (!"stand".equals(animationChannel.getAnimationName())) {

    animationChannel.setAnim("stand", 1f);

    }

    } else {

    character.setViewDirection(walkDirection);

    if (airTime > .3f) {

    if (!"stand".equals(animationChannel.getAnimationName())) {

    animationChannel.setAnim("stand");

    }

    } else if (!"Walk".equals(animationChannel.getAnimationName())) {

    animationChannel.setAnim("Walk", 0.7f);

    }

    }

    character.setWalkDirection(walkDirection);

    if(targetChosen){

    bulletRBC.applyImpulse(targetLoc.subtract(bulletRBC.getPhysicsLocation()).divide(5), Vector3f.ZERO);

    }

    }



    public void onAction(String binding, boolean value, float tpf) {

    if (binding.equals("CharLeft")) {

    if (value) {

    left = true;

    } else {

    left = false;

    }

    } else if (binding.equals("CharRight")) {

    if (value) {

    right = true;

    } else {

    right = false;

    }

    } else if (binding.equals("CharUp")) {

    if (value) {

    up = true;

    } else {

    up = false;

    }

    } else if (binding.equals("CharDown")) {

    if (value) {

    down = true;

    } else {

    down = false;

    }

    } else if (binding.equals("CharSpace")) {

    character.jump();

    } else if (binding.equals("ShootRay")) {

    if(value){

    CollisionResults results = new CollisionResults();



    Vector2f click2d = inputManager.getCursorPosition();

    Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone();

    Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d).normalizeLocal();



    Ray ray = new Ray(click3d, dir);



    rootNode.collideWith(ray, results);

    if(results.size()>0){

    if (results.getCollision(0).getGeometry().getName().startsWith("terrain")) {

    targetChosen=true;

    targetLoc=results.getCollision(0).getContactPoint();

    newBox(targetLoc);

    }

    }

    }else{

    }

    }

    }



    public void collision(PhysicsCollisionEvent event) {

    }



    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {

    if (channel == shootingChannel) {

    channel.setAnim("stand");

    }

    }



    public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {

    }

    }

    [/java]



    So, am I doing something totally wrong here or what? Thanks!

@wezrule, I’m not quite sure that’s the problem (which is probably why you deleted your comment (is that what happened? lol)), because it disappears when it comes into contact with the batched model (that’s where I’m thinking the problem lies, although I’m confused as to why the CharacterControl can collide with it but the dynamic RigidBody can’t). It’s interesting though, because I don’t think it’s removed from the physics space or rootNode, it just… disappears. Like it got teleported.



But yeah, I probably have to add tpf into there somewhere :slight_smile: just didn’t think about doing that then, as I was just quickly writing a testcase, lol…

yeh i deleted it (a few hours ago) as soon as i wrote it :). I can’t remember what i said either, i didn’t have much time to look at it as I had 10 minutes to browse the forums in my lunch break

Oh, okay haha. Sorry, I didn’t know if it glitched or something (been on some forums where that happens)… lol, you suggested to put in tpf in the simpleUpdate part where I apply the impulse to the monkey head. But I would be very surprised if that was actually the problem, hehe. If you could look at it later when you’re free I would be very appreciative :smiley: and thanks for taking your precious lunch break to check up on the forums haha.

hehe will look at it now

yay! ^^ Thanks

hmmm something if definitely , strange, as sometimes when i put the object close to him i get:



[java]java.util.concurrent.ExecutionException: java.lang.ArrayIndexOutOfBoundsException: -1

at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)

at java.util.concurrent.FutureTask.get(FutureTask.java:83)

at com.jme3.bullet.BulletAppState.postRender(BulletAppState.java:193)

at com.jme3.app.state.AppStateManager.postRender(AppStateManager.java:280)

at com.jme3.app.SimpleApplication.update(SimpleApplication.java:253)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)

at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:184)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)

at java.lang.Thread.run(Thread.java:662)

Caused by: java.lang.ArrayIndexOutOfBoundsException: -1

at com.bulletphysics.collision.shapes.TriangleShape.localGetSupportingVertexWithoutMargin(TriangleShape.java:98)

at com.bulletphysics.collision.narrowphase.GjkPairDetector.getClosestPoints(GjkPairDetector.java:141)

at com.bulletphysics.collision.narrowphase.DiscreteCollisionDetectorInterface.getClosestPoints(DiscreteCollisionDetectorInterface.java:69)

at com.bulletphysics.collision.dispatch.ConvexConvexAlgorithm.processCollision(ConvexConvexAlgorithm.java:127)

at com.bulletphysics.collision.dispatch.ConvexTriangleCallback.processTriangle(ConvexTriangleCallback.java:163)

at com.bulletphysics.dom.HeightfieldTerrainShape.processAllTriangles(HeightfieldTerrainShape.java:212)

at com.bulletphysics.collision.dispatch.ConvexConcaveCollisionAlgorithm.processCollision(ConvexConcaveCollisionAlgorithm.java:86)

at com.bulletphysics.collision.dispatch.DefaultNearCallback.handleCollision(DefaultNearCallback.java:55)

at com.bulletphysics.collision.dispatch.CollisionDispatcher$CollisionPairCallback.processOverlap(CollisionDispatcher.java:236)

at com.bulletphysics.collision.broadphase.HashedOverlappingPairCache.processAllOverlappingPairs(HashedOverlappingPairCache.java:190)

at com.bulletphysics.collision.dispatch.CollisionDispatcher.dispatchAllCollisionPairs(CollisionDispatcher.java:247)

at com.bulletphysics.collision.dispatch.CollisionWorld.performDiscreteCollisionDetection(CollisionWorld.java:150)

at com.bulletphysics.dynamics.DiscreteDynamicsWorld.internalSingleStepSimulation(DiscreteDynamicsWorld.java:378)

at com.bulletphysics.dynamics.DiscreteDynamicsWorld.stepSimulation(DiscreteDynamicsWorld.java:339)

at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:324)

at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:311)

at com.jme3.bullet.BulletAppState$2.call(BulletAppState.java:98)

at com.jme3.bullet.BulletAppState$2.call(BulletAppState.java:95)

at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)

at java.util.concurrent.FutureTask.run(FutureTask.java:138)

at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:98)

at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:206)

at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)

[/java]



going to cook food now, as starving will investigate it more later

What mesh is that? The monkey head? And are you using the latest stable update? Seems like the mesh isn’t really revered by bullet…

The monkey head is a sphere. Ok found the problem, you cannot use setCollisionShape() either: to a mesh collision shape OR from a mesh collision shape. 2 Solutions which work include:



either replacing:



[java]boxRBC.setCollisionShape(CollisionShapeFactory.createMeshShape(boxNode));[/java]



with



[java]boxRBC = new RigidBodyControl(CollisionShapeFactory.createMeshShape(boxNode), 0);[/java]



Or using primitive shapes for both



[java]

boxRBC=new RigidBodyControl(CollisionShapeFactory.createMeshShape(boxNode), 0);



//and later on…



boxRBC.setCollisionShape(CollisionShapeFactory.createMeshShape(boxNode));

[/java]



i.e

[java]

boxRBC=new RigidBodyControl(new BoxCollisionShape(new Vector3f(0.5f, 0.5f, 0.5f)), 0);



boxRBC.setCollisionShape(new BoxCollisionShape(new Vector3f(2, 2, 2)));

[/java]



solves your problem. Using the latter approach you need to mess around to add a setPhysicsLocation() after setting the collision shape, and also mess around with some of the locations of the ninjas

2 Likes

The issue is that when you remove a PhysicsControl from a Spatial it loses its collision shape. This behavior is kind of intended because it can generate a new collision shape when added to a new spatial but I think I’ll change this. The only drawback is that the Control only auto-generates a collision shape from the first spatial its added to then.

Edit: Generally the RigidBodyControl.enabled() call is better to suspend operation of the Control, it automatically removes it from the physics space too.

1 Like

Awesome! It works! Thanks wezrule. So, would that create an eventual performance loss because you’re creating the boxRBC over and over again? (I’m talking about the first method) Or does it garbage collect it so quickly that it doesn’t really matter?

You can just save that collision shape as a variable if you want to. Or just setEnabled(false) the control which just removes it from the physics space and thus disables the physics on it. Like I explained the issue is that the collision shape is set to null, which I changed in svn now too.

2 Likes

Sorry normen, didn’t see your last two comments, haha :wink: So now all I have to do (once my SDK updates) is remove it from the physicsSpace (err, actually setEnabled(false) on the control), set the collision shape, then enable it again? Sounds nifty.

You just setEnabled(false), it will keep its collision shape then. Its only removed when the control is removed from its spatial.

Oh, okay. Cool cool, thanks!

Newbie here. I’m trying much the same thing as nomnom but can’t seem to get it working. I’ve downloaded the nightly build JME3 library as directed above.



In my collision method in my PhysicsCollisionListener, I’m trying to remove a single block from an item built of multiple blocks (think Minecraft, etc.). I’ve been using a CompoundCollisionShape in the hopes that I can just remove the single child shape (a block) from the compound instead of recreating the entire compound shape.





[java]shipABlockGeomHit.removeFromParent();

CollisionControl shipACollisionControl :((Node)spatialA).getControl(CollisionControl.class);

shipACollisionControl.setEnabled(false);

((CompoundCollisionShape)shipACollisionControl.collisionShape).removeChildShape(shipABlockHit.getCollisionShape());

shipACollisionControl.setEnabled(true);[/java]



I get messages that seem to confirm some success:



Aug 17, 2012 7:03:56 PM com.jme3.scene.Node detachChildAt

INFO: com.infinitearenas.ship.SimpleShip@2aa82afe (Node): Child removed.

Aug 17, 2012 7:03:56 PM com.jme3.bullet.PhysicsSpace removeRigidBody

INFO: Removing RigidBody 466660 from physics space.

Aug 17, 2012 7:03:56 PM com.jme3.bullet.PhysicsSpace addRigidBody

INFO: Adding RigidBody 4,613,728 to physics space.



But then in the graphics display, the debug wire frame for the collision shape still includes the collision boxes where I thought I had removed them from the CompoundCollisionShape. The geometry is gone (the once opaque block is gone) but the collision wire mesh remains.



Any pointers?