Using bullet physics car and heightfield, car get stuck how can I fix?

Post Reply
gwendal_
Posts: 1
Joined: Mon May 09, 2022 5:53 pm

Using bullet physics car and heightfield, car get stuck how can I fix?

Post by gwendal_ »

I've been working on implementing a car in my game using bullet physics. The physics of the car uses `btRaycastVehicle` and the code is mostly based on the ForkLift Demo. At this point, the vehicle seems to work properly on a flat ground but then I've started working on a non flat terrain and I saw the class `btHeightfieldTerrainShape` which take an array of heights to construct a shape.
So I've managed to use this class and the vehicle can be used on it but it sometimes get stuck on terrain's hollows even if the height to climb is really small.

I'm using MLCP constraint solver and tested PGS solver but does not help.
Here is the concerned code :

Vehicle.hpp

Code: Select all

#define USE_MLCP_SOLVER
// I removed other #define because all were just floats
 
class Vehicle {
public:
    // theses bools are set to true when the corresponding key is pressed
    bool m_foward = false, m_backward = false, m_leftward = false, m_rightward = false;
    bool m_reset = false;
 
    Vehicle(Vao *chassisVao, Vao *wheelVao, const float *heights, uint32_t gridsize, float amplitude);
 
    ~Vehicle();
 
    // this function runs the logic of the physics simulation, it gets executed each frame
    void stepSimulation(uint32_t frameTime);
 
    // this function instantiate objects to rendered, it gets executed each frame
    void prepareRendering(std::vector<const Entity *> &entities);
 
private:
    // members are declared here --->  <---
 
    // create physics world and vehicle
    void initPhysics(const float *heights, uint32_t gridsize, float amplitude);
 
    // cleanup things
    void exitPhysics(void);
 
    // reset vehicle position, rotation, momentum, etc..
    void resetVehicle(void);
 
    // helper function to create rigid body
    btRigidBody* localCreateRigidBody(btScalar mass, const btTransform& startTransform, btCollisionShape* shape);
};

Vehicle.cpp

Code: Select all

#include "Vehicle.hpp"
 
Vehicle::Vehicle(Vao *chassisVao, Vao *wheelVao, const float *heights, uint32_t gridsize, float amplitude) {
    initPhysics(heights, gridsize, amplitude);
 
    if (chassisVao) {
        m_chassisEntity = new Entity(chassisVao);
    }
    for (int i = 0; i < 4; ++i) {
        m_wheelEntities.push_back(Entity(wheelVao));
    }
}
 
Vehicle::~Vehicle() {
    exitPhysics();
 
    if (m_chassisEntity) {
        delete m_chassisEntity;
    }
    m_wheelEntities.clear();
}
 
void Vehicle::initPhysics(const float *heights, uint32_t gridsize, float amplitude) {
    // setup dynamics world
    m_collisionConfiguration = new btDefaultCollisionConfiguration();
    m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
    btVector3 worldMin(-1000, -1000, -1000);
    btVector3 worldMax(1000, 1000, 1000);
    m_overlappingPairCache = new btAxisSweep3(worldMin, worldMax);
 
#ifdef USE_MLCP_SOLVER
    btDantzigSolver* mlcp = new btDantzigSolver();
    // btSolveProjectedGaussSeidel* mlcp = new btSolveProjectedGaussSeidel();
    btMLCPSolver* sol = new btMLCPSolver(mlcp);
    m_solver = sol;
#else
    m_solver = new btSequentialImpulseConstraintSolver();
#endif
 
    m_world = new btDiscreteDynamicsWorld(m_dispatcher, m_overlappingPairCache, m_solver, m_collisionConfiguration);
#ifdef USE_MLCP_SOLVER
    m_world->getSolverInfo().m_minimumSolverBatchSize = 1;
#else
    m_world->getSolverInfo().m_minimumSolverBatchSize = 128;
#endif
    m_world->getSolverInfo().m_globalCfm = 0.00001;
 
    // create ground object
    // btVector3 groundExtents(100, 3, 100);
    // btCollisionShape* groundShape = new btBoxShape(groundExtents);
    btCollisionShape* groundShape = new btHeightfieldTerrainShape(gridsize + 1, gridsize + 1, heights, 0.0f, amplitude, 1, false);
    m_collisionShapes.push_back(groundShape);
    btTransform tr;
    tr.setIdentity();
    tr.setOrigin(btVector3(gridsize * 0.5f, WHEEL_RADIUS, gridsize * 0.5f));
    localCreateRigidBody(0, tr, groundShape);
 
 
 
 
    // create vehicle
        // BEGIN - create chassis shape
    btVector3 vehicleExtents(1.76f, 1.1f, 4.0f);
    btCollisionShape* chassisShape = new btBoxShape(vehicleExtents);
    m_collisionShapes.push_back(chassisShape);
 
    btCompoundShape* compound = new btCompoundShape();
    m_collisionShapes.push_back(compound);
    btTransform localTrans;
    localTrans.setIdentity();
    //localTrans effectively shifts the center of mass with respect to the chassis
    localTrans.setOrigin(btVector3(0, 1, 0));
    compound->addChildShape(localTrans, chassisShape);
 
    tr.setOrigin(btVector3(0, 0, 0));
    m_carChassis = localCreateRigidBody(800, tr, compound);
        // END - create chassis shape
 
        // BEGIN - create vehicle
    m_vehicleRayCaster = new btDefaultVehicleRaycaster(m_world);
    m_vehicle = new btRaycastVehicle(m_tuning, m_carChassis, m_vehicleRayCaster);
    m_carChassis->setActivationState(DISABLE_DEACTIVATION); // never deactivate the vehicle
    m_world->addVehicle(m_vehicle);
 
    // choose coordinate system
    m_vehicle->setCoordinateSystem(0, 1, 2);
 
    btVector3 wheelDirection(0, -1, 0);
    btVector3 wheelAxis(-1, 0, 0);
    btVector3 connectionPoint(0.5f * vehicleExtents.x(), WHEEL_RADIUS, 0.5f * vehicleExtents.z() - WHEEL_RADIUS);
    m_vehicle->addWheel(connectionPoint, wheelDirection, wheelAxis, SUSPENSION_REST_LENGTH, WHEEL_RADIUS, m_tuning, true);
    connectionPoint = btVector3(-0.5f * vehicleExtents.x(), WHEEL_RADIUS, 0.5f * vehicleExtents.z() - WHEEL_RADIUS);
    m_vehicle->addWheel(connectionPoint, wheelDirection, wheelAxis, SUSPENSION_REST_LENGTH, WHEEL_RADIUS, m_tuning, true);
    connectionPoint = btVector3(0.5f * vehicleExtents.x(), WHEEL_RADIUS, -0.5f * vehicleExtents.z() + WHEEL_RADIUS);
    m_vehicle->addWheel(connectionPoint, wheelDirection, wheelAxis, SUSPENSION_REST_LENGTH, WHEEL_RADIUS, m_tuning, false);
    connectionPoint = btVector3(-0.5f * vehicleExtents.x(), WHEEL_RADIUS, -0.5f * vehicleExtents.z() + WHEEL_RADIUS);
    m_vehicle->addWheel(connectionPoint, wheelDirection, wheelAxis, SUSPENSION_REST_LENGTH, WHEEL_RADIUS, m_tuning, false);
 
    for (int i = 0; i < m_vehicle->getNumWheels(); i++) {
        btWheelInfo& wheel = m_vehicle->getWheelInfo(i);
        wheel.m_suspensionStiffness = SUSPENSION_STIFFNESS;
        wheel.m_wheelsDampingRelaxation = SUSPENSION_DAMPING;
        wheel.m_wheelsDampingCompression = SUSPENSION_COMPRESSION;
        wheel.m_frictionSlip = WHEEL_FRICTION;
        wheel.m_rollInfluence = ROLL_IN_INFLUENCE;
    }
 
    resetVehicle();
}

void Vehicle::stepSimulation(uint32_t frameTime) {
    float speed = m_vehicle->getCurrentSpeedKmHour();
    
    m_vehicleEngineForce = 0.0f;
    m_vehicleBreakingForce = 0.0f;
    /* --->
    Processing input sets m_vehicleEngineForce, m_vehicleBreakingForce, m_vehicleSteering
    <--- */

 
    m_vehicle->applyEngineForce(m_vehicleEngineForce, 2);
    m_vehicle->setBrake(m_vehicleBreakingForce, 2);
    m_vehicle->applyEngineForce(m_vehicleEngineForce, 3);
    m_vehicle->setBrake(m_vehicleBreakingForce, 3);
 
    m_vehicle->setSteeringValue(m_vehicleSteering, 0);
    m_vehicle->setSteeringValue(m_vehicleSteering, 1);
 
    m_world->stepSimulation(frameTime * 0.001f, 2);
    btMLCPSolver *solver = (btMLCPSolver *) m_world->getConstraintSolver();
    int numFallbacks = solver->getNumFallbacks();
    if (numFallbacks) {
        std::cerr << "MLCP solver failed " << numFallbacks << " times, falling back to btSequentialImpulseSolver" << std::endl;
    }
    solver->setNumFallbacks(0);
}
And here is a video to illustrate : link

Thank you
Post Reply