btRaycastVehicle example


Vehicle dynamics is one of my interests and I recently discovered Bullet’s btRaycastVehicle example and ported to Urho Vehicle demo.
Replace the Vehicle.cpp/.h files in Samples/19_VehicleDemo/ and you should be good to go.
Note: this code sample is intended to show the raycastVehicle basics and should be refactored, and it also breaks saving/restoring the scene.

Edit: added video


#include <Urho3D/Urho3D.h>

#include <Urho3D/Physics/CollisionShape.h>
#include <Urho3D/Physics/Constraint.h>
#include <Urho3D/Core/Context.h>
#include <Urho3D/Graphics/Material.h>
#include <Urho3D/Graphics/Model.h>
#include <Urho3D/Physics/PhysicsEvents.h>
#include <Urho3D/Physics/PhysicsWorld.h>
#include <Urho3D/Physics/PhysicsUtils.h>
#include <Urho3D/Resource/ResourceCache.h>
#include <Urho3D/Physics/RigidBody.h>
#include <Urho3D/Scene/Scene.h>
#include <Urho3D/Graphics/StaticModel.h>
#include <Urho3D/Graphics/DebugRenderer.h>

#include "Vehicle.h"

#include <SDL/SDL_log.h>
#include <Bullet/BulletDynamics/Vehicle/btRaycastVehicle.h>
#include <Bullet/BulletDynamics/Dynamics/btDynamicsWorld.h>

#define DELETE_NULL(x)      { if (x) delete x; x = NULL; }

Vehicle::Vehicle(Context* context) 
    : LogicComponent( context )
    , steering_( 0.0f )
    // fixed update() for inputs and post update() to sync wheels for rendering

    m_fEngineForce = 0.0f;                                                 
    m_fBreakingForce = 0.0f;                                               
    m_fmaxEngineForce = 2500.f;//this should be engine/velocity dependent  
    m_fmaxBreakingForce = 100.f;                                           
    m_fVehicleSteering = 0.0f;                                             
    m_fsteeringIncrement = 0.04f;                                          
    m_fsteeringClamp = 0.3f;                                               
    m_fwheelRadius = 0.5f;                                                 
    m_fwheelWidth = 0.4f;                                                  
    m_fwheelFriction = 1000;//BT_LARGE_FLOAT;                              
    m_fsuspensionStiffness = 14.0f;//20.f;                                 
    m_fsuspensionDamping = 2.0f;//2.3f;                                    
    m_fsuspensionCompression = 4.0f;//4.4f;                                
    m_frollInfluence = 0.01f;//1.0f;                                       
    m_fsuspensionRestLength = 0.6f;//0.6

    m_vehicleRayCaster = NULL;
    m_vehicle = NULL;


    DELETE_NULL( m_vehicleRayCaster );
    DELETE_NULL( m_vehicle );

void Vehicle::RegisterObject(Context* context)
    ATTRIBUTE("Controls Yaw", float, controls_.yaw_, 0.0f, AM_DEFAULT);
    ATTRIBUTE("Controls Pitch", float, controls_.pitch_, 0.0f, AM_DEFAULT);
    ATTRIBUTE("Steering", float, steering_, 0.0f, AM_DEFAULT);

void Vehicle::ApplyAttributes()

void Vehicle::Init()
    // This function is called only from the main program when initially creating the vehicle, not on scene load
    ResourceCache* cache = GetSubsystem<ResourceCache>();
    StaticModel* hullObject = node_->CreateComponent<StaticModel>();
    hullBody_ = node_->CreateComponent<RigidBody>();
    CollisionShape* hullColShape = node_->CreateComponent<CollisionShape>();

    hullBody_->SetLinearDamping(0.2f); // Some air resistance
    int rightIndex = 0;
    int upIndex = 1;
    int forwardIndex = 2;
    Scene* scene = GetScene();
    PhysicsWorld *pPhysWorld = scene->GetComponent<PhysicsWorld>();
    btDynamicsWorld *pbtDynWorld = (btDynamicsWorld*)pPhysWorld->GetWorld();

    m_vehicleRayCaster = new btDefaultVehicleRaycaster( pbtDynWorld );
    m_vehicle = new btRaycastVehicle( m_tuning, hullBody_->GetBody(), m_vehicleRayCaster );
    pbtDynWorld->addVehicle( m_vehicle );

    m_vehicle->setCoordinateSystem( rightIndex, upIndex, forwardIndex );

    node_->SetScale( Vector3(1.5f, 1.0f, 3.5f) );
    Vector3 v3BoxExtents = Vector3::ONE;//Vector3(1.5f, 1.0f, 3.0f);
    hullColShape->SetBox( v3BoxExtents );


    float connectionHeight = -0.4f;//1.2f;
    bool isFrontWheel=true;
    btVector3 wheelDirectionCS0(0,-1,0);
    btVector3 wheelAxleCS(-1,0,0);

    btVector3 connectionPointCS0(CUBE_HALF_EXTENTS-(0.3f*m_fwheelWidth),connectionHeight,2*CUBE_HALF_EXTENTS-m_fwheelRadius);

    connectionPointCS0 = btVector3(-CUBE_HALF_EXTENTS+(0.3f*m_fwheelWidth),connectionHeight,2*CUBE_HALF_EXTENTS-m_fwheelRadius);

    isFrontWheel = false;
    connectionPointCS0 = btVector3(-CUBE_HALF_EXTENTS+(0.3f*m_fwheelWidth),connectionHeight,-2*CUBE_HALF_EXTENTS+m_fwheelRadius);

    connectionPointCS0 = btVector3(CUBE_HALF_EXTENTS-(0.3f*m_fwheelWidth),connectionHeight,-2*CUBE_HALF_EXTENTS+m_fwheelRadius);

    for ( int i = 0; i < m_vehicle->getNumWheels(); i++ )
        btWheelInfo& wheel = m_vehicle->getWheelInfo( i );
        wheel.m_suspensionStiffness = m_fsuspensionStiffness;
        wheel.m_wheelsDampingRelaxation = m_fsuspensionDamping;
        wheel.m_wheelsDampingCompression = m_fsuspensionCompression;
        wheel.m_frictionSlip = m_fwheelFriction;
        wheel.m_rollInfluence = m_frollInfluence;

	if ( m_vehicle )

		for ( int i = 0; i < m_vehicle->getNumWheels(); i++ )
			//synchronize the wheels with the (interpolated) chassis worldtransform

            btTransform transform = m_vehicle->getWheelTransformWS( i );
            Vector3 v3Origin = ToVector3( transform.getOrigin() );
            Quaternion qRot = ToQuaternion( transform.getRotation() );

            // create wheel node
            Node *wheelNode = GetScene()->CreateChild();
            m_vpNodeWheel.Push( wheelNode );

            wheelNode->SetPosition( v3Origin );
            btWheelInfo whInfo = m_vehicle->getWheelInfo( i );
            Vector3 v3PosLS = ToVector3( whInfo.m_chassisConnectionPointCS );

            wheelNode->SetRotation( v3PosLS.x_ >= 0.0 ? Quaternion(0.0f, 0.0f, -90.0f) : Quaternion(0.0f, 0.0f, 90.0f) );
            wheelNode->SetScale(Vector3(1.0f, 0.65f, 1.0f));

            StaticModel *pWheel = wheelNode->CreateComponent<StaticModel>();

void Vehicle::FixedUpdate(float timeStep)
    float newSteering = 0.0f;
    float accelerator = 0.0f;

    // Read controls
    if (controls_.buttons_ & CTRL_LEFT)
        newSteering = -1.0f;
    if (controls_.buttons_ & CTRL_RIGHT)
        newSteering = 1.0f;
    if (controls_.buttons_ & CTRL_FORWARD)
        accelerator = 1.0f;
    if (controls_.buttons_ & CTRL_BACK)
        accelerator = -0.5f;

    // When steering, wake up the wheel rigidbodies so that their orientation is updated
    if ( newSteering != 0.0f )
        steering_ = steering_ * 0.95f + newSteering * 0.05f;
        steering_ = steering_ * 0.8f + newSteering * 0.2f;

    // Set front wheel angles
    m_fVehicleSteering = steering_;
    int wheelIndex = 0;
    wheelIndex = 1;

    if ( newSteering != 0.0f || accelerator != 0.0f )

    // apply forces
    m_fEngineForce = m_fmaxEngineForce * accelerator;
    m_fBreakingForce = 0.f;

    // 2x wheel drive
    for ( int i = 2; i < 4; ++i )
        m_vehicle->applyEngineForce( m_fEngineForce, i );
        //m_vehicle->setBrake( m_fBreakingForce, i );

// sync wheels for rendering
void Vehicle::PostUpdate(float )
    for ( int i = 0; i < m_vehicle->getNumWheels(); i++ )
		m_vehicle->updateWheelTransform( i, true );

        btTransform transform = m_vehicle->getWheelTransformWS( i );
        Vector3 v3Origin = ToVector3( transform.getOrigin() );
        Quaternion qRot = ToQuaternion( transform.getRotation() );

        Node *pWheel = m_vpNodeWheel[ i ];
        pWheel->SetPosition( v3Origin );
        btWheelInfo whInfo = m_vehicle->getWheelInfo( i );
        Vector3 v3PosLS = ToVector3( whInfo.m_chassisConnectionPointCS );
        Quaternion qRotator = ( v3PosLS.x_ >= 0.0 ? Quaternion(0.0f, 0.0f, -90.0f) : Quaternion(0.0f, 0.0f, 90.0f) );
        pWheel->SetRotation( qRot * qRotator );


#pragma once

#include <Urho3D/Input/Controls.h>
#include <Urho3D/Scene/LogicComponent.h>
#include <Bullet/BulletDynamics/Vehicle/btRaycastVehicle.h>

namespace Urho3D
class Constraint;
class Node;
class RigidBody;

using namespace Urho3D;

const int CTRL_FORWARD = 1;
const int CTRL_BACK = 2;
const int CTRL_LEFT = 4;
const int CTRL_RIGHT = 8;

const float YAW_SENSITIVITY = 0.1f;
const float ENGINE_POWER = 10.0f;
const float DOWN_FORCE = 10.0f;
const float MAX_WHEEL_ANGLE = 22.5f;

// Vehicle component, responsible for physical movement according to controls.
class Vehicle : public LogicComponent

    /// Construct.
    Vehicle(Context* context);
    /// Register object factory and attributes.
    static void RegisterObject(Context* context);
    /// Perform post-load after deserialization. Acquire the components from the scene nodes.
    virtual void ApplyAttributes();

    /// Initialize the vehicle. Create rendering and physics components. Called by the application.
    void Init();

    /// Handle physics world update. Called by LogicComponent base class.
    virtual void FixedUpdate(float timeStep);
    virtual void PostUpdate(float timeStep);
    /// Movement controls.
    Controls controls_;
    // Hull RigidBody
    WeakPtr<RigidBody> hullBody_;
    /// Current left/right steering amount (-1 to 1.)
    float steering_;

    // raycast vehicle
    btRaycastVehicle::btVehicleTuning	m_tuning;
    btVehicleRaycaster                  *m_vehicleRayCaster;
    btRaycastVehicle                    *m_vehicle;

    // IDs of the wheel scene nodes for serialization.
    Vector<Node*>           m_vpNodeWheel;

    float	m_fEngineForce;
    float	m_fBreakingForce;

    float	m_fmaxEngineForce;
    float	m_fmaxBreakingForce;

    float	m_fVehicleSteering;
    float	m_fsteeringIncrement;
    float	m_fsteeringClamp;
    float	m_fwheelRadius;
    float	m_fwheelWidth;
    float	m_fwheelFriction;
    float	m_fsuspensionStiffness;
    float	m_fsuspensionDamping;
    float	m_fsuspensionCompression;
    float	m_frollInfluence;
    float   m_fsuspensionRestLength;


Bullet raycast vehicle
Raycast vehicle
Accessing/subclassing to TriangleMeshInterface
Newbie: Simple All terrain Vehicle -without slipping?

You can lower the wheel connection height to:
float connectionHeight = 0.0f; (Line 147, I think)

and have more stability to allow you to increase the engine force a bit:
m_fmaxEngineForce = 3000.f;

Try adjusting suspension parameters to your liking.


This is nice, and the suspension feature is really an upgrade. Maybe this can be PR’d for next release?


Nice example. For a pull request this should be refactored into a component so that the Bullet raycastvehicle class is not directly exposed to users, as otherwise it will be hard to expose to scripting languages.


added an archived video


Great! :stuck_out_tongue:


Iv’ve started porting to component.
I’m thinking of creating 2 separate components: a Vehicle component for vehicle chassis and a Wheel component for each wheel. As many settings are per wheel, it could be handy at reloading time to re-create the whole vehicle that way. Wheel would store the vehicle node ID.
Before going further, I’d like to have feedback about this option or other better options.


I think that’s an excellent idea. Having a wheel component would definitely give more flexibility on how the vehicle is constructed.


Thanks Lumak, I will follow this path.


I can’t figure out how to remove a wheel from the vehicle.
I haven’t found a method to do this in btRaycastVehicle, so I’m trying to use ‘remove’ from LinearMath/btAlignedObjectArray.h, but it fails to compile:

void Vehicle::RemoveWheel(int wheelID) { if (!vehicle_) return; btWheelInfo& wheel = vehicle_->getWheelInfo(wheelID); vehicle_->m_wheelInfo.remove(wheel); }
where vehicle_ is the btRaycastVehicle and m_wheelInfo is the btAlignedObjectArray from btRaycastVehicle.h.


Verified the compile error when calling remove() which results in a compare error in findLinearSearch() function at:
if (m_data[i] == key)

btAlignedObjectArray class seems to have a problem with this when you declare btAlignedObjectArray vector with a non-pointer data type.

But the work around for this is to do something similar to what the remove() function attempts:

void Vehicle::RemoveWheel(int wheelID)
   if (!m_vehicle)

   if ( wheelID < m_vehicle->m_wheelInfo.size() )
       if ( wheelID != m_vehicle->m_wheelInfo.size() - 1 )
           m_vehicle->m_wheelInfo.swap( wheelID, m_vehicle->m_wheelInfo.size() - 1 );

Hope this helps.


Thanks Lumak, this is indeed a good way to get rid of findLinearSearch() issue.


Is there any update on vehicle components in Urho?

Sorry for reviving old topic, but I’m really-really interested :slight_smile:


Me too! This is a great example btw.


It most certainly is. I merged the Vehicle from this sample with the Vehicles in OGTatt. I couldn’t quite get the visual wheels to update correctly somehow, but the cars can drive! :slight_smile:


Well, I packed-up reasonable component for a vehicle. With examples, can be found
here: A problem with this PR is that win32 shared library builds fail for
no reason I understand :frowning: Please help find the culprit.


A problem with this PR is that win32 shared library builds fail for

add URHO3D_API to component


Simply put it on its own line. This is called a onebox


I added these and put all accessors to .cpp. This is not enough.
I see

CMakeFiles/46_RaycastVehicle.dir/objects.a(RaycastVehicleDemo.cpp.obj):RaycastVehicleDemo.cpp:(.text$_ZN17btTypedConstraintD0Ev[btTypedConstraint::~btTypedConstraint()]+0xb): undefined reference to btAlignedFreeInternal(void*)’ CMakeFiles/46_RaycastVehicle.dir/objects.a(RaycastVehicleDemo.cpp.obj):RaycastVehicleDemo.cpp:(.data$_ZTV17btTypedConstraint[vtable for btTypedConstraint]+0x60): undefined reference to btTypedConstraint::serialize(void*, btSerializer*) const’

errors which are from some low-level bullet stuff. Looks like I won’t have enough time to resolve this if
nobody points on what to do, so that is going to rot.


Sorry, I deleted PR, this was too ambitious for me with time I have. If anybody wants the code
and have some ideas feel free to pull from

I will continue to add features there, but as I don’t use windows and shared library builds I will not fix
problems with shared library on windows, as it looks quite complicated issue and I have zero motivation
to fix that.
So I will keep changes local and add features to have full btRaycastVehicle wrapper.