Bullet Vehicle problems

I have been trying to get the FancyCar physics/bullet example working, replacing the Ferrari with a different, relatively low-poly, model, but having trouble.

I scaled my own model to roughly about the same size as the original Ferrari, but the wheels tend to either go straight through the floor (with the car body resting on the floor), or the wheels get incredibly bouncy (bouncing through the bonnet) and the car slowly drifts of to the horizon uncontrollably.

I have been trying different values for suspension etc. for hours, but maybe the problem is somewhere else entirely. Anyway, I am lost.

For those interested, replacing/adding these files to the bullet fancy-car example should be enough to test:

model

code

Could the problem be with the wheel design? This is the wireframe of the wheels of the vehicle:

Or is there something wrong with my code. This is what I have so far:

private void buildPlayer() {
    float stiffness = 60.0f;//200=f1 car
    float compValue = 0.2f; //(lower than damp!)
    float dampValue = 0.3f;
    final float mass = 400;

    //Load model and get chassis Geometry
    carNode = (Node)assetManager.loadModel("Models/Cars/Coupe_Green.j3o");
    carNode.setShadowMode(ShadowMode.Cast);
    Geometry main = findGeometry(carNode, "main");
    
    //Create a hull collision shape for the chassis
    CollisionShape carHull = CollisionShapeFactory.createDynamicMeshShape(main);

    //Create a vehicle control
    player = new VehicleControl(carHull, mass);
    carNode.addControl(player);

    //Setting default values for wheels
    player.setSuspensionCompression(compValue * 2.0f * FastMath.sqrt(stiffness));
    player.setSuspensionDamping(dampValue * 2.0f * FastMath.sqrt(stiffness));
    player.setSuspensionStiffness(stiffness);
    player.setMaxSuspensionForce(10000);

    //Create four wheels and add them at their locations
    //note that our fancy car actually goes backwards..
    Vector3f wheelDirection = new Vector3f(0, -1, 0);
    Vector3f wheelAxle = new Vector3f(-1, 0, 0);
    SetupWheel(carNode, "fl", true, wheelDirection, wheelAxle);
    SetupWheel(carNode, "fr", true, wheelDirection, wheelAxle);
    SetupWheel(carNode, "rl", false, wheelDirection, wheelAxle);
    SetupWheel(carNode, "rr", false, wheelDirection, wheelAxle);

    player.getWheel(2).setFrictionSlip(4);
    player.getWheel(3).setFrictionSlip(4);

    rootNode.attachChild(carNode);
    getPhysicsSpace().add(player);
}

private void SetupWheel(Spatial carNode, String suffix, boolean isFrontWheel, Vector3f wheelDirection, Vector3f wheelAxle) {
    Spatial wheel = findSpatial(carNode, "wheel_" + suffix);
    BoundingBox box = (BoundingBox)wheel.getWorldBound();
    float radius = box.getYExtent() / 2.0f;  
    float suspensionLength = radius * 1.1f;
    Vector3f mountPoint = box.getCenter().add(0, -suspensionLength, 0);
    
    Node node = (Node) wheel;        
    for (int i = 0; i < node.getQuantity(); i++) {
        Spatial child = node.getChild(i);
        if (child instanceof Geometry) {
            child.center();
        }
    }        
    
    player.addWheel(wheel, mountPoint, wheelDirection, wheelAxle, 
                    suspensionLength, radius, isFrontWheel);
}

public static Spatial findSpatial(Spatial spatial, String name) {
    if (spatial instanceof Node) {
        Node node = (Node) spatial;
        for (int i = 0; i < node.getQuantity(); i++) {
            Spatial child = node.getChild(i);
            if (child.getName().startsWith(name)) {
                return child;
            }
            else {
                Spatial result = findSpatial(child, name);
                if (result != null) {
                    return result;
                }
            }
        }
    }
    return null;
}

public static Geometry findGeometry(Spatial spatial, String name) {
    if (spatial instanceof Node) {
        Node node = (Node) spatial;
        for (int i = 0; i < node.getQuantity(); i++) {
            Spatial child = node.getChild(i);
            Geometry result = findGeometry(child, name);
            if (result != null) {
                return result;
            }
        }
    } else if (spatial instanceof Geometry) {
        if (spatial.getName().startsWith(name)) {
            return (Geometry) spatial;
        }
    }
    return null;
}

It looks pretty solid to me, but maybe I am missing something.

Thanks for any help,

Martin

Note: the model looks fine in the SceneComposer, but the texture or lighting is completely of in the game itself, but that is an other issue I suppose.

1 Like

OK, so I corrected a few things that were off, I think.

First, the wheels were parented to the car body, so I fixed that. This is the new blend file:
https://dl.dropboxusercontent.com/u/21307972/Coupe_Green.blend

Also, I learned that my suspension rest length was probably way too long. This is how I setup the wheels now:

private VehicleWheel SetupWheel(Spatial carNode, String suffix, boolean isFrontWheel) {
    Spatial wheelSpatial = findSpatial(carNode, "wheel_" + suffix);
    BoundingBox box = (BoundingBox)wheelSpatial.getWorldBound();
    Vector3f wheelCenter = box.getCenter();
    float wheelRadius = box.getYExtent(); 
    float suspensionLength = wheelRadius * 0.2f;
    Vector3f mountPoint = wheelCenter.add(0, suspensionLength, 0);   
    
    return player.addWheel(wheelSpatial, mountPoint, wheelDirection, wheelAxle, 
                            suspensionLength, wheelRadius, isFrontWheel);
}

These changes influenced the behavior. The wheels no longer crash through the floor. Now I have the car floating and spinning in mid air. The wheels shake erratically, but they roughly seem to be where I expect them to be.

When I debug my app, the coordinates I see in the debugger correspond with what I can verify in Blender. So I am clueless at the moment as to why it isn’t working. :frowning:

I would really appreciate it if someone with a little more knowledge and experience with bullit vehicles could help me out a little.

1 Like

OK, so I read in another thread that sometimes using createDynamicMeshShape to get a CollisionShape produces incorrect results, so I modified my code as follows:

//CollisionShape carHull = CollisionShapeFactory.createDynamicMeshShape(main);
BoundingBox box = (BoundingBox)main.getModelBound();        
BoxCollisionShape carHull = new BoxCollisionShape(box.getExtent(null));

And this is my wheel generation code:

private VehicleWheel SetupWheel(Spatial carNode, String suffix, boolean isFrontWheel) {
    Spatial wheelSpatial = findSpatial(carNode, "wheel_" + suffix);
    BoundingBox box = (BoundingBox)wheelSpatial.getWorldBound();
    float wheelRadius = box.getYExtent(); 
    float suspensionLength = wheelRadius * 0.2f;
    Vector3f mountPoint = box.getCenter().add(0, -suspensionLength, 0);   
    
    return player.addWheel(wheelSpatial, mountPoint, wheelDirection, wheelAxle, 
                            suspensionLength, wheelRadius, isFrontWheel);
}

This improved things somewhat. The car is now at rest when it hits the floor. But it starts sinking through the floor when I accelerate.

Also, the wheels are too far down. If I change this line:

Vector3f mountPoint = box.getCenter().add(0, -suspensionLength, 0);

to:

Vector3f mountPoint = box.getCenter().add(0, suspensionLength, 0);

the wheels are positioned where I want them to be. But then, when I accelerate, the wheels start bouncing and the car starts drifting in mid air.

So I am getting closer, but still not there… :frowning: Anybody out there who sees what I am doing wrong?

2 Likes

Hi, did you fix your problem or are you still struggling?

1 Like

Sorry, still struggling. :cry:

I did a few more attempts but didn’t get any further. I’ve let it rest for a while now. I’m still eager to try any suggestions though.

1 Like

Ouhh, let’s look for solution I will try to help you now. Give me your whole code. I have tried to run your code, but it’s not complete.

1 Like

You can download the complete project here:

https://dl.dropboxusercontent.com/u/21307972/WattsRacer.zip

1 Like

What do you think? Did you want to make playable car like this?

1 Like

Yes! That’s what I wanted. :slight_smile: What was wrong?

1 Like

I used TestPhysicsCar example, it has different method of adding wheels to a car, but at last result is same. Example here. I have been coding my own game so I studied both examples, Fancy Car and Physics Car and I combine it in one. Fancy Car is too difficult to run all things corectly for me, only experts could make it run right. In Fancy Car example you say: go here, find this, create this from it and do this with it, but in Physics Car you say: here you are, do this… In Physics Car you have to replace jMonkey geometry to your own models from blender and say where you want to place it. You have to make independent scenes for car model and wheel model.
I rework your Coupe_Green, I split car and wheels and put in new scenes: car, wheel_left and wheel_right. Wheels have to be placed in the centre (0, 0, 0) and good is to place car too in the centre, but with bottom of the car on 0 of Z (up) axis or higher. If you want to lower the centre of mass, just move the car up on Z axis in blender and increase restLenght of suspension in jME.
Here are your reworked project.
Here are docs for vehicle physics

I had a one little problem with your project, jME doesn’t like stateManager.attach(bulletAppState); so I copyied your project files to a new one created in my version of jME.

Ask a question if you are missing something, I will explain it.

2 Likes

Hi. I downloaded your new project. I haven’t been able to run it yet. It
seems my version of jME doesn’t like the way the bulletAppState is attached
now, so I’ll probably have to copy your new code and models back to my own
project. I’ll try that later. Gotta work now.

Thanks for your help!

1 Like

Okey, when you try it, say if everything is running right.

1 Like

Everything is running right… :wink:

That is, in jME 3.0.
At first, I pasted your code in my own project, and I still had the same problems (stuttering wheels, car spinning out of sight).

Since your comment indicated that you were on a different version of jME, I downloaded the stable jME 3.0 version (I was on the beta 2 build of jME 3.1). I created a BasicGame in jME 3.0, copied the assets and code into it, and it ran perfectly.

I did the same in jME 3.1 (new BasicGame from scratch, copy assets and code), and I got the result I have been having all the time.

So the problem appears to be in jME 3.1. Code that works fine in jME 3.0, no longer works in version 3.1. :cry:

1 Like

So, the problem was in version of jME. I am glad that everything is running correct now. Actually I am running on 3.1 alpha-2 and I didn’t have any problems.
Which code do you use now? That what I reworked or yours?

1 Like

I had some other problems too, with the materials, that don’t occur in
version 3.0. I was liking the improvements in 3.1 but I think I’ll stick to
3.0 for now.

I was running your code. But I’ll try my own in 3.0 soon.

1 Like

If you experience any issues, please do report them, not ignore them. They won’t get fixed, at least until someone else reports them - so it’s in everybody’s interest.

1 Like

Agreed. I want to verify my findings though first, and create a test set so
it can easily be reproduced.

Update: I have posted the issue and the files needed to reproduce here: https://github.com/jMonkeyEngine/jmonkeyengine/issues/593

2 Likes

I could reproduce it.
The reason it happens on JME SDK 3.1 but not 3.0 is because it uses bullet native library while JME SDK 3.0 uses jbullet (a pure java port of bullet).

I do not know what goes wrong in native bullet version. It is something core developers can help with.

It also happens in TestPhysicsCar

You can always switch to jbullet in SDK 3.1.
See

1 Like

In case anybody is interested, I got the vehicle working in native bullet eventually. So it’s working in JME 3.1 now. If you copy the files in the zip file to a new BasicGame project, it should work: https://dl.dropboxusercontent.com/u/21307972/BasicGame.zip

The model is similar to the TestFancyCar model. The wheels are in the same model, not in a separate file. This is how the vehicle is set up now:

private void buildPlayer() {
    float stiffness = 60.0f;//200=f1 car
    float compValue = 0.29f; //(lower than damp!)
    float dampValue = 0.3f;
    final float mass = 400;

    //Load model and get chassis Geometry
    carNode = new Node();
    carNode.setShadowMode(ShadowMode.Cast);
    
    Spatial carModel = assetManager.loadModel("Models/Cars/Coupe_Green.j3o");
    Spatial carSpatial = findSpatial(carModel, "car");
    Geometry carBody = (Geometry)((Node)carSpatial).getChild(0);
    
    HullCollisionShape vehicleShape = new HullCollisionShape(carBody.getMesh());
    
    //Create a vehicle control
    player = new VehicleControl(vehicleShape, mass);
    carNode.addControl(player);
    
    player.setSuspensionCompression(compValue * 0.1f * FastMath.sqrt(stiffness));
    player.setSuspensionDamping(dampValue * 3f * FastMath.sqrt(stiffness));
    player.setSuspensionStiffness(stiffness);
    player.setMaxSuspensionForce(10000);
       
    setupWheel(carModel, "fl", true);
    setupWheel(carModel, "fr", true);
    setupWheel(carModel, "rl", false).setFrictionSlip(6f);
    setupWheel(carModel, "rr", false).setFrictionSlip(6f);
    
    carNode.attachChild(carSpatial);

    //rootNode.addLight(setupHeadlight(carSpatial, "fl"));
    //rootNode.addLight(setupHeadlight(carSpatial, "fr"));

    rootNode.attachChild(carNode);
    getPhysicsSpace().add(player);
    
    chaseCam = new ChaseCamera(cam, carNode, inputManager);
    chaseCam.setSmoothMotion(true);
    chaseCam.setDefaultDistance(40);
}

private VehicleWheel setupWheel(Spatial carModel, String suffix, boolean isFrontWheel) {
    Spatial wheel = findSpatial(carModel, "wheel_" + suffix);
            
    BoundingBox box = ((BoundingBox)wheel.getWorldBound());
    Vector3f wheelPosition = box.getCenter();
    float radius = box.getYExtent();
    float maxSuspensionTravel = 10f;
    float restLength = maxSuspensionTravel / 50.0f;
    
    List<Spatial> list = ((Node)wheel).getChildren();
    for (int i = 0; i < list.size(); i++) {
        list.get(i).setLocalTranslation(wheelPosition.negate());
    }
    
    VehicleWheel vw = player.addWheel(wheel, wheelPosition.add(0, restLength, 0),
            wheelDirection, wheelAxle, restLength, radius, isFrontWheel);
    vw.setMaxSuspensionTravelCm(maxSuspensionTravel);
    
    return vw;
}
3 Likes

Great,
I will check it tomorrow.
Thanks Martin :grinning:

1 Like