Standalone Bullet Collision Detection

Hi Guys,

I could use some input on this one. I currently need a method to perform a Mesh / Capsule collision detection, using my own physics ( Basicly the capsule is controlled directly by a robotic arm ( a haptic device ) and the mesh will be static. I currently use a ray to define the robotic arm, but I need something “thicker”, a capsule would be perfect ).

There-for I am trying to separate the Bullet collision detection out from the bullet world. Has anyone done this before?

I currently have the below code, with two different methods of running the collision detection. But as you will see if you run it the collisions flicker a bit, sometimes triggering when there is no over lap ( seen in image ), and sometimes off when there is clear overlap. It is quite hard to debug since there is little to no javadoc, so I am hoping someone has a clue.

Note: Press to enable ‘bullet time’, to observe the flickering.
Note 2: I did not bother making an actual capsule mesh, so the spheres show the extremes of each capsule, the small spheres are the local collision points.


[java]
/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.
    */
    package mygame;

import com.bulletphysics.collision.broadphase.AxisSweep3;
import com.bulletphysics.collision.broadphase.CollisionAlgorithm;
import com.bulletphysics.collision.dispatch.CollisionDispatcher;
import com.bulletphysics.collision.dispatch.CollisionObject;
import com.bulletphysics.collision.dispatch.CollisionWorld;
import com.bulletphysics.collision.dispatch.DefaultCollisionConfiguration;
import com.bulletphysics.collision.dispatch.ManifoldResult;
import com.bulletphysics.collision.narrowphase.ManifoldPoint;
import com.bulletphysics.collision.narrowphase.PersistentManifold;
import com.bulletphysics.util.ObjectArrayList;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
import com.jme3.bullet.objects.PhysicsRigidBody;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.debug.Grid;
import com.jme3.scene.shape.Sphere;

public class CollisionInterfaceDemo extends SimpleApplication {

private CollisionWorld collisionWorld;
private javax.vecmath.Vector3f worldAabbMin;
private javax.vecmath.Vector3f worldAabbMax;

private CollisionObject objects[] = new CollisionObject[ 2 ];

private Node debugModel[] = new Node[ 2 ];

private PhysicsRigidBody body;

private Material capsuleMaterial;
private Material pointMaterial;

private boolean bulletTime = false;

@Override
public void simpleInitApp() {
    this.capsuleMaterial = new Material( this.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
    this.capsuleMaterial.setColor( "Color", ColorRGBA.Orange );
    this.capsuleMaterial.getAdditionalRenderState().setWireframe( true );

    this.pointMaterial = new Material( this.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
    this.pointMaterial.setColor( "Color", ColorRGBA.Red );
    
    this.initCollsionDetection();
    this.attachGrid( Vector3f.ZERO, 31, ColorRGBA.Yellow );

    this.attachGrid( Vector3f.ZERO, 31, ColorRGBA.Yellow );
    
    this.inputManager.addListener( new ActionListener(){

        public void onAction( String name, boolean isPressed, float tpf ) {
            bulletTime = isPressed;
        }
    }, new String[]{ "BulletTime" } );
    this.inputManager.addMapping("BulletTime", new KeyTrigger( KeyInput.KEY_SPACE ) );
}

private void attachGrid(Vector3f pos, int size, ColorRGBA color) {
    Geometry g = new Geometry("wireframe grid", new Grid( size, size, 1f ) );
    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mat.getAdditionalRenderState().setWireframe(true);
    mat.setColor("Color", color);
    g.setMaterial(mat);
    g.center().move(pos);
    rootNode.attachChild( g );
}    

float time = 0;

@Override
public void simpleUpdate( float tpf ) {
    time += bulletTime ? 0.01f * tpf : tpf;
    
    time = time > FastMath.DEG_TO_RAD * 50 ? -FastMath.DEG_TO_RAD * 50 : time;
    
    Vector3f position = new Vector3f( 3 * FastMath.sin( time ), FastMath.abs( 3 * FastMath.sin( 10f * time ) ), 5 * FastMath.cos( time ) - 5 + 0.3f );
    body.setPhysicsLocation( position );
    this.debugModel[ 0 ].setLocalTranslation( position );
    boolean collision = this.collide();
    if( collision ){
        capsuleMaterial.setColor("Color", ColorRGBA.Red );
    } else {
        capsuleMaterial.setColor("Color", ColorRGBA.Green );
    }
}


private void initCollsionDetection()
{

    DefaultCollisionConfiguration collisionConfiguration = new DefaultCollisionConfiguration();
    CollisionDispatcher dispatcher = new CollisionDispatcher( collisionConfiguration );
    worldAabbMin = new javax.vecmath.Vector3f(-1000,-1000,-1000);
    worldAabbMax = new javax.vecmath.Vector3f(1000,1000,1000);

    AxisSweep3 broadphase = new AxisSweep3( worldAabbMin, worldAabbMax );

    collisionWorld = new CollisionWorld( dispatcher, broadphase, collisionConfiguration );

    float height = 2f;
    float radius = 0.5f;
    debugModel[ 0 ] = this.createCapsuleModel( height, radius );
    debugModel[ 1 ] = this.createCapsuleModel( height, radius );

    this.getRootNode().attachChild( this.debugModel[ 0 ] );
    this.getRootNode().attachChild( this.debugModel[ 1 ] );

    body = new PhysicsRigidBody( this.createCapsule( new Vector3f( 0, 0, 0 ), height, radius ), 5f );
    objects[ 0 ] = body.getObjectId();
    objects[ 1 ] = new PhysicsRigidBody( this.createCapsule( new Vector3f(), height, radius ), 5f ).getObjectId();
    collisionWorld.addCollisionObject( objects[ 0 ] );
    collisionWorld.addCollisionObject( objects[ 1 ] );
        
}

/**
 * Create a visual model for the two capsules
 * @param height
 * @param radius
 * @return 
 */
private Node createCapsuleModel( float height, float radius ){
    Node n = new Node();
    float offset = height / 2f - radius;

    Geometry s1 = new Geometry( "Hi-Sphere", new Sphere( 20, 20, radius ) );
    s1.setLocalTranslation( 0, offset, 0 );
    s1.setMaterial( this.capsuleMaterial );
    n.attachChild( s1 );
    
    Geometry collisionPoint = new Geometry( "CollisionPoint", new Sphere( 20, 20, 0.1f ) );
    collisionPoint.setLocalTranslation( 0, offset, 0 );
    collisionPoint.setMaterial( this.pointMaterial );
    n.attachChild( collisionPoint );
    
    Geometry s2 = new Geometry( "Lo-Sphere", new Sphere( 20, 20, radius ) );
    s2.setLocalTranslation( 0, -offset, 0 );
    s2.setMaterial( this.capsuleMaterial );
    n.attachChild( s2 );
    
    return n;
}

/**
 * Setup a collision capsule
 * @param location
 * @param height
 * @param radius
 * @return 
 */
protected CollisionShape createCapsule( Vector3f location, float height, float radius ) {
    CapsuleCollisionShape capsuleCollisionShape = new CapsuleCollisionShape( radius, height );
    CompoundCollisionShape compoundCollisionShape = new CompoundCollisionShape();
    compoundCollisionShape.addChildShape( capsuleCollisionShape, location );
    return compoundCollisionShape;
}  

/**
 * Classic collision detection, colliding the entire 'world'
 * @return 
 */
private boolean collideClassic(){
    boolean collision = false;
    int numManifolds = collisionWorld.getDispatcher().getNumManifolds();
    for ( int i = 0; i  0;

        for (int j=0;j<numContacts;j++)
        {
            ManifoldPoint pt = contactManifold.getContactPoint( i );
            System.out.println("At: " + pt );

        }

    }        
    return collision;
}

/**
 * Direct collision detection, specifying which two objects to check collision on.
 * @return 
 */
private boolean collideDirect(){
    boolean collision = false;
    CollisionAlgorithm algo = collisionWorld.getDispatcher().findAlgorithm( objects[0], objects[1] );
    ManifoldResult contactPointResult = new ManifoldResult( objects[0], objects[1] );
    algo.processCollision( objects[0], objects[1], collisionWorld.getDispatchInfo(), contactPointResult );

    ObjectArrayList manifoldArray = new ObjectArrayList();
    algo.getAllContactManifolds( manifoldArray );

    int numManifolds = manifoldArray.size();
    for ( int i = 0; i  0;

        for ( int j = 0; j < numContacts; j++ )
        {
                ManifoldPoint pt = contactManifold.getContactPoint( j );

                javax.vecmath.Vector3f ptA = swap ? pt.getPositionWorldOnA( new javax.vecmath.Vector3f() ) : pt.getPositionWorldOnB( new javax.vecmath.Vector3f() );
                javax.vecmath.Vector3f ptB = swap ? pt.getPositionWorldOnB( new javax.vecmath.Vector3f() ) : pt.getPositionWorldOnA( new javax.vecmath.Vector3f() );

                this.debugModel[ 0 ].getChild( "CollisionPoint" ).setLocalTranslation( ptA.x, ptA.x, ptA.z );
                this.debugModel[ 1 ].getChild( "CollisionPoint" ).setLocalTranslation( ptB.x, ptB.x, ptB.z );

        }

        contactManifold.clearManifold();     
    }
    return collision;
}

/**
 * Performs collision detection, with a choice of two methods
 * @return 
 */
private boolean collide() {
    
    collisionWorld.performDiscreteCollisionDetection();

    /**
     * If TRUE: use the collision dispatcher
     *   FALSE: process collisions directly between objects
     */
    boolean METHOD = false;

    if( METHOD ){
        return this.collideClassic();
    }
    else {
        return this.collideDirect();
    }
}

public static void main( String args[] ){
    CollisionInterfaceDemo demo = new CollisionInterfaceDemo();
    demo.start();
}

}
[/java]

Just use the PhysicsRigidBody etc. and the PhysicsSpace separately, if you import any com.bulletphysics.* class you do it wrong.
The content of this post is meant to be read as a straight information or question without an implicit dismissive stance or interest in having the other party feel offended unless there’s emotes that hint otherwise or there’s an increased use of exclamation marks and all-capital words.