Zay-ES and Bullet physics best practices

Normen, I have spent several hours on trying to get a vehicle to work without controls (only using PhysicsVehicle), starting from a stable working situation with a vehicle that did use the control, but I can’t get it to work, although I think I am 99% there. The wheels are positioned correctly and react to steering commands (they turn) but collision detection does not work.

I am going back to my original approach for now, using controls and copying the spatial coordinates back to the ES, but if you are interested in the control-less version, I can post a link to the project here.

EDIT: link to test project here: https://dl.dropboxusercontent.com/u/21307972/ESPhysicsTest.zip

I am currently doing some tests sim-eth-es and your BulletPhysicsState. How did you create collision detection or workaround for terrain simulation

I have a factory class it creates ControlDrivers based on entity CollisionShape and PhysicsControl component.

for HeightMap based terrains i just use the HeightfieldCollisionShape as you can see below.

public class BulletDriverFactory extends AbstractGameSystem implements PhysicsDriverFactory{
    static Logger log = LoggerFactory.getLogger(BulletDriverFactory.class);

    private EntityData ed;

    private AssetManager assetManager;

    @Override
    protected void initialize() {
        log.info("onInitialize()");
        this.ed = getSystem(EntityData.class);
        if (ed == null) {
            throw new RuntimeException("PhysicsDriverFactory requires an EntityData object.");
        }

        assetManager = getSystem(AssetManager.class);
        if (assetManager == null) {
            throw new RuntimeException("PhysicsDriverFactory requires an AssetManager.");
        }

    }

    @Override
    public void start() {
        log.info("start()");
    }

    @Override
    public void stop() {
        log.info("stop()");
    }

    @Override
    protected void terminate() {
        assetManager.clearCache();
    }

    @Override
    public PhysicsControlDriver createDriver(EntityData ed, EntityId entityId) {
        PhysicDriver driverInfo = ed.getComponent(entityId, PhysicDriver.class);
        PhysicsControlDriver driver = null;
        switch (driverInfo.getPhysicsShape()) {

            case BulletDriverTypes.COLLISION_SHAPE_HEIGHTFIELD:
                TerrainQuad terrainQuad = TerrainCreator.createNonMatTerrainQuad(assetManager, ed, ed.getComponent(entityId, Terrain.class));

                switch (driverInfo.getPhysicsControl()) {
                    case BulletDriverTypes.CONTROL_RIGID_BODY:
                        log.info("Create physic driver:[CONTROL_RIGID_BODY, COLLISION_SHAPE_HEIGHTFIELD]");
                        driver = new RigidBodyDriver(new RigidBodyControl(new HeightfieldCollisionShape(terrainQuad.getHeightMap(), terrainQuad.getLocalScale()), 0));
                        break;
                }
                break;
            case BulletDriverTypes.COLLISION_SHAPE_BOX:
                switch (driverInfo.getPhysicsControl()) {
                    case BulletDriverTypes.CONTROL_RIGID_BODY:
                        log.info("Create physic driver:[CONTROL_RIGID_BODY, COLLISION_SHAPE_BOX]");
                        BoxShape shape = ed.getComponent(entityId, BoxShape.class);
                        BoxCollisionShape box = new BoxCollisionShape(shape.getHalfExtents());
                        MassProperties mass = ed.getComponent(entityId, MassProperties.class);
                        driver = new RigidBodyDriver(new RigidBodyControl(box, mass.getMass()));
                        break;
                }
                break;
            case BulletDriverTypes.COLLISION_SHAPE_CAPSULE:
                switch (driverInfo.getPhysicsControl()) {
                    case BulletDriverTypes.CONTROL_BETTER_CHARACTER:
                        log.info("Create physic driver:[CONTROL_BETTER_CHARACTER, COLLISION_SHAPE_CAPSULE]");
                        CapsuleShape shape = ed.getComponent(entityId, CapsuleShape.class);
                        MassProperties mass = ed.getComponent(entityId, MassProperties.class);           
                        driver = new BetterCharacterDriver(new PhysicsCharacterControl(shape.getRadius(), shape.getHeight(), mass.getMass()));
                        break;
                }
                break;
            case BulletDriverTypes.COLLISION_SHAPE_COMPOUND:
                break;
            case BulletDriverTypes.COLLISION_SHAPE_CONE:
                break;
            case BulletDriverTypes.COLLISION_SHAPE_CYLINDER:
                break;
            case BulletDriverTypes.COLLISION_SHAPE_GIMPACT:
                break;
            case BulletDriverTypes.COLLISION_SHAPE_HULL:
                break;
            case BulletDriverTypes.COLLISION_SHAPE_MESH:
                break;
            case BulletDriverTypes.COLLISION_SHAPE_PLANE:
                break;
            case BulletDriverTypes.COLLISION_SHAPE_SPHERE:
                switch (driverInfo.getPhysicsControl()) {
                    case BulletDriverTypes.CONTROL_RIGID_BODY:
                        driver = new PhysicShipDriver();
                        break;
                }
                break;

        }

        if (driver == null) {
            log.error("Unsuported Physics Driver !");
        }
        return driver;
    }
}

for example this my RigidBodyDriver which wraps a RigidBodyControl inside.
And then the mechanics work just like the SimplePhysicState from sim-eth-es example.

public class RigidBodyDriver implements PhysicsControlDriver {

    static Logger log = LoggerFactory.getLogger(RigidBodyDriver.class);

    private RigidBodyControl control;
    private Vector3f physicsLocation;
    private Quaternion physicsRotation;

    public RigidBodyDriver() {
    }

    public RigidBodyDriver(RigidBodyControl control) {
        this.control = control;
    }

    @Override
    public void addToPhysicSpace(PhysicsSpace physicsSpace) {
        physicsSpace.add(control);
    }

    @Override
    public void removeFromPhysicSpace(PhysicsSpace physicsSpace) {
        physicsSpace.remove(control);
    }

    @Override
    public void update(double stepTime, Body body) {
        // Grab local versions of the player settings in case another
        // thread sets them while we are calculating.
//        Quaternion rot = physicsRotation;
//        Vector3f loc = physicsLocation;
//        control.setPhysicsRotation(rot);
//        control.setPhysicsLocation(loc);

        // Setup the current world rotation of the body 
        Quaternion rot = control.getPhysicsRotation();

        body.orientation.set(rot.getX(), rot.getY(), rot.getZ(), rot.getW());
    
        // Apply the accelerated velocity oriented into world space 
        Vector3f temp = control.getPhysicsLocation();
        Vec3d pos = new Vec3d(temp.x, temp.y, temp.z);
        body.pos.set(pos);

        Vector3f temp2 = control.getLinearVelocity();
        Vec3d v = new Vec3d(temp2.x, temp2.y, temp2.z);
        body.velocity.set(v);
    }

    @Override
    public void updatePhysicComponents(EntityData ed, EntityId entityId) {
        log.info("updatePhysicComponents() :" + entityId.toString());
        MassProperties mass = ed.getComponent(entityId, MassProperties.class);
        Position pos = ed.getComponent(entityId, Position.class); 
        control.setMass(mass.getMass());
        control.setPhysicsLocation(pos.getLocation().toVector3f()); 
        
        PhysicsType physicType = ed.getComponent(entityId, PhysicsType.class);
        if(physicType.getPhysicType() == PhysicsType.KINEMATIC)
            control.setKinematic(true);
        else
            control.setKinematic(false);
    }

    public void applyMovementState(Vector3f location, Quaternion rotation) {
        this.physicsLocation = location;
        this.physicsRotation = rotation;
    }
    public RigidBodyControl getControl() {
        return control;
    }
}