GhostObject overlapping Objects support for jBullet-jme3

Hey, since I needed it, I implemented the overlapping feature, so that you can retrieve all overlapping objects from a GhostObject, like in jBullet itself.



three new methods:

List<CollisionObjet> getOverlappingObjects();

int getOverlappingCount();

CollisionObject getOverlapping(int index);



just like in jBullet itself, really! :smiley:



i've also added a Test class for demonstrating GhostObject:

TestGhostObject.java in jme3test.bullet.



So patches:

PhysicsSpace:


Index: PhysicsSpace.java
--- PhysicsSpace.java Base (BASE)
+++ PhysicsSpace.java Locally Modified (Based On LOCAL)
@@ -71,6 +71,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -276,10 +277,23 @@
         for (PhysicsNode physicsNode : physicsNodes.values()) {
             physicsNode.updatePhysicsState();
         }
-        //sync ghostnodes TODO!
-        for (PhysicsGhostNode node : physicsGhostNodes.values()) {
+
+        //sync ghostnodes, with overlapping Objects.
+        for (Entry<GhostObject, PhysicsGhostNode> entry : physicsGhostNodes.entrySet()) {
+            PhysicsGhostNode node = entry.getValue();
+            List<CollisionObject> overlappingObjs = node.getOverlappingObjects();
+            overlappingObjs.clear(); // <-- clear from old values.
+            for (com.bulletphysics.collision.dispatch.CollisionObject collObj : entry.getKey().getOverlappingPairs()) {
+                if(collObj instanceof GhostObject)
+                    overlappingObjs.add(physicsGhostNodes.get(collObj));
+                else if(collObj instanceof RigidBody)
+                    overlappingObjs.add(physicsNodes.get(collObj));
+            }
+
+            // finally update
             node.updatePhysicsState();
         }
+        
         //step simulation
         getDynamicsWorld().stepSimulation(time, maxSteps, accuracy);
 



PhyisicsGhostNode:

Index: PhysicsGhostNode.java
--- PhysicsGhostNode.java Base (BASE)
+++ PhysicsGhostNode.java Locally Modified (Based On LOCAL)
@@ -43,6 +43,8 @@
 import com.jme3.bullet.collision.shapes.CollisionShape;
 import com.jme3.bullet.collision.shapes.SphereCollisionShape;
 import com.jme3.bullet.util.Converter;
+import java.util.LinkedList;
+import java.util.List;
 
 /**
  * <i>From Bullet manual:</i><br>
@@ -63,6 +65,9 @@
     protected com.jme3.math.Transform jmeTrans = new com.jme3.math.Transform();
     protected javax.vecmath.Quat4f tempRot = new javax.vecmath.Quat4f();
 
+    // Linked list should be fine, because it won't grow big and Arraylist would acquire a new array each update
+    private List<CollisionObject> overlappingObjects = new LinkedList<CollisionObject>();
+
     public PhysicsGhostNode() {
         cShape = new SphereCollisionShape(0.5f);
         buildObject();
@@ -191,4 +196,31 @@
      */
     public void destroy() {
     }
+
+    /**
+     * Another Object is overlapping with this GhostNode,
+     * if and if only there CollisionShapes overlaps.
+     * They could be both regular PhysicsNodes or PhysicsGhostNode.
+     * @return All CollisionObjects overlapping with this GhostNode.
+     */
+    public List<CollisionObject> getOverlappingObjects() {
+        return overlappingObjects;
 }
+
+    /**
+     *
+     * @return With how many other CollisionObjects this GhostNode is currently overlapping.
+     */
+    public int getOverlappingCount() {
+        return overlappingObjects.size();
+    }
+
+    /**
+     *
+     * @param index The index of the overlapping Node to retrieve.
+     * @return The Overlapping CollisionObject at the given index.
+     */
+    public CollisionObject getOverlapping(int index) {
+        return overlappingObjects.get(index);
+    }
+}



and new class:

package jme3test.bullet;

import com.jme3.app.Application;
import com.jme3.app.SimpleBulletApplication;
import com.jme3.asset.TextureKey;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.nodes.PhysicsGhostNode;
import com.jme3.bullet.nodes.PhysicsNode;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.texture.Texture;
import java.util.logging.Logger;

/**
 *
 * @author tim8dev [at] gmail [dot com]
 */
public class TestGhostObject extends SimpleBulletApplication {

    private PhysicsGhostNode ghostNode;

    public static void main(String[] args) {
        Application app = new TestGhostObject();
        app.start();
    }

    private void initGhostObject() {
        Vector3f halfExtents = new Vector3f(3, 4.2f, 1);
        // create an visual Box to see, where the GhostObject is:
        Box boxGeom = new Box(Vector3f.ZERO,
                halfExtents.x, halfExtents.y, halfExtents.z);
        Geometry ghostGeom = new Geometry("ghostVisual", boxGeom);
        ghostGeom.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
        ghostNode = new PhysicsGhostNode(ghostGeom, new BoxCollisionShape(halfExtents));
        rootNode.attachChild(ghostNode);
        getPhysicsSpace().add(ghostNode);
    }

    @Override
    public void simpleInitApp() {

        Material mat = new Material(assetManager, "Common/MatDefs/Misc/SimpleTextured.j3md");
        TextureKey key = new TextureKey("Interface/Logo/Monkey.jpg", true);
        key.setGenerateMips(true);
        Texture tex = assetManager.loadTexture(key);
        tex.setMinFilter(Texture.MinFilter.Trilinear);
        mat.setTexture("m_ColorMap", tex);

        // Mesh to be shared across several boxes.
        Box boxGeom = new Box(Vector3f.ZERO, 1f, 1f, 1f);
        // CollisionShape to be shared across several boxes.
        CollisionShape shape = new BoxCollisionShape(new Vector3f(1, 1, 1));

        // Add some phyisics boxes higher up, to fall down and be tracked.
        Geometry geom0 = new Geometry("box", boxGeom);
        geom0.setMaterial(mat);
        PhysicsNode physicsBox = new PhysicsNode(geom0, new BoxCollisionShape(new Vector3f(1, 1, 1)), 1);
        physicsBox.setName("box0");
        physicsBox.setFriction(0.1f);
        physicsBox.setLocalTranslation(new Vector3f(.6f, 4, .5f));
        physicsBox.updateGeometricState();
        physicsBox.updateModelBound();
        rootNode.attachChild(physicsBox);
        getPhysicsSpace().add(physicsBox);

        Geometry geom1 = new Geometry("box", boxGeom);
        geom1.setMaterial(mat);
        PhysicsNode physicsBox1 = new PhysicsNode(geom1, shape, 1);
        physicsBox1.setName("box1");
        physicsBox1.setFriction(0.1f);
        physicsBox1.setLocalTranslation(new Vector3f(0, 40, 0));
        physicsBox1.updateGeometricState();
        physicsBox1.updateModelBound();
        rootNode.attachChild(physicsBox1);
        getPhysicsSpace().add(physicsBox1);

        Geometry geom2 = new Geometry("box", boxGeom);
        geom2.setMaterial(mat);
        PhysicsNode physicsBox2 = new PhysicsNode(geom2, new BoxCollisionShape(new Vector3f(1, 1, 1)), 1);
        physicsBox2.setName("box0");
        physicsBox2.setFriction(0.1f);
        physicsBox2.setLocalTranslation(new Vector3f(.5f, 80, -.8f));
        physicsBox2.updateGeometricState();
        physicsBox2.updateModelBound();
        rootNode.attachChild(physicsBox2);
        getPhysicsSpace().add(physicsBox2);

        // the floor, does not move (mass=0)
        Geometry geom3 = new Geometry("box2", new Box(Vector3f.ZERO, 100f, 1f, 100f));
        geom3.setMaterial(mat);
        geom3.updateGeometricState();
        PhysicsNode node = new PhysicsNode(geom3, new BoxCollisionShape(new Vector3f(100, 1, 100)), 0);
        node.setName("floor");
        node.setLocalTranslation(new Vector3f(0f, -6, 0f));
        rootNode.attachChild(node);
        node.updateModelBound();
        node.updateGeometricState();
        getPhysicsSpace().add(node);

        initGhostObject();
    }

    @Override
    public void simpleUpdate(float tpf) {
        Logger.getLogger(TestGhostObject.class.getName()).
                info(ghostNode.getOverlappingObjects().toString());
    }
}


Nice! I like how you used the existing jbullet-jme objects instead of using the jbullet objects! Gotta put this in some time.



Cheers,

Normen

This would be nice to have also in JME2! :slight_smile:

normen said:

tim8dev said:

This would be job of the jBullet-jME maintainers ;)

Uh, that would also be me ;)


I just didn't want to be that direct :P
tim8dev said:

This would be job of the jBullet-jME maintainers ;)

Uh, that would also be me ;)

This would be job of the jBullet-jME maintainers :wink:

added to the jme3 version

normen said:

added to the jme3 version


I've founded a performance bottleneck, if there are many overlapping Objects, a LinkedList would generate many Instances of LinkedList.Entry each frame..

Maybe an ArrayList would be more appropriate, if you get back to it, somewhen in the future.