[SOLVED] Only the last BulletAppState’s PhysicsCollisionListeners work with multiple BulletAppStates

I think there are 2 bugs which i have found:

  • Only the PhysicsCollisionListeners are called of the last BulletAppState attached to the AppStateManager
  • collisions detected across all the BulletAppStates are detected in the last BulletAppState’s PhysicsCollisionListeners



    Heres a testcase of it, each BulletAppState has a square with a sphere collision shape inside. The order of the attached BulletAppStates dictates which PhysicsCollisionListener is called (always the last one)



    TestMultipleBulletPhysicsCollisions.java

    [java]package test;



    import com.jme3.app.SimpleApplication;

    import com.jme3.bullet.BulletAppState;

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

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

    import com.jme3.bullet.control.GhostControl;

    import com.jme3.math.Vector3f;

    import com.jme3.scene.Node;



    public class TestMultipleBulletPhysicsCollisions extends SimpleApplication {



    private BulletAppState bulletAppState1;

    private BulletAppState bulletAppState2;

    private BulletAppState bulletAppState3;

    private static TestMultipleBulletPhysicsCollisions thisApp;



    public static void main(String args[]) {

    new TestMultipleBulletPhysicsCollisions().start();

    }



    @Override

    public void simpleInitApp() {



    thisApp = this;



    bulletAppState1 = new BulletAppState();

    bulletAppState2 = new BulletAppState();

    bulletAppState3 = new BulletAppState();



    stateManager.attach(bulletAppState1);

    stateManager.attach(bulletAppState2);

    stateManager.attach(bulletAppState3);



    bulletAppState1.getPhysicsSpace().enableDebug(assetManager);

    bulletAppState2.getPhysicsSpace().enableDebug(assetManager);

    bulletAppState3.getPhysicsSpace().enableDebug(assetManager);



    Node box1 = new Node(“box1”);

    box1.setLocalTranslation(new Vector3f (-2.5f,0,0));

    BoxCollisionShape boxCollisionShape1 = new BoxCollisionShape(new Vector3f(1, 1, 1));

    box1.addControl(new GhostControl(boxCollisionShape1));

    bulletAppState1.getPhysicsSpace().add(box1);



    Node sphere1 = new Node(“sphere1”);

    sphere1.setLocalTranslation(new Vector3f (-2.5f,0,0));

    SphereCollisionShape sphereCollisionShape1 = new SphereCollisionShape(0.5f);

    sphere1.addControl(new GhostControl(sphereCollisionShape1));

    bulletAppState1.getPhysicsSpace().add(sphere1);



    rootNode.attachChild(box1);

    rootNode.attachChild(sphere1);

    rootNode.addControl(new PhysicsCollisionControl(“one”));



    Node box2 = new Node(“box2”);

    BoxCollisionShape boxCollisionShape2 = new BoxCollisionShape(new Vector3f(1, 1, 1));

    box2.addControl(new GhostControl(boxCollisionShape2));

    bulletAppState2.getPhysicsSpace().add(box2);



    Node sphere2 = new Node(“sphere2”);

    SphereCollisionShape sphereCollisionShape2 = new SphereCollisionShape(0.5f);

    sphere2.addControl(new GhostControl(sphereCollisionShape2));

    bulletAppState2.getPhysicsSpace().add(sphere2);



    rootNode.attachChild(box2);

    rootNode.attachChild(sphere2);

    rootNode.addControl(new PhysicsCollisionControl(“two”));



    Node box3 = new Node(“box3”);

    box3.move(new Vector3f(2.5f, 0, 0));

    BoxCollisionShape boxCollisionShape3 = new BoxCollisionShape(new Vector3f(1, 1, 1));

    box3.addControl(new GhostControl(boxCollisionShape3));

    bulletAppState3.getPhysicsSpace().add(box3);



    Node sphere3 = new Node(“sphere3”);

    sphere3.move(new Vector3f(2.5f, 0, 0));

    SphereCollisionShape sphereCollisionShape3 = new SphereCollisionShape(0.5f);

    sphere3.addControl(new GhostControl(sphereCollisionShape3));

    bulletAppState3.getPhysicsSpace().add(sphere3);



    rootNode.attachChild(box3);

    rootNode.attachChild(sphere3);

    rootNode.addControl(new PhysicsCollisionControl(“three”));



    }



    public static TestMultipleBulletPhysicsCollisions getApp() {

    return thisApp;

    }



    @Override

    public void simpleUpdate(float tpf) {

    }



    public BulletAppState getBulletAppState1() {

    return bulletAppState1;

    }



    public BulletAppState getBulletAppState2() {

    return bulletAppState2;

    }



    public BulletAppState getBulletAppState3() {

    return bulletAppState3;

    }

    }



    [/java]



    PhysicsCollisionControl.java



    [java]

    package test;



    import com.jme3.bullet.collision.PhysicsCollisionEvent;

    import com.jme3.bullet.collision.PhysicsCollisionListener;

    import com.jme3.renderer.RenderManager;

    import com.jme3.renderer.ViewPort;

    import com.jme3.scene.Spatial;

    import com.jme3.scene.control.AbstractControl;

    import com.jme3.scene.control.Control;



    class PhysicsCollisionControl extends AbstractControl implements PhysicsCollisionListener {



    private String number;

    private TestMultipleBulletPhysicsCollisions myApp = TestMultipleBulletPhysicsCollisions.getApp();



    public PhysicsCollisionControl(String number) {

    this.number = number;



    if(number.equals(“one”)) {

    myApp.getBulletAppState1().getPhysicsSpace().addCollisionListener(this);

    } else if (number.equals(“two”)) {

    myApp.getBulletAppState2().getPhysicsSpace().addCollisionListener(this);

    } else {

    myApp.getBulletAppState3().getPhysicsSpace().addCollisionListener(this);

    }

    }



    @Override

    protected void controlUpdate(float tpf) {

    }



    @Override

    protected void controlRender(RenderManager rm, ViewPort vp) {

    }



    public Control cloneForSpatial(Spatial spatial) {

    throw new UnsupportedOperationException(“Not supported yet.”);

    }



    public void collision(PhysicsCollisionEvent event) {

    System.out.println(number);

    System.out.println(event.getNodeA() + " " + event.getNodeB());

    }

    }

    [/java]



    http://i.imgur.com/8YZUG.png



    This image shows that all collision events detected are called in the last PhysicsCollisionListener, as shown by “three”

    http://i.imgur.com/Ubws4.png
1 Like

Thanks. This is due to bullet doing the callbacks to a threadlocal listener… Can you try adding the listeners using he enqueue method of physicsspace and a callable?

1 Like

i will try :stuck_out_tongue: but knowing me i will screw it up

i tried this (no idea if its right), but nothing gets called now



[java]

package test;



import com.jme3.bullet.collision.PhysicsCollisionEvent;

import com.jme3.bullet.collision.PhysicsCollisionListener;

import com.jme3.renderer.RenderManager;

import com.jme3.renderer.ViewPort;

import com.jme3.scene.Spatial;

import com.jme3.scene.control.AbstractControl;

import com.jme3.scene.control.Control;

import java.util.concurrent.Callable;



class PhysicsCollisionControl extends AbstractControl implements PhysicsCollisionListener {



private String number;

private TestMultipleBulletPhysicsCollisions myApp = TestMultipleBulletPhysicsCollisions.getApp();

private PhysicsCollisionListener listener = this;



public PhysicsCollisionControl(String number) {

this.number = number;



if (number.equals("one")) {

myApp.getBulletAppState1().getPhysicsSpace().enqueue(new Callable<Void>() {



public Void call() throws Exception {

myApp.getBulletAppState1().getPhysicsSpace().addCollisionListener(listener);

return null;

}

});

} else if (number.equals("two")) {

myApp.getBulletAppState2().getPhysicsSpace().enqueue(new Callable<Void>() {



public Void call() throws Exception {

myApp.getBulletAppState2().getPhysicsSpace().addCollisionListener(listener);

return null;

}

});

} else {

myApp.getBulletAppState3().getPhysicsSpace().enqueue(new Callable<Void>() {



public Void call() throws Exception {

myApp.getBulletAppState3().getPhysicsSpace().addCollisionListener(listener);

return null;

}

});

}

}



@Override

protected void controlUpdate(float tpf) {

}



@Override

protected void controlRender(RenderManager rm, ViewPort vp) {

}



public Control cloneForSpatial(Spatial spatial) {

throw new UnsupportedOperationException("Not supported yet.");

}



public void collision(PhysicsCollisionEvent event) {

System.out.println(number);

System.out.println(event.getNodeA() + " " + event.getNodeB());

}

}

[/java]

Okay, thanks. I’ll look into this. Basically you have to register each listener on the thread its supposed to listen on… And they all have to listen on separate threads… Does it work when you enable parallel multithreading?

1 Like

i removed the callable, and added the bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL); to all the bulletAppStates and, yeh that fixed it :), thanks!

Okay good, basically I guess this will be the only way it can work anyway… But it also only makes sense having multiple physics spaces in a parallel setup.

1 Like

ah ok sorry, thanks for the help! :), for completeness heres the working classes.



[java]

package test;



import com.jme3.app.SimpleApplication;

import com.jme3.bullet.BulletAppState;

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

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

import com.jme3.bullet.control.GhostControl;

import com.jme3.math.Vector3f;

import com.jme3.scene.Node;



public class TestMultipleBulletPhysicsCollisions extends SimpleApplication {



private BulletAppState bulletAppState1;

private BulletAppState bulletAppState2;

private BulletAppState bulletAppState3;

private static TestMultipleBulletPhysicsCollisions thisApp;



public static void main(String args[]) {

new TestMultipleBulletPhysicsCollisions().start();

}



@Override

public void simpleInitApp() {



thisApp = this;



bulletAppState1 = new BulletAppState();

bulletAppState1.setThreadingType(BulletAppState.ThreadingType.PARALLEL);

bulletAppState2 = new BulletAppState();

bulletAppState2.setThreadingType(BulletAppState.ThreadingType.PARALLEL);

bulletAppState3 = new BulletAppState();

bulletAppState3.setThreadingType(BulletAppState.ThreadingType.PARALLEL);



stateManager.attach(bulletAppState1);

stateManager.attach(bulletAppState2);

stateManager.attach(bulletAppState3);



bulletAppState1.getPhysicsSpace().enableDebug(assetManager);

bulletAppState2.getPhysicsSpace().enableDebug(assetManager);

bulletAppState3.getPhysicsSpace().enableDebug(assetManager);



Node box1 = new Node(“box1”);

box1.setLocalTranslation(new Vector3f (-2.5f,0,0));

BoxCollisionShape boxCollisionShape1 = new BoxCollisionShape(new Vector3f(1, 1, 1));

box1.addControl(new GhostControl(boxCollisionShape1));

bulletAppState1.getPhysicsSpace().add(box1);



Node sphere1 = new Node(“sphere1”);

sphere1.setLocalTranslation(new Vector3f (-2.5f,0,0));

SphereCollisionShape sphereCollisionShape1 = new SphereCollisionShape(0.5f);

sphere1.addControl(new GhostControl(sphereCollisionShape1));

bulletAppState1.getPhysicsSpace().add(sphere1);



rootNode.attachChild(box1);

rootNode.attachChild(sphere1);

rootNode.addControl(new PhysicsCollisionControl(“one”));



Node box2 = new Node(“box2”);

BoxCollisionShape boxCollisionShape2 = new BoxCollisionShape(new Vector3f(1, 1, 1));

box2.addControl(new GhostControl(boxCollisionShape2));

bulletAppState2.getPhysicsSpace().add(box2);



Node sphere2 = new Node(“sphere2”);

SphereCollisionShape sphereCollisionShape2 = new SphereCollisionShape(0.5f);

sphere2.addControl(new GhostControl(sphereCollisionShape2));

bulletAppState2.getPhysicsSpace().add(sphere2);



rootNode.attachChild(box2);

rootNode.attachChild(sphere2);

rootNode.addControl(new PhysicsCollisionControl(“two”));



Node box3 = new Node(“box3”);

box3.move(new Vector3f(2.5f, 0, 0));

BoxCollisionShape boxCollisionShape3 = new BoxCollisionShape(new Vector3f(1, 1, 1));

box3.addControl(new GhostControl(boxCollisionShape3));

bulletAppState3.getPhysicsSpace().add(box3);



Node sphere3 = new Node(“sphere3”);

sphere3.move(new Vector3f(2.5f, 0, 0));

SphereCollisionShape sphereCollisionShape3 = new SphereCollisionShape(0.5f);

sphere3.addControl(new GhostControl(sphereCollisionShape3));

bulletAppState3.getPhysicsSpace().add(sphere3);



rootNode.attachChild(box3);

rootNode.attachChild(sphere3);

rootNode.addControl(new PhysicsCollisionControl(“three”));



}



public static TestMultipleBulletPhysicsCollisions getApp() {

return thisApp;

}



@Override

public void simpleUpdate(float tpf) {

}



public BulletAppState getBulletAppState1() {

return bulletAppState1;

}



public BulletAppState getBulletAppState2() {

return bulletAppState2;

}



public BulletAppState getBulletAppState3() {

return bulletAppState3;

}

} [/java]



[java]

package test;



import com.jme3.bullet.collision.PhysicsCollisionEvent;

import com.jme3.bullet.collision.PhysicsCollisionListener;

import com.jme3.renderer.RenderManager;

import com.jme3.renderer.ViewPort;

import com.jme3.scene.Spatial;

import com.jme3.scene.control.AbstractControl;

import com.jme3.scene.control.Control;



class PhysicsCollisionControl extends AbstractControl implements PhysicsCollisionListener {



private String number;

private TestMultipleBulletPhysicsCollisions myApp = TestMultipleBulletPhysicsCollisions.getApp();

private PhysicsCollisionListener listener = this;



public PhysicsCollisionControl(String number) {

this.number = number;



if (number.equals(“one”)) {

myApp.getBulletAppState1().getPhysicsSpace().addCollisionListener(listener);



} else if (number.equals(“two”)) {

myApp.getBulletAppState2().getPhysicsSpace().addCollisionListener(listener);



} else {

myApp.getBulletAppState3().getPhysicsSpace().addCollisionListener(listener);

}

}



@Override

protected void controlUpdate(float tpf) {

}



@Override

protected void controlRender(RenderManager rm, ViewPort vp) {

}



public Control cloneForSpatial(Spatial spatial) {

throw new UnsupportedOperationException(“Not supported yet.”);

}



public void collision(PhysicsCollisionEvent event) {

System.out.println(number);

System.out.println(event.getNodeA() + " " + event.getNodeB());

}

}

[/java]



http://i.imgur.com/xdcd3.png

Beautiful thread !