PhysX support for vehicles has been significantly reworked in 3.x. In place of the NxWheelShape class of 2.8.x, a more optimal integration of the core PhysX SDK and vehicle simulation code has been developed. More specifically, the vehicles component now sits outside the core SDK in a manner similar to PhysXExtensions. This change allows vehicles to be updated in a single pass as well as promoting a more intuitive approach to vehicle modelling data. Vehicles support has been extended from the suspension/wheel/tire modelling of 2.8.x to a more complete model that couples modular vehicle components including engine, clutch, gears, autobox, differential, wheels, tires, suspensions, and chassis. A quick glance at the data structures in PxVehicleComponents.h will provide a flavour of the behaviors supported by PhysX vehicles.

The vehicle SDK can be thought of as having two separate components: the core vehicle SDK and an optional set of utility classes and functions that are provided as a reference solution to common problems in game vehicle dynamics. The utility classes and functions may be found in PxVehicleUtilControl.h, PxVehicleUtilSetup.h, and PxVehicleUtilTelemetry.h, while the remaining files make up the core vehicle SDK. It is important to note that these utility classes are provided only as an example solution to common game vehicle problems. It is quite possible that many developers will be able to use these utility classes directly without modification. However, it is expected that many developers will use these classes as a starting point for integrating the physx core vehicle SDK with gameplay code. An example might be the code that filters the raw inputs from a gamepad controller in order to generate meaningful acceleration, brake and steer values that can be passed to the core SDK. The implementation provided might be unsuitable for some developers because it is written with specific assumptions about the kind of smoothing required to generate enjoyable gameplay. It is perfectly reasonable that some developers will consider these assumptions to be at odds with the gameplay requirements of their project. Developers who find themselves in this situation are invited to use the given implementation as a reference or even to independently develop an alternative if they wish. Other utility classes include data structures that record telemetry data in real time and helper functions to enable 3-wheeled cars.

In the following Section some basic details of the algorithm will be provided. The Section that follows this algorithmic introduction discusses the steps required to create and update a PhysX vehicle. The use of filter shaders to mark shapes as drivable and non-drivable shall be presented, as well as a demonstration of how to set up the friction of different combinations of tire type and drivable surface type. Following this, the recording and visualization of vehicle telemetry data is introduced with reference to tuning and debugging vehicle behaviors. Additionally, some of the key functions to query the internal vehicle state shall be introduced. The last Section details the steps required to set up 3-wheeled and N-wheeled cars and tanks. References to example code in SampleVehicle is made throughout.

Algorithm Details

The PhysX Vehicle SDK models vehicles as collections of sprung masses, where each sprung mass represents a suspension line with associated wheel and tire data. These collections of sprung masses have a complementary representation as a rigid body actor whose mass, center of mass, and moment of inertia matches exactly the masses and coordinates of the sprung masses. The purpose of the PhysX Vehicle SDK update function is to compute suspension and tire forces using the sprung mass model and then to apply the aggregate of these forces to the PhysX SDK rigid body representation in the form of a modified velocity and angular velocity. Interaction of the rigid body actor with other scene objects and global pose update is then managed by the PhysX SDK.

The update of each vehicle begins with a raycast for each suspension line, with the raycast starting at the wheel center and casting downwards along the direction of suspension travel. The suspension force from each elongated or compressed spring is computed and added to the aggregate force to be applied to the rigid body. Additionally, the suspension force is used to compute the load that is bearing down on the tire. This load is used to determine the tire forces that will be generated in the contact plane and then added to the aggregate force to be applied to the rigid body. The tire force computation actually depends on a number of factors including steer angle, camber angle, friction, wheel rotation speed, and rigid body momentum. The aggregated force of all tire and suspension forces is then applied to the rigid body actor associated with the vehicle so that the transform may be modified accordingly in the next PhysX SDK update.

In addition to being collections of sprung masses, PhysX vehicles also support a variety of drive models. The centre of the drive model is a torsion clutch, which couples together the wheels and the engine via forces that arise from differences in rotational speeds at both sides of the clutch. At one side of the clutch is the engine, a 1D rigid body that is powered directly from the accelerator pedal. At the other side of the clutch are the gearing system, the differential and the wheels. The effective rotational speed of the other side of the clutch can be computed directly from the gearing ratio and the rotational speed of the wheels that are coupled to the clutch through the differential. This model naturally allows engine torques to propagate to the wheels and wheel torques to propagate back to the engine, just as in a standard car.

The data describing each component of the PhysX vehicle can be found in Section Tuning Guide.

Vehicle SDK Initialization

Before using the vehicle sdk it must first be initialized in order to set up a number of threshold values from various tolerance scales. This is as straightforward as calling the following function:

PX_C_EXPORT bool PX_CALL_CONV PxInitVehicleSDK(PxPhysics& physics);

This function should be called after setting up the required PxPhysics and PxFoundation instances.

The basis vectors of the vehicle simulation must also be configured so that longitudinal and lateral tire slips may be unambiguously computed:

void PxVehicleSetBasisVectors(const PxVec3& up, const PxVec3& forward);

This function can be called at any time prior to the first execution of PxVehicleUpdates.

The rigid body actors associated with vehicles can be updated either immediately with velocity modifications or updated with an acceleration that is applied in the next PhysX SDK simulate call. The following function can be used to select the required update mode:

void PxVehicleSetUpdateMode(PxVehicleUpdateMode::Enum vehicleUpdateMode);

As expected, the vehicle sdk also has a shutdown process which needs to be invoked:

PX_C_EXPORT void PX_CALL_CONV PxCloseVehicleSDK();

This needs to be called before the PxPhysics instance and PxFoundation instance are released; that is, the order of shutdown is the reverse of the initialisation order.

Vehicle Creation

In this Section the process of vehicle creation shall be decomposed into an intuitive sequence of simple procedures. Each of these procedures will be explained in turn.

The first step in the process is to configure the data structures that completely describe the vehicle and its components:

PxVehicleWheelsSimData* wheelsSimData=PxVehicleWheelsSimData::allocate(4);
PxVehicleDriveSimData4W driveSimData;

The above code instantiates the data structures for the wheels of a 4-wheeled car, as well as the data structures for non-wheel data such as engine, clutch, gears etc. With the exception of geometry data, the data structures are all given the default values of a "typical" car at construction. Geometry data must be explicitly set to successfully complete the creation of a PhysX vehicle, while the data fields with default values might be inappropriate for the vehicle under consideration. Some code is therefore required to configure the data to the requirements of the car in the sample:

PxVehicleChassisData chassisData;

This code introduces the PxVehicleChassisData struct that is used to configure the PhysX actor that represents the vehicle in the PhysX SDK, and then calls a function to configure wheelsSimData, driveSimData and chassisData with meaningful data:

void createVehicle4WSimulationData
(const PxF32 chassisMass, PxConvexMesh* chassisConvexMesh,
 const PxF32 wheelMass, PxConvexMesh** wheelConvexMeshes, const PxVec3* wheelCentreOffsets,
 PxVehicleWheelsSimData& wheelsData, PxVehicleDriveSimData4W& driveData, PxVehicleChassisData& chassisData)
        //Extract the chassis AABB dimensions from the chassis convex mesh.
        const PxVec3 chassisDims=computeChassisAABBDimensions(chassisConvexMesh);

        //The origin is at the center of the chassis mesh.
        //Set the center of mass to be below this point and a little towards the front.
        const PxVec3 chassisCMOffset=PxVec3(0.0f,-chassisDims.y*0.5f+0.65f,0.25f);

        //Now compute the chassis mass and moment of inertia.
        //Use the moment of inertia of a cuboid as an approximate value for the chassis moi.
        PxVec3 chassisMOI
                ((chassisDims.y*chassisDims.y + chassisDims.z*chassisDims.z)*chassisMass/12.0f,
                 (chassisDims.x*chassisDims.x + chassisDims.z*chassisDims.z)*chassisMass/12.0f,
                 (chassisDims.x*chassisDims.x + chassisDims.y*chassisDims.y)*chassisMass/12.0f);
        //A bit of tweaking here.  The car will have more responsive turning if we reduce the
        //y-component of the chassis moment of inertia.

        //Let's set up the chassis data structure now.

        //Compute the sprung masses of each suspension spring using a helper function.
        PxF32 suspSprungMasses[4];

        //Extract the wheel radius and width from the wheel convex meshes.
        PxF32 wheelWidths[4];
        PxF32 wheelRadii[4];

        //Now compute the wheel masses and inertias components around the axle's axis.
        PxF32 wheelMOIs[4];
        for(PxU32 i=0;i<4;i++)
        //Let's set up the wheel data structures now with radius, mass, and moi.
        PxVehicleWheelData wheels[4];
        for(PxU32 i=0;i<4;i++)
        //Disable the handbrake from the front wheels and enable for the rear wheels
        //Enable steering for the front wheels and disable for the front wheels.

        //Let's set up the tire data structures now.
        //Put slicks on the front tires and wets on the rear tires.
        PxVehicleTireData tires[4];

        //Let's set up the suspension data structures now.
        PxVehicleSuspensionData susps[4];
        for(PxU32 i=0;i<4;i++)

        //Set up the camber.
        //Remember that the left and right wheels need opposite camber so that the car preserves symmetry about the forward direction.
        //Set the camber to 0.0f when the spring is neither compressed or elongated.
        const PxF32 camberAngleAtRest=0.0;
        //Set the wheels to camber inwards at maximum droop (the left and right wheels almost form a V shape)
        const PxF32 camberAngleAtMaxDroop=0.1f;
        //Set the wheels to camber outwards at maximum compression (the left and right wheels almost form a A shape).
        const PxF32 camberAngleAtMaxCompression=-0.1f;

        //We need to set up geometry data for the suspension, wheels, and tires.
        //We already know the wheel centers described as offsets from the actor center and the center of mass offset from actor center.
        //From here we can approximate application points for the tire and suspension forces.
        //Lets assume that the suspension travel directions are absolutely vertical.
        //Also assume that we apply the tire and suspension forces 30cm below the center of mass.
        PxVec3 suspTravelDirections[4]={PxVec3(0,-1,0),PxVec3(0,-1,0),PxVec3(0,-1,0),PxVec3(0,-1,0)};
        PxVec3 wheelCentreCMOffsets[4];
        PxVec3 suspForceAppCMOffsets[4];
        PxVec3 tireForceAppCMOffsets[4];
        for(PxU32 i=0;i<4;i++)

        //Now add the wheel, tire and suspension data.
        for(PxU32 i=0;i<4;i++)

        //Set the car to perform 3 sub-steps when it moves with a forwards speed of less than 5.0
        //and with a single step when it moves at speed greater than or equal to 5.0.
        wheelsData.setSubStepCount(5.0f, 3, 1);

        //Now set up the differential, engine, gears, clutch, and ackermann steering.

        PxVehicleDifferential4WData diff;

        PxVehicleEngineData engine;
        engine.mMaxOmega=600.0f;//approx 6000 rpm

        PxVehicleGearsData gears;

        PxVehicleClutchData clutch;

        //Ackermann steer accuracy
        PxVehicleAckermannGeometryData ackermann;

In the above code, tire types have been specified for the front and rear vehicles. These tire types have an influence on the friction that the tires will experience on different surface types. This will be discussed in more detail in Section Tire Friction On Drivable Surfaces.

The next step is to create a PhysX actor that will be used to represent the vehicle and its collision geometry as a rigid body actor in the PhysX sdk:

PxRigidDynamic* vehActor=createVehicleActor4W(chassisData,wheelConvexMeshes4,chassisConvexMesh,scene,physics,material);

This function creates a PhysX rigid body actor, sets up all the shapes for the wheels and chassis, configures the rigid body mass and inertia, and sets collision and scene query filters for wheel and chassis shapes. The filtering shall be discussed in more detail in Section Filtering.

The next step is to create a PhysX vehicle:

PxVehicleDrive4W* car = PxVehicleDrive4W::allocate(4);

This code creates a car with 4 driven wheels and no non-driven wheels, as described by wheelsSimData/driveSimData, and with a rigid body rerpresented in the PhysX SDK by vehActor. Two representations of the vehicle (PxVehicleDrive4W and PxRigidDynamic) are necessary because vehicles are simulated in two distinct steps. In the first step the vehicle simulation code runs outside of the core PhysX SDK. This code computes tire and suspension forces/torques that are applied to each vehicle's rigid body actor, in addition to simulating the internal dynamics of the vehicle. These rigid body actors are subsequently updated inside the PhysX SDK, taking into account the applied vehicle dynamics forces/torques as well as the collision shapes of the actor that represent the combination of chassis and wheels. More details of these two update steps are discussed in Section Vehicle Update.

The vehicle update poses the wheel shapes to account for suspension travel limits, steer angle, wheel rotation speed, and drivable surface contact. This requires the vehicles to understand a mapping between each wheel and its associated shape so that it can apply the pose to the correct shape. The code in createVehicleActor4W adds the wheel shapes before the chassis shapes and does this in the same order as the vehicle wheel enumeration; that is, shape 0 corresponds to wheel 0, shape 1 to wheel 1, and so on. As a consequence, the mapping is configured as follows:

//Set up the mapping between wheel and actor shape.

Each suspension needs to be assigned a scene query filter data to determine the world shapes that will interact with the raycast issued by each suspension line:

//Set up the scene query filter data for each suspension line.
PxFilterData vehQryFilterData;
car->mWheelsSimData.setSceneQueryFilterData(0, vehQryFilterData);
car->mWheelsSimData.setSceneQueryFilterData(1, vehQryFilterData);
car->mWheelsSimData.setSceneQueryFilterData(2, vehQryFilterData);
car->mWheelsSimData.setSceneQueryFilterData(3, vehQryFilterData);

More details of the filtering scheme will be provided in Section Filtering.

Vehicle Actor

The actor representing the vehicle in the PhysX SDK is configured with this function:

void setupActor
(PxRigidDynamic* vehActor,
 const PxFilterData& vehQryFilterData,
 const PxGeometry** wheelGeometries, const PxTransform* wheelLocalPoses, const PxU32 numWheelGeometries, const PxMaterial* wheelMaterial, const PxFilterData& wheelCollFilterData,
 const PxGeometry** chassisGeometries, const PxTransform* chassisLocalPoses, const PxU32 numChassisGeometries, const PxMaterial* chassisMaterial, const PxFilterData& chassisCollFilterData,
 const PxVehicleChassisData& chassisData,
 PxPhysics* physics)
        //Add all the wheel shapes to the actor.
        for(PxU32 i=0;i<numWheelGeometries;i++)
                PxShape* wheelShape=vehActor->createShape(*wheelGeometries[i],*wheelMaterial);

        //Add the chassis shapes to the actor.
        for(PxU32 i=0;i<numChassisGeometries;i++)
                PxShape* chassisShape=vehActor->createShape(*chassisGeometries[i],*chassisMaterial);


Each wheel and chassis shape is assigned a query filter data and a simulation filter data. Additionally, each shape is given a local pose. For wheels this local pose is just the rest pose of the wheel. The local pose of each wheel is subsequently reset after each vehicle update pass to reflect the wheel's current position along the suspension travel direction. See Section Vehicle Update for more details.

The above code adds the wheel shapes in the order specified in PxVehicleDrive4W::eWheelOrdering, followed by the shapes of the chassis. This ordering is not a requirement because any mapping between wheel and shape can be configured by calling PxVehicleWheelsSimData::setWheelShapeMapping. The association between wheel and shape is explicitly set in Section Vehicle Creation.

Tire Friction on Drivable Surfaces

In this Section setting up tire types, drivable surface types, and tire friction on combinations of tire and surface type shall be discussed.

In Section Vehicle Creation tire types were assigned to the front and rear tires:

PxVehicleTireData tires[4];

In addition to tire types, drivable surface types can be associated with PxMaterial instances:


This code creates a mapping, stored in a PxVehicleDrivableSurfaceToTireFrictionPairs instance, that associates the nth PxMaterial pointer in the drivableSurfaceMaterials array with the nth PxVehicleDrivableSurfaceType in the drivableSurfaceTypes array. As a consequence, PxVehicleDrivableSurfaceToTireFrictionPairs permits a mapping between each specified PxMaterial instance and the integer value stored in PxVehicleDrivableSurfaceType. It is not necessary to store all possible PxMaterial instances in the hash table: unrecognised PxMaterial pointers are automatically associated with a surface type having integer value zero.

Each combination of surface type and tire type can be assigned a unique friction value. This allows different types of tires to react differently to the same surface conditions. Wet tires, for example, would be expected to behave very differently on wet tarmac to slick tires. This code refines the friction for each possible combination in SampleVehicle:

for(PxU32 i=0;i<MAX_NUM_SURFACE_TYPES;i++)
        for(PxU32 j=0;j<MAX_NUM_TIRE_TYPES;j++)

Here, a table of friction values has been hard-coded:

//Tire model friction for each combination of drivable surface type and tire type.
static PxF32 gTireFrictionMultipliers[MAX_NUM_SURFACE_TYPES][MAX_NUM_TIRE_TYPES]=
        //WETS  SLICKS  ICE             MUD
        {0.95f, 0.95f,  0.95f,  0.95f},         //MUD
        {1.10f, 1.15f,  1.10f,  1.10f},         //TARMAC
        {0.70f, 0.70f,  0.70f,  0.70f},         //ICE
        {0.80f, 0.80f,  0.80f,  0.80f}          //GRASS

In this table the friction of slick tires on tarmac has been assigned a value of 1.15. Slick tires on mud, on the other hand, have been assigned a friction value of 1.10. By tuning this table, along with the tire types assigned to each vehicle and the surface types associated with each material, it is possible to modify the tire grip to the specifications of any game.

There is no upper bound on the friction values used in the PhysX vehicles SDK. Although the maximum value of friction that obeys the laws of physics is 1.0, the PhysX vehicles SDK purposefully does not enforce this rule. One reason for this is that the vehicle model is far from a complete description of a real vehicle, meaning that some liberties need to be taken with friction values to generate the desired behaviour. A more complete model would certainly provide greater accuracy given a specific set of vehicle parameters but it is not at all clear that it would provide a greater range of editable and controllable behaviors or have the performance characteristics required for games. Another reason that friction is not clamped at 1.0 is that games typically simulate the physics update at 60Hz. This comes at a cost to numerical accuracy, especially when there are a number of transient tire effects that require KHz update frequencies. One source of numerical accuracy is the amplitude of oscillation of the suspension, which is governed in turn by the distance that the vehicle falls under gravity between each update. At KHz update frequencies this simulation artifact is acceptably small, but not at 60Hz. The last reason is that there is simply no need to impose the strict rules of friction on the vehicles SDK. This can allow interesting behaviors to be generated that would perhaps be impossible when constrained by the laws of rigid body and tire dynamics. Having said all this, however, the implemented model simulated at 60Hz ought to have enough integrity that only small tweaks above 1.0 should be necessary. If very large friction values are required, say greater than 2.0, then it is likely that something is wrong with the update order or perhaps very unphysical vehicle data has been used.

The PxMaterial instances discussed here are the results of per-wheel raycasts. Raycast filtering shall be discussed in more detail in Section Filtering. The relationship between raycast execution and vehicle updates shall be introduced in Section Vehicle Update.


In this Section the concepts of wheel raycast filtering shall be introduced. Following this, the steps required to add filter data to a drivable surface and configure the filter data of vehicle shapes will be described.

The key goal of filtering for vehicles is to configure raycasts and collisions in such a way that raycasts along the suspension travel directions interact with drivable surfaces while also ensuring that the wheel shapes themselves never collide with drivable surfaces. This allows vehicles on drivable surfaces to be supported by spring forces arising from raycast intersections without interference from wheel shape intersection.

A simulation filter is required for the wheel shapes to ensure they do not collide with drivable surfaces:

PxFilterData wheelCollFilterData;

Similarly, a simulation filter is required for the chassis shapes but this time it is desired that the chassis shapes do collide with drivable surfaces:

PxFilterData chassisCollFilterData;

The combination of simulation filter shader:

if ((filterData0.word0 != 0 || filterData1.word0 != 0) &&
        !(filterData0.word0&filterData1.word1 || filterData1.word0&filterData0.word1))
        return PxFilterFlag::eSUPPRESS;

with the definitions of the COLLISION_FLAG_DRIVABLE_OBSTACLE_AGAINST and COLLISION_FLAG_WHEEL_AGAINST flags enforces the rule that the wheel shapes do not collide with the drivable surfaces, while also allowing the chassis shapes to collide with drivable surfaces.

In addition to simulation filter data, it is necessary to configure query filter data for the vehicle wheel raycasts. Drivable surfaces, such as the terrain and ramp obstacles, are assigned query filter data as follows:

PxFilterData qryFilterData;

Non-drivable surfaces, such as dynamic boxes and pendula in SampleVehicle, are also assigned query filter data:

PxFilterData qryFilterData;

In SampleVehicle, the vehicle shapes have been assigned query filter data that forbids vehicles being treated as drivable surfaces:

PxFilterData vehQryFilterData;

It is not at all necessary to stop cars driving on other cars. In SampleVehicle this choice has been made for reasons of simplicity but it would be straightforward to configure the scene queries to allow cars to drive on other cars. Care must be taken, however, to configure the filters to ensure that wheel raycasts only interact with the shapes of other vehicles. If a raycast was to intersect the geometry of the vehicle that issued the raycasts then there is a strong possibility that the intersection with the drivable terrain would be rejected in favour of a deeper intersection with the vehicle geometry.

Similar to the filter shader described above, a query filter shader must also be specified:

static PxQueryHitType::Enum SampleVehicleWheelRaycastPreFilter(
        PxFilterData filterData0,
        PxFilterData filterData1,
        const void* constantBlock, PxU32 constantBlockSize,
        PxQueryFlags& queryFlags)
        //filterData0 is the vehicle suspension raycast.
        //filterData1 is the shape potentially hit by the raycast.
        return ((0 == (filterData1.word3 & SAMPLEVEHICLE_DRIVABLE_SURFACE)) ? PxQueryHitType::eNONE : PxQueryHitType::eBLOCK);

The vehicle SDK ensures that filterData0 in the above function is always assigned the query filter data of the wheel that is issuing the raycast. For the case of SampleVehicle this means that filterData0 will always have the properties of a non-drivable surface because the wheels have been set up to be non-drivable in the sample.

Deployment of the query filters will be discussed in Section Vehicle Update.

Vehicle Update

It has already mentioned that vehicles are updated in two stages: specific vehicle code that updates the vehicle internal dynamics and computes forces/torques to apply to the vehicle's rigid body representation, followed by an SDK update that accounts for the applied forces/torques as well as collision with other scene bodies. In this Section these separate update phases will be discussed in some detail.

Beginning with the vehicle dynamics update, it is necessary to initialise and execute batched raycasts for suspension line intersection with drivable shape surfaces:

void SampleVehicle_VehicleManager::suspensionRaycasts(PxScene* scene)
        //Create a scene query if we haven't already done so.

Creating a batched scene query is discussed elsewhere in the guide and in the api documentation. The key points to note here are that SampleVehicleWheelRaycastPreFilter is used as the shader for the the batched scene query, and that buffers have been pre-allocated to provide enough memory for a single raycast hit for each active wheel.

Having completed setting up the scene queries, the scene queries can now be issued for all vehicles:


There is some freedom in the order in which raycasts can be issued relative to the vehicle dynamics update. In a real-world situation it might be that raycasts can be issued on a separate thread at the end of the update loop so that they are ready for the beginning of the next. However, this really all depends on the threading environment and the ordering of rigid body updates.

Prior to the first time that a vehicle is updated in PxVehicleUpdates, it must have already performed suspension line raycasts at least once with PxVehicleSuspensionRaycasts. In subsequent updates it is not strictly necessary to issue fresh raycasts because each vehicle caches raycast hit planes that can be re-used. It is recommended that there is a one-to-one correspondence between raycast completion and updates for each vehicle except for the case of vehicles that only require a low level of detail. This might include cars that are far from the camera or where it is known that the vehicle is driving on geometry with high spatial coherence. Support for vehicles that require only a low level of detail is discussed in Section Vehicle Level of Detail.

With the raycasts complete the update of the vehicles can now be performed:

void SampleVehicle_VehicleManager::update(const PxF32 timestep, const PxVec3& gravity)

In Section Tire Friction On Drivable Surfaces it was discussed how to set up friction for different combinations of drivable surface type and tire type. The parameter mSurfaceTirePairs in the above code shows the application of the friction values and of the mapping of PxMaterial intances to integer values representing surface type.

The mVehicles buffer is an array of pointers to any type of vehicle; that is, PxVehicleDrive4W, PxVehicleDriveNW, PxVehicleDriveTank or PxVehicleNoDrive. The length of the buffer (the number of vehicles in the array) is denoted by mNumVehicles. Each of these vehicles will be updated by a single timestep. More specifically, the update procedure modifies persistent dynamics data stored in the vehicle such as wheel rotation speed/angle and engine rotation speed. The update procedure also modifies the associated rigid body with either a force and torque to be applied in the next sdk update or with an immediate update of rigid body linear and angular velocity. Here, the update mode set by PxVehicleSetUpdateMode determines whether a force or velocity modification will be applied to the rigid body. Finally, the update procedure sets the local pose of all PxShapes of the vehicle's rigid body that have been identified as wheel shapes through the PxVehicleWheelsSimData::setWheelShapeMapping function. The pose is determined by the steer, camber, and rotation angles, and attempts to place the wheel geometry exactly on the contact plane identified by the raycast that was issued along the suspension line. The local pose is, of course, contrained by the limits of suspension travel.

The PxVehicleUpdates function generates a significant amount of non-persistent wheel data that can be used to help build and render a snapshot of vehicle state. Examples of non-persistent wheel data include the PxShape, PxActor, and PxMaterial of the drivable surface experienced by the tire; the lateral and longitudinal tire slip used in the tire force calculation; the tire friction; or the suspension force. This data is all recorded in the mVehicleWheelQueryResults buffer. This last argument in the PxVehicleUpdates function allows each vehicle to be temporarily associated with a data block large enough to record non-persistent wheel data for each wheel of the vehicle. It is not obligatory to record non-persistent data for later query. Indeed, it is perfect legal to associate a vehicle with a NULL data block to avoid storing of non-persistent wheel data. This feature allows memory budgets to be targeted at the vehicles of highest interest. Non-persistent wheel data queries are discussed in more detail in Section Vehicle State Queries .

A second code-path has been implemented to allow the update of each vehicle and to record telemetry data for a single specific vehicle:

void SampleVehicle_VehicleManager::updateAndRecordTelemetryData
(const PxF32 timestep, const PxVec3& gravity, PxVehicleWheels* focusVehicle, PxVehicleTelemetryData* telemetryData)
        PX_ASSERT(focusVehicle && telemetryData);

        //Update all vehicles except for focusVehicle.
        PxVehicleWheels* vehicles[MAX_NUM_4W_VEHICLES+MAX_NUM_6W_VEHICLES];
        PxVehicleWheelQueryResult vehicleWheelQueryResults[MAX_NUM_4W_VEHICLES+MAX_NUM_6W_VEHICLES];
        PxVehicleWheelQueryResult focusVehicleWheelQueryResults[1];
        PxU32 numVehicles=0;
        for(PxU32 i=0;i<mNumVehicles;i++)

        //Update the vehicle for which we want to record debug data.    PxVehicleUpdateSingleVehicleAndStoreTelemetryData(timestep,gravity,*mSurfaceTirePairs,focusVehicle,focusVehicleWheelQueryResults,*telemetryData);

The function:

//Update the vehicle for which we want to record debug data.

updates a single vehicle and collects telemetry data for that vehicle. The remaining cars, those from whom telemetry data is not collected, are updated in the usual way. Telemetry data shall be discussed in more detail in Section Telemetry.

To ensure thread safety with the telemetry data collection it is important that the update with telemetry data and the regular update run sequentially. Telemetry data is unlikely to be collected in the final build of a real game so this ought to have no significant impact on release performance. Further, to ensure thread safety, telemetry updates must also run sequentially.

The code discussed so far in this Section performs raycasts for each wheel and uses the results to compute tire and suspension forces to apply to each rigid body actor. Simultaneous to the computation of tire and suspension forces, wheel and engine rotation speeds are also updated. None of this code, however, advances the vehicle through the game world. This advancement is performed by the PhysX SDK update, where vehicles are represented by rigid body actors. There isn't too much to say here except that the SDK update and the vehicle dynamics update must run sequentially. It ought not to matter in which order they are updated in a game update loop or which is updated first. It is worth bearing in mind, though, that the vehicle dynamics update poses the wheel shapes along the suspension travel using the results of the suspension line raycasts. This might have an impact on the desired ordering of vehicle update and vehicle render.

Tire Shaders

It is possible to replace the default tire model used by PhysX vehicles with custom models. This requires a shader function that can be set per-vehicle along with shader data that must be set per-wheel:

void PxVehicleWheelsDynData::setTireForceShaderFunction(PxVehicleComputeTireForce tireForceShaderFn)
void PxVehicleWheelsDynData::setTireForceShaderData(const PxU32 tireId, const void* tireForceShaderData)

The shader function must implement this function prototype:

typedef void (*PxVehicleComputeTireForce)
(const void* shaderData,
 const PxF32 tireFriction,
 const PxF32 longSlip, const PxF32 latSlip, const PxF32 camber,
 const PxF32 wheelOmega, const PxF32 wheelRadius, const PxF32 recipWheelRadius,
 const PxF32 restTireLoad, const PxF32 normalisedTireLoad, const PxF32 tireLoad,
 const PxF32 gravity, const PxF32 recipGravity,
 PxF32& wheelTorque, PxF32& tireLongForceMag, PxF32& tireLatForceMag, PxF32& tireAlignMoment);

The vehicle update code will call the shader function for each wheel with the shader data for that wheel.


The purpose of the telemetry data is to expose the inner dynamics of the car and aid handling tuning through the use of telemetry graphs. In this Section initialisation, collection, and rendering of telemetry data shall be discussed.

In Section Vehicle Update the possibility of collecting telemetry data for a single vehicle was introduced with this code:

//Update the vehicle for which we want to record debug data.

Setting up the telemetry data is relatively straightforward:

mTelemetryData4W = PxVehicleTelemetryData::allocate(4);

const PxF32 graphSizeX=0.25f;
const PxF32 graphSizeY=0.25f;
const PxF32 engineGraphPosX=0.5f;
const PxF32 engineGraphPosY=0.5f;
const PxF32 wheelGraphPosX[4]={0.75f,0.25f,0.75f,0.25f};
const PxF32 wheelGraphPosY[4]={0.75f,0.75f,0.25f,0.25f};
const PxVec3 backgroundColor(255,255,255);
const PxVec3 lineColorHigh(255,0,0);
const PxVec3 lineColorLow(0,0,0);

The sizes, positions, and colors are all values that are used to render the graphs. The exact values of these fields will depend on the coordinate system and color coding being used to visualize the telemetry data. In the above example, the coordinates have been configured to render an engine-related graph in the centre of the screen. Screen coordinates have also been specified for rendering data associated with each of the four wheels. The coordinates used in the vehicle sample visualize the graph data of the front left wheel at the top left-hand side of the screen, the front right wheel at the top right-hand side of the screen, and the rear wheels at the bottom left and right.

The following enumerated lists detail the telemetry data that is collected:



Data is collected for suspension jounce, suspension force, tire load, normalised tire load, wheel rotation speed, tire friction, tire longitudinal slip, tire longitudinal force, tire lateral slip, tire lateral force, and tire aligning moment. Data is also collected separately for engine revs, engine drive torque, clutch slip, applied acceleration/brake/handbrake/steer, and gear ratio. For each graph all associated data is collected in separate graph channels that can be accessed after the update is complete.

The data for a particular graph channel is computed as follows:


This code computes a sequence of screen coords in the format [x0,y0,x1,y1,x2,y2,....xn,yn] that represent the points of the specified graph channel of the engine's graph data. In SampleVehicle only a single graph channel is rendered at a time with the possibility of iterating through the channels from the keyboard or gamepad. It is important to note that the data for all the channels (see the above enumerated lists) is available so it is possible to visualize simultaneously many channels for each graph. The above function also stores a color for each graph point. The idea here is that each graph channel is assigned a value that separates high and low values, along with a color that is assigned to points with high values, and a color that is assigned to points with low values. This is a useful feature if multiple channnels are being simultaneously visualized. In SampleVehicle the value that separates high and low values for each channel is hard-coded within the optional setup function PxVehicleTelemetryData::setup. With this function the jounce channel, for example, uses zero as the demarcation between high value and low value. This allows spring compression and elongation to be easily recognised from the different colors used to represent these two states. Users are free to set up graphs as required if the provided graph setup is unsuited to their needs.

Vehicle State Queries

It has already been mentioned that a PhysX vehicle is a multi-shape actor with a single shape for each wheel and multiple shapes for the chassis. It has also been mentioned that the local poses of the wheels are set during the vehicle dynamics update to reflect the results of the suspension raycasts. The transforms used to render the vehicle can readily be found by querying the shapes of the vehicle:

PxShape* carShapes[PX_MAX_NUM_WHEELS+1];
const PxVehicleWheels& vehicle=*mVehicleManager.getVehicle(carId);
const PxU32 numShapes=vehicle.getRigidDynamicActor()->getNbShapes();

As previously discussed in Section Vehicle Update, non-persistent wheel data is generated each update and stored in data blocks that can be temporarily associated with vehicles. Much of this data is useful for triggering audio and graphics effects based on the state of the vehicle. For example, particles with a specific texture might be emitted from a wheel if the wheel is found to be spinning at high speed on a specific surface type. This could be determined by querying PxWheelQueryResult::longSlip and PxWheelQueryResult::tireSurfaceType. Alternatively, when the suspension spring hits its limit (PxWheelQueryResult::suspJounce) a special audio path could be triggered. Another example might be to add a decal to the contact surface when a tire is skidding badly (PxWheelQueryResult::longSlip, PxWheelQueryResult::tireContactPoint, PxWheelQueryResult::tireContactShape).

Persistent vehicle state data such as engine rotation speed and wheel rotation speed and angle can also be queried:

PX_FORCE_INLINE PxReal PxVehicleDriveDynData::getEngineRotationSpeed() const;
PxReal PxVehicleWheelsDynData::getWheelRotationSpeed(const PxU32 wheelIdx) const;
PxReal PxVehicleWheelsDynData::getWheelRotationAngle(const PxU32 wheelIdx) const;

A number of helper functions are also available to determine the velocity components of the vehicle:

PxReal PxVehicleWheels::computeForwardSpeed() const;
PxReal PxVehicleWheels::computeSidewaysSpeed() const;

Finally, it is possible to determine if a vehicle is in the air or if it has wheel contact with the ground. This requires knowledge of the non-persistent suspension jounce data and the suspension travel limit of the vehicle:

bool PxVehicleIsWheelInAir(const PxVehicleWheels& vehicle, const PxVehicleWheelQueryResult& vehWheelQueryResults, const PxU32 wheelId);

Vehicle Controls

In this Section setting the control values used to drive a vehicle shall be discussed. These are accelerate, brake, handbrake, steer, gear change up, and gear change down. The simplest way to do this is to use the following function:

void PxVehicleDriveDynData::setAnalogInput(const PxReal analogVal, const PxU32 type);

One of the difficulties with vehicle dynamics in games is knowing how to filter the raw controller data in a way that results in pleasing handling. Players, for example, often demonstrate their eagerness to accelerate by pressing very quickly on the accelerator trigger in a way would never happen in a real car. This rapid acceleration can have a counter-producive effect because the resulting wheel spin reduces the lateral and longitudinal forces that can be generated by the tire. To help overcome some of these problems some optional code has been provided to filter the control data from keyboard and gamepad.

A solution to the problem of filtering controller input data is to assign a rise and fall rate to each button or pad. For analog values under digital control it is possible to simply increase or decrease the analog value at a specified rate depending on whether the digital input is on or off. For analog values under analog control it makes more sense to blend from the previous input value to the current input at a specified rate. A slight complication to this simple model is that the difficulty of achieving a large steer angle at large speed must also be modelled. One technique to achieve this would be to model the forces from the tires' aligning moments and apply these to a steering linkage model. This sounds rather complicated and quite difficult to tune. A simpler solution might be to scale the filtered steer value by another value in range (0,1) that decreases at high speed. This simpler method has been implemented in the helper classes and functions.

Rise and fall rates for digital and analog control have been hard-coded in SampleVehicle:

PxVehicleKeySmoothingData gKeySmoothingData=
                3.0f,   //rise rate eANALOG_INPUT_ACCEL
                3.0f,   //rise rate eANALOG_INPUT_BRAKE
                10.0f,  //rise rate eANALOG_INPUT_HANDBRAKE
                2.5f,   //rise rate eANALOG_INPUT_STEER_LEFT
                2.5f,   //rise rate eANALOG_INPUT_STEER_RIGHT
                5.0f,   //fall rate eANALOG_INPUT__ACCEL
                5.0f,   //fall rate eANALOG_INPUT__BRAKE
                10.0f,  //fall rate eANALOG_INPUT__HANDBRAKE
                5.0f,   //fall rate eANALOG_INPUT_STEER_LEFT
                5.0f    //fall rate eANALOG_INPUT_STEER_RIGHT

PxVehiclePadSmoothingData gCarPadSmoothingData=
                6.0f,   //rise rate eANALOG_INPUT_ACCEL
                6.0f,   //rise rate eANALOG_INPUT_BRAKE
                12.0f,  //rise rate eANALOG_INPUT_HANDBRAKE
                2.5f,   //rise rate eANALOG_INPUT_STEER_LEFT
                2.5f,   //rise rate eANALOG_INPUT_STEER_RIGHT
                10.0f,  //fall rate eANALOG_INPUT_ACCEL
                10.0f,  //fall rate eANALOG_INPUT_BRAKE
                12.0f,  //fall rate eANALOG_INPUT_HANDBRAKE
                5.0f,   //fall rate eANALOG_INPUT_STEER_LEFT
                5.0f    //fall rate eANALOG_INPUT_STEER_RIGHT

A look-up table has also been specified to describe the maximum steer as a function of speed:

PxF32 gSteerVsForwardSpeedData[2*8]=
        0.0f,           0.75f,
        5.0f,           0.75f,
        30.0f,          0.125f,
        120.0f,         0.1f,
        PX_MAX_F32, PX_MAX_F32,
        PX_MAX_F32, PX_MAX_F32,
        PX_MAX_F32, PX_MAX_F32,
        PX_MAX_F32, PX_MAX_F32
PxFixedSizeLookupTable<8> gSteerVsForwardSpeedTable(gSteerVsForwardSpeedData,4);

Using a PxVehicleDrive4WRawInputData instance it is straightforward to record the user inputs in the event a keyboard is used:


or in the event that a gamepad is used:

rawInputData.setAnalogHandbrake(mHandbrake ? 1.0f : 0.0f);

Here, rawInput data is an instance of the vehicle SDK helper class PxVehicleDrive4WRawInputData.

The vehicle SDK offers two optional functions to smooth the keyboard or gamepad data and apply the smoothed input values to the PhysX vehicle. If the vehicle is controlled by digital inputs then the following function is used in SampleVehicle:


while gamepad controllers employ the following code:


3-Wheeled, N-Wheeled Cars, and N-Wheeled Tanks

The primary focus until now has been on how to create, simulate and query 4-wheeled cars. This Section shall focus instead on cars that either have only 3 wheels or more than 4 wheels. To complete the discussion many-wheeled tanks shall be introduced.

Utility functions have been provided to quickly configure 3-wheeled cars. The basic idea is to start with a 4-wheeled car and then disable one of the wheels:

void PxVehicle4WEnable3WTadpoleMode(PxVehicleWheelsSimData& wheelsSimData, PxVehicleWheelsDynData& wheelsDynData, PxVehicleDriveSimData4W& driveSimData);
void PxVehicle4WEnable3WDeltaMode(PxVehicleWheelsSimData& wheelsSimData, PxVehicleWheelsDynData& wheelsDynData, PxVehicleDriveSimData4W& driveSimData);

These functions ensure that no raycast hits are returned for the disabled wheel and additionally do some other work to decouple the disabled wheel from the differential, disable ackermann correction, re-position the opposite remaining wheel to the centre of the axle, and adjust the suspension of the opposite remaining wheel to compensate for the missing suspension of the disabled wheel. Further wheels could in theory be removed with custom code to create a vehicle with 1 or 2 effective wheels. At that point, however, extra balancing code would be required to prevent the vehicle falling over.

Some care must be taken when removing a wheel because the PxVehicleUpdates function has a number of requirements that must be sastisfied for all vehicles. The first requirement is that any wheel that has been disabled must not be associated with a PxShape. This is a safety feature that prevents PxVehicleUpdates attempting to set the local pose of a PxShape that may no longer be valid. The function PxVehicleWheelsSimData::setWheelShapeMapping can be used to satisfy this requirement. The second requirement is that any wheel that has been disabled must have zero wheel rotation speed. This can be satisfied by calling PxVehicleWheelsDynData::setWheelRotationSpeed for the relevant wheel. The final requirement is that disabled wheels must receive no drive torque. For tanks this requirement can actually be ignored because it is automatically enforced with custom tank code called by the PxVehicleUpdates function. For vehicles of type PxVehicleNoDrive the requirement on drive torque is fulfilled by ensuring that PxVehicleNoDrive::setDriveTorque is never called with a non-zero torque value. Further, the drive torque requirement can be readily fulfilled for vehicles of type PxVehicleDriveNW by ensuring that the differential is disconnected from the disabled wheel. This is achieved using the function PxVehicleDifferentialNWData::setDrivenWheel.

Configuring the differential of a PxVehicle4W to ensure that no drive torque is delivered to a disabled wheel is a little more complex because there are many different ways to achieve this. If the wheel is not a driven wheel then disabling the wheel automatically satisfies the drive torque requirement because such wheels can never be connected to the differential. On the other hand, if the wheel has index eFRONT_LEFT or eFRONT_RIGHT or eREAR_LEFT or eREAR_RIGHT then the differential does need to be modified to enforce the requirement. One way to do this is to set up the differential so that it delivers torque to only the rear(front) wheels if a front(rear) wheel has been disabled. This can be readily implemented by selecting front-wheel drive mode or rear-wheel drive mode as appropriate:

PxVehicleDifferential4WData diff = myVehicle.getDiffData();
if(PxVehicleDrive4WWheelOrder::eFRONT_LEFT == wheelToDisable || PxVehicleDrive4WWheelOrder::eFRONT_RIGHT == wheelToDisable)
        if(PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD == diff.mType ||
          PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD == diff.mType ||
          PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD == diff.mType ||
          PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD == diff.mType)
                  diff.mBias = 1.3f;
                  diff.mRearLeftRightSplit = 0.5f;
              diff.mType = PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD;//could also be PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_REARWD;
else if(PxVehicleDrive4WWheelOrder::eREAR_LEFT == wheelToDisable || PxVehicleDrive4WWheelOrder::eREAR_RIGHT == wheelToDisable)
        if(PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD == diff.mType ||
           PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD == diff.mType ||
           PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD == diff.mType ||
           PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_REARWD == diff.mType)
                  diff.mBias = 1.3f;
                  diff.mFronteftRightSplit = 0.5f;
              diff.mType = PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD;//could also be PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD;

In some situations limiting the drove torque to just the front or rear wheels might not be acceptable. If only a single wheel has been disabled then it is possible to engage a drive mode where 3 wheels are driven. This can be achieved by modifying a differential that delivers torque to all four wheels (eDIFF_TYPE_LS_4WD or eDIFF_TYPE_OPEN_4WD) so that torque is only delivered to 3 wheels:

PxVehicleDifferential4WData diff = myVehicle.getDiffData();
if(PxVehicleDrive4WWheelOrder::eFRONT_LEFT == wheelToDisable || PxVehicleDrive4WWheelOrder::eFRONT_RIGHT == wheelToDisable)
        if(PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD == diff.mType ||
          PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD == diff.mType)
                  if(PxVehicleDrive4WWheelOrder::eFRONT_LEFT == wheelToDisable)
                      diff.mFrontLeftRightSplit = 0.0f;
                      diff.mFrontLeftRightSplit = 1.0f;
else if(PxVehicleDrive4WWheelOrder::eREAR_LEFT == wheelToDisable || PxVehicleDrive4WWheelOrder::eREAR_RIGHT == wheelToDisable)
        if(PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD == diff.mType ||
           PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD == diff.mType)
                  if(PxVehicleDrive4WWheelOrder::eREAR_LEFT == wheelToDisable)
                      diff.mRearLeftRightSplit = 0.0f;
                      diff.mRearLeftRightSplit = 1.0f;

In some situations it will make sense to disable Ackermann steer correction if the disabled wheel was able to steer. In particular, if the remaining wheel of the front or rear axle is re-positioned so that it is at the centre of the axle then it would almost certainly follow that Ackermann correction would be disabled. This can be achieved by setting the accuracy to zero (PxVehicleAckermannGeometryData::mAccuracy). The role of Ackermann steer correction, however, really needs to be determined on a case by case basis.

In addition to removing wheels from a vehicle, it is also possible to construct a PxVehicleDrive4W with more than 4 wheels but with the caveat that only 4 wheels may be driven. As a consequence of this caveat the functionality of the extra wheels is slightly limited compared to the first 4 wheels. More specifically, only the first 4 wheels are connected to the differential or the steering; that is, only the first block of 4 wheels can experience a drive torque or a steer angle and only the first block of 4 wheels participate in the Ackermann steering correction. As a consequence, the extra wheels play an identical role to the rear wheels of a 4-wheeled car that has front-wheel drive or the front wheels or a 4-wheeled car that has rear-wheel drive. Adding extra wheels does not preclude the ability to call PxVehicle4WEnable3WTadpoleMode or PxVehicle4WEnable3WDeltaMode. These functions, however, are hard-coded to disable one of the 4 wheels that could be connected to the steering and driven through the differential.

The following pseudo-code illustrates the key steps in the creation of a 6-wheeled PxVehicleDrive4W vehicle:

PxVehicleWheelsSimData* wheelsSimData=PxVehicleWheelsSimData::allocate(6);
PxVehicleDriveSimData4W driveSimData;
PxVehicleDrive4W* car = PxVehicleDrive4W::allocate(6);
PxRigidDynamic* vehActor=createVehicleActor6W();

While the PxVehicleDrive4W allows cars with any number of wheels to be created and simulated it only allows 4 of those wheels to be driven by engine torques via the differential. The vehicle type PxVehicleDriveNW has been introduced to solve this specific limitation. This vehicle class makes use of the differential type PxVehicleDifferentialNW, a class that allows any or all of the vehicle's wheels to be coupled to the differential with the limitation that the torque available at the differential is always divided equally among the wheels that are coupled to the differential. The generality of PxVehicleNW precludes advanced features such as limited slip differentials and Ackermann steering correction, meaning that only a simple equal-split differential model can be provided at present.

The following pseudo-code illustrates the key steps in the creation of a 6-wheeled PxVehicleDriveNW vehicle:

PxVehicleWheelsSimData* wheelsSimData=PxVehicleWheelsSimData::allocate(6);
PxVehicleDriveSimDataNW driveSimData;
PxVehicleDriveNW* car = PxVehicleDriveNW::allocate(6);
PxRigidDynamic* vehActor=createVehicleActorNW();

The PhysX vehicle SDK also supports tanks through the use of the PxVehicleDriveTank class. Tanks are different to multi-wheeled vehicles in that the wheels are all driven through the differential in a way that ensures that all the wheels on the left-hand side have the same speed, and all the wheels on the right-hand have the same speed. This extra constraint on wheel speed mimics the effect of the caterpillar tracks but avoids the expense of simulating the jointed track structure. Adding the geometry of the caterpillar tracks is as easy as adding an actor shape down each side and setting up the collision and query filters as appropriate for the tracks. The motion of the caterpillar tracks could be rendered with a scrolling texture, safe in the knowledge that all wheels have the same speed, just as though they were properly constrained by the track rotation.

Creating a PxVehicleDriveTank instance is very similar to creating a PxVehicleDrive4W instance with the exception that tanks have no concept of extra wheels that are not connected to the differential: all tank wheels are driven. The following code illustrates how to set up a 12-wheeled tank:

PxVehicleWheelsSimData* wheelsSimData = PxVehicleWheelsSimData::allocate(12);
PxVehicleDriveSimData4W driveSimData;
PxVehicleDriveTank* tank = PxVehicleDriveTank::allocate(12);
PxRigidDynamic* vehActor=createVehicleActor12W();

Controlling a tank is quite different to controlling a car because tanks have a completely different steering mechanism: the turning action of a tank arises from the difference in left and right wheel speeds, while cars turn by the action of a steering wheel that orientates the front wheels relative to the forward motion of the vehicle. This requires quite a different set of helper classes and functions to smooth the control inputs. Instead of a PxVehicleDrive4WRawInputData for N-wheeled cars, PxVehicleDriveTankRawInputData is used for N-wheeled tanks. Further, instead of PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs/PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs for N-wheeled cars, PxVehicleDriveTankSmoothDigitalRawInputsAndSetAnalogInputs and PxVehicleDriveTankSmoothAnalogRawInputsAndSetAnalogInputs are used to smooth the keyboard and gamepad inputs for tanks.

PhysX tanks currently support two drive models: eDRIVE_MODEL_STANDARD and eDRIVE_MODEL_SPECIAL. The drive model eDRIVE_MODEL_SPECIAL allows the tank tracks to rotate in different directions, while eDRIVE_MODEL_STANDARD does not. These two modes result in quite different turning actions. Drive model eDRIVE_MODEL_STANDARD simulates the usual turning action of a tank: pushing forward on the left(right) stick drives the left(right) wheels forward, while pulling back on the right(left) stick applies the brake to the right(left) wheels. eDRIVE_MODEL_SPECIAL, on the other hand, simulates a more exotic turning action where pushing back on the right(left) stick drives the right(left) wheels backwards. This can result in a turning circle focused at the centre of the tank. The smallest possible turning circle of a tank in eDRIVE_MODEL_STANDARD will have a focus at a point along one of the caterpillar tracks, depending on whether the tank is turning left or right.


The class PxVehicleNoDrive has been introduced to provide a close approximation to backwards compatibility with the interface to the 2.8.x NxWheelShape class. It is essentially a rigid body with N suspension/wheel/tire units attached. It's behaviour is identical to that of a PxVehicleDrive4W which is permanently in neutral gear so that the engine has no influence on the wheels and the wheels are coupled only through the motion of the rigid body. This comes, of course, without the storage overhead of Ackermann steering correction data, engine torque curve data etc. The idea is that users can develop their own drive model on top of already existing vehicle code to manage the suspension raycasts, tire and suspension force computation, and PhysX SDK integeration.

The key function are the application of per wheel drive and brake torques and per wheel steer angles:

\brief Set the brake torque to be applied to a specific wheel
void setBrakeTorque(const PxU32 id, const PxReal brakeTorque);

\brief Set the drive torque to be applied to a specific wheel
void setDriveTorque(const PxU32 id, const PxReal driveTorque);

\brief Set the steer angle to be applied to a specific wheel
void setSteerAngle(const PxU32 id, const PxReal steerAngle);

Tuning Guide

This Sections describes the effect of the editable vehicle parameters of the data structures in PxVehicleComponents.h.



This is the distance in metres between the centre of the wheel and the outside rim of the tire. It is important that the value of the radius closely matches the radius of the render mesh of the wheel. Any mismatch will result in the wheels either hovering above the ground or intersecting the ground. Ideally, this parameter will be exported from the 3D modeller.


This is the full width of the wheel in metres. This parameter has no bearing on the handling but is a very useful parameter to have when trying to render debug data relating to the wheel/tire/suspension. Without this parameter it would be difficult to compute coordinates for render points and lines that ensure their visibility. Ideally, this parameter will be exported from the 3D modeller.


This is the combined mass of the wheel and the tire in kg. Typically, a wheel has mass between 20Kg and 80Kg but can be lower and higher depending on the vehicle.


This is the component of the wheel's moment of inertia about the rolling axis. Larger values make it harder for the wheel to rotate about this axis, while lower values make it easier for the wheel to rotate about the rolling axis. Another way of expressing this is that a high MOI will result in less wheel spin when stamping on the accelerator because it is harder to make the wheel spin. Conversely, lower values of MOI will result in more wheel spin when stamping on the accelerator.

If the wheel is approximately cylindrical then a simple formula can be used to compute MOI:

MOI = 0.5 * Mass * Radius * Radius

There is no reason, however, to rely on equations to compute this value. A good strategy for tuning this number might to be start with the equation above and then make small tweaks to the value until the handling is as desired.


This value describes how quickly a freely spinning wheel will come to rest. The damping rate describes the rate at which a freely spinning wheel loses rotational speed. Here, a freely spinning wheel is one that experiences no forces except for the damping forces arising from the wheel's internal bearings. Higher damping rates result in the wheel coming to rest in shorter times, while lower damping rates result in the wheel maintaining speed for longer. Values in range (0.25, 2) seem like sensible values. Experimentation is always a good idea, even outside this range. Always exercise some caution with very small damping rates. In particular, a damping rate of exactly 0 should be avoided.


This is the value of the torque applied to the wheel when the brakes are maximally applied. Higher torques will lock the wheel quicker when braking, while lower torques will take longer to lock the wheel. This value is strongly related to the wheel MOI because the MOI determines how quickly the wheel will react to applied torques.

A value of around 1500 is a good starting point for a vanilla wheel but a google search will reveal typical braking torques. One difficulty is that these are often expressed by manufacturers as braking horsepower or in "pounds inches". The values required here are in "Newton metres".


This is the same as the max brake torque except for the handbrake rather than the brake. Typically, for a 4-wheeled car, the handbrake is stronger than the brake and is only applied to the rear wheels. A value of 4000 for the rear wheels is a good starting point, while a value of 0 is necessary for the front wheels to make sure they do not react to the handbrake.


This is the value of the steer angle of the wheel (in radians) when the steering wheel is at full lock. Typically, for a 4-wheeled car, only the front wheels respond to steering. In this case, a value of 0 is required for the rear wheels. More exotic cars, however, might wish front and rear wheels to respond to steering. A value in radians equivalent to somewhere between 30 degrees and 90 degrees seems like a good starting point but it really depends on the vehicle being simulated. Larger values of max steer will result in tighter turns, while smaller values will result in wider turns. Be aware, though, that large steer angles at large speeds are likely to result in the car losing traction and spinning out of control, just as would happen with a real car. A good way to avoid this is to filter the steer angles passed to the car at run-time to generate smaller steer angles at larger speeds. This strategy will simulate the difficulty of achieving large steer angles at high speeds (at high speeds the wheels resist the turning forces applied by the steering wheel).


This is the angle of the wheel (in radians) that occurs with no steer applied. The toe angle can be used to help the car straighten up after coming out of a turn. This is a good number to experiment with but is best left at 0 unless detailed tweaks are required.

To help the car straighten up apply a small negative angle to one of the front wheels and a small positive angle to the other front wheel. By choosing which wheel takes the positive angles, and which the negative, it is straightforward to make the wheels either "toe'in" or "toe'out". A "toe-in" configuration, the front wheels pointing slightly towards each other, should help the car straighten up after a turn but at the expense of making it a little harder to turn in the first place. A "toe-out" configuration can have the opposite effect. Toe angles greater than a few degrees are best avoided.


void setSuspTravelDirection(const PxU32 id, const PxVec3& dir):

This is the direction of the suspension in the downward direction in the rest configuration of the vehicle. A vector that points straight downwards is a good starting point.

void setSuspForceAppPointOffset(const PxU32 id, const PxVec3& offset):

This is the application point of the suspension force, expressed as an offset vector from the center of mass of the vehicle's rigid body. Another way of expressing this is to start at the center of mass of the rigid body, then move along the offset vector. The point at the end off the offset vector is the point at which suspension forces will be applied.

In a real vehicle the suspension forces are mediated through the suspension strut. These are often incredibly complex mechanical systems that are computationally expensive to simulate. As a consequence, instead of modelling the details of the suspension strut, it makes sense to assume that the suspension strut has an effective point at which it applies the force to the rigid body. Choosing that point, however, needs careful consideration. At the same time, it opens up all sorts of tweaking possibilities, freed from the constraints of the real world.

Deciding on the suspension force application point requires some thought. The suspension is very close to the wheel so the wheel center is a good starting point. Consider a line through the wheel center and along the suspension travel direction. Somewhere along this line seems like an even better idea for the application point, albeit not completely scientific. For a standard 4-wheeled car it makes sense that the application point is somewhere above the wheel center but below the centre of mass of the rigid body. It is probably above the wheel centre because the suspension is mostly above this point. It can be assumed that it is somewhere below the rigid body centre of mass because otherwise vehicles would lean out of the turn rather than in to the turn. This narrows down the application point to really quite a small section of a known line.

When editing the suspension force application point it is important to bear in mind that lowering the app point too far will result in cars leaning more into the turn. This can have a negative effect on handling because the inner wheel can take so much load that the response saturates, while the outer wheel ends up with reduced load and reduced turning force. The result is poor cornering. Conversely, setting the app point too high will result in cornering that looks unnatural. The aim is to achieve a good balance.

void setTireForceAppPointOffset(const PxU32 id, const PxVec3& offset):

This is almost the same as the suspension force app point except for the lateral and longitudinal forces that develop on the tire. A good starting point is to duplicate the suspension force application point. Only for really detailed editing is it advised to start tweaking the tire force app offset independently of the suspension force app offset.

void setWheelCentreOffset(const PxU32 id, const PxVec3& offset):

This is the centre of the wheel at rest position, expressed as an offset vector from the vehicle's centre of mass.



This is the mass in kg that is supported by the suspension spring.

A vehicle with rigid body centre of mass at the centre of the four wheels would typically be equally supported by each of the suspension springs; that is, each suspension spring supports 1/4 of the total vehicle mass. If the centre of mass was moved forward then it would be expected that the front wheels would need to support more mass than the rear wheels. Conversely, a centre of mass nearer the rear wheels ought to result in the rear suspension springs supporting more mass than at the front.



These values describe the maximum compression and elongation in metres that the spring can support. The total travel distance along the spring direction that is allowed is the sum of mMaxCompression and mMaxDroop.

A simple way to illustrate the maximum droop and compression values is to consider a car that is suspended in mid-air so that none of the wheels are touching the ground. The wheels will naturally fall downwards from their rest position until the maximum droop is reached. The spring cannot be elongated beyond this point. Now consider that the wheel is pushed upwards, first to its rest position, then further pushed until the spring can no longer be compressed. The displacement from the rest position is the maximum compression of the spring.

It is important to choose the maximum compression value so that the wheel is never placed where the visual mesh of the wheel intersects the visual meshes of the car chassis. Ideally, these values will be exported from the 3d modeller.


This value describes the strength of the suspension spring. The spring strength has a profound influence on handling by modulating the time it takes for the vehicle to respond to bumps in the road and on the amount of load experienced by the tire.

Key to understanding the effect of spring strength is the concept of a spring's natural frequency. Consider a simple spring system, such as a pendulum swinging back and forth. The number of trips per second that the pendulum makes from full left to full right and then back again is called the natural frequency of the pendulum. A more powerful pendulum spring will result in the pendulum swinging faster, thereby increasing the natural frequency. Conversely, increasing the pendulum mass will result in a slower oscillation, thereby reducing the natural frequency.

In the context of a suspension spring supporting a fixed portion of vehicle mass, the strength of the spring will affect the natural frequency; that is, the rate at which the spring can respond to changes in load distribution. Consider a car taking a corner. As the car corners it leans in to the turn, putting more weight on the suspensions on the outside of the turn. The speed at which the spring reacts by applying forces to redistribute the load is controlled by the natural frequency. Very high natural frequencies, such as those on a racing car, will naturally produce twitchy handling because the load on the tires, and therefore the forces they can generate, is varying very rapidly. Very low natural frequencies, on the other hand, will result in the car taking a long time to straighten up even after the turn is complete. This will produce sluggish and unresponsive handling.

Another effect of strength and and natural frequency is the response of a car to a bump in the road. High natural frequencies can result in the car responding very strongly and quickly to the bump, with the wheel possibly even leaving the road for a short while. This not only creates a bumpy ride but also periods of time when the tire is generating no forces. Weaker springs will result in a smoother trip over the bump, with weaker but more constant tire forces. A balance must be found to tune the car for the expected types of turn and terrain.

The natural frequency of the spring presents a challenge for computer simulation. A smooth and stable simulation requires that the spring is updated at a frequency much greater than the spring's natural frequency. An alternative way of expressing this is to consider the period of the spring relative to the timestep of the simulation. The period of the spring is the time the spring takes to complete a single oscillation, and is mathematically equal to the reciprocal of the natural frequency. In order to achieve a stable simulation the spring must be sampled at several points during each oscillation. A natural consequence of this observation is that the simulation timestep must be significantly smaller than the period of the spring. To discuss this further it is helpful to introduce a ratio that describes the number of simulation updates that will occur during each spring oscillation. This ratio is simply the spring period divided by the timestep

alpha = sqrt(mSprungMass/mSpringStrength)/timestep

where sqrt(mSprungMass/mSpringStrength) is the period of the spring. An alpha value of 1.0 means that the chosen timestep and spring properties only allow a single sample of the spring during each oscillation. As described above, this is almost guaranteed to produce unstable behaviour. In fact, the argument presented so far suggests a value of alpha signifcantly greater than 1.0 is essential to produce a smooth simulation. The exact value of alpha at which stability emerges is very difficult to predict and depends on many other parameters. As a guide, however, it is recommended that the timestep and spring properties are chosen so that they produce an alpha value greater than 5.0; that is, a minimum of five simulation updates per spring cycle.

When tuning a suspension spring it can be very useful to use manafacturer data to discover typical values used across a range of vehicle types. This data is not always readily available. An alternative strategy would be to think in terms of the natural frequency of the spring by imagining how quickly the car would oscillate up and down if it was dropped onto the ground from a height of, say, 0.5m. The springs of a typical family car have natural frequency somewhere between 5 and 10; that is, such a car would make 5-10 oscillations per second if gently dropped to the ground. If the mass supported by the spring is already known then the spring strength can be calculated from the following equation

mSpringStrength = naturalFrequency * naturalFrequency * mSprungMass


This describes the rate at which the spring dissipates the energy stored in the spring.

Key to the understanding of damper rate are the concepts of under-damping, over-damping, and critical damping. An over-damped pendulum displaced from rest is unable to make a single back-and-forth trip before it dissipates all its energy, while an under-damped pendulum would be able to make at least a single back-and-forth trip. A critically damped pendulum makes exactly a single back-and-forth trip before expending all its energy.

For vehicle suspension springs, it is typically important to make sure that the spring has a damper rate that produces over-damping but not by too much. When cornering, for example, it is important that the spring doesn't over-respond by shifting the weight from the left suspension to the right suspension then back again. If this happened the tire load, and the forces generated, would be extremely variable, resulting in twitchy and uncontrollable handling. A very heavily over-damped spring, on the other hand, will feel sluggish and unresponsive.

The concept of critical damping can be used to help tune the damping rate of the spring. It is helpful to introduce a value known as the damping ratio, which helps to mathematically describe the under-damping, critical damping and over-damping regimes.

dampingRatio = mSpringDamperRate/[2 * sqrt(mSpringStrength * mSprungMass)]

A dampingRatio with value greater than 1.0 produces over-damping, a value of exactly 1.0 generates critical damping, and a value less than 1.0 is under-damped. It can be useful to first think about whether the spring will be under-damped or over-damped, then think about how far it will be from critical damping. This process allows a number to be subjectively applied to the damping ratio. From here the damping rate can be directly computed by rearranging the equation above

mSpringDamperRate = dampingRatio * 2 * sqrt(mSpringStrength * mSprungMass)

A typical family car is probably slightly over-damped, having dampingRatio with value perhaps just over 1.0. A guideline would be that values very far from critical damping are likely to be unrealistic and will either produce sluggish or twitchy handling. It is difficult to put an exact figure on this but somewhere between 0.8 and 1.2 seems like a good starting point for the damping ratio.




These values describe the camber angle of the wheels as a function of suspension spring compression. It is typical for the wheels of extended springs to camber inwards; that is, the left and right wheels almost seem to form the edges of a V shape when viewed from the front or rear along the forward axis of the vehicle. Compressed springs, on the other hand, typically camber outwards; that is, they almost form the outer edges of an A shape when when viewed from the front or rear along the forward axis of the vehicle.

These three values allow the camber angle to be computed for any value of spring compression using simple linear interpolation. At rest, when the spring is neither elongated or compressed, the camber angle is equal to mCamberAtRest. When the spring is compressed the camber is computed as a linear interpolation between mCamberAtRest and mCamberAtMaxCompression. When the spring is elongated the camber is computed as a linear interpolation between mCamberAtRest and mCamberAtMaxDroop.

The camber angle is used by the default tire model and is passed as a function argument to the tire shader. It is also used to set the local pose of the PxShape that geometrically represents the wheel.


The tire force computation is performed in two conceptual stages. The first stage of the computation independently computes the lateral and longitudinal components of the force using linear equations. These independent forces are computed by treating the tire as a linear system so that the force in each direction can be theoretically viewed as the product of a tire strength per unit slip and the slippage experienced by the tire. The second stage of the computation applies the rule that the combined tire force is limited by the product of the tire load and friction. Just as with rigid bodies, tires are able to resist greater horizontal forces when they experience a large normal load on a surface with high friction value. With this in mind the maximum resistance force for a tire can be approximated as the product of the normal load and the friction value. The default PhysX Vehicle tire model employs a series of smoothing functions to implement the normalization of the combined tire forces.

In addition to the lateral and longitudinal components of force a camber thrust force, arising from the camber angle of the tire, is also computed. Typically, this provides only a small correction to the effect of the lateral and longitudinal components. The camber force participates in the normalisation process.

The following tire parameters describe the computation of the independent lateral and longitudinal and camber components; that is, the first conceptual stage of the force computation. Reference is made throughout to the handling consequences of the normalisation process.


The longitudinal stiffness describes the longitudinal forces that develop per unit of longitudinal slip (in radians). Here, a variable that represents the longitudinal stiffness per unit gravity has been introduced in order to make the variable robust against any edits to the value of gravitational acceleration. The longitudinal tire force is approximately the product of the longitudinal stiffness per unit gravity and the longitudinal slip and the magnitude of gravitational acceleration:

longitudinalTireForce = mLongitudinalStiffnessPerUnitGravity * longitudinalSlip * gravity;

Increasing this value will result in the tire attempting to generate more longitudinal force when the tire is slipping. Typically, increasing longitudinal stiffness will help the car accelerate and brake. The total tire force available is limited by the load on the tire so be aware that increases in this value might have no effect or even come at the expense of reduced lateral force.



These values together describe the lateral stiffness per unit lateral slip (in radians) of the tire. The lateral stiffness of a tire has a role similar to the longitudinal stiffness (mLongitudinalStiffnessPerUnitGravity), except that it governs the development of lateral tire forces, and is a function of tire load. Typically, increasing lateral stiffness will help the car turn more quickly. The total tire force available is limited by the load on the tire so be aware that increases in this value might have no effect or even come at the expense of reduced longitudinal force.

Lateral stiffness is a little more complicated than longitudinal stiffness because tires typically provide poor response under heavy load. Typical for car tires is a graph of lateral force against load that has linear response close to zero load but saturates at greater loads. This means that at low tire loads the lateral stiffness has a linear response to load; that is, more load results in more stiffness and more lateral(turning) force. At higher tire loads the tire has a saturated response and is in a regime where applying more load will not result in more tire stiffness. In this latter regime it would be expected that the tire would start slipping.

The combination of two values mLatStiffX and mLatStiffY describe a graph of lateral stiffness per unit load as a function of normalised tire load. The tire force computation employs a smoothing function which requires knowledge of the normalised tire load at which the tire has a saturated response to tire load along with the lateral stiffness per unit load that occurs at this saturation point. A typical curve can be seen in the graph below.

The parameter mLatStiffX describes the normalised tire load above which the tire has a saturated response to tire load. The normalised tire load is simply the tire load divided by the load that is experienced when the vehicle is perfectly at rest. A value of 2 for mLatStiffX, for example, means that when the the tire has a load more than twice its rest load it can deliver no more lateral stiffness no matter how much extra load is applied to the tire. In the graph below mLatStiffX has value 3.

The parameter mLatStiffY describes the maximum stiffness per unit of lateral slip (in radians) per unit rest load. The maximum stiffness is delivered when the tire is in the saturated load regime, governed in turn by mLatStiffX. In the graph below mLatStiffY has value 18.

The computation of the lateral stiffness begins by computing the load on the tire and then computing the normalised load in order to compute the number of rest loads experienced by the tire. This places the tire somewhere along the X-axis of the graph below. The corresponding value on the Y-axis of the curve parameterised by mLatStiffX and mLatStiffY is queried to provide the lateral stiffness per unit rest load. The final value for the lateral stiffness is then computed by multiplying the queried graph value by the rest load. This final value describes the lateral stiffness per unit lateral slip.


A good starting value for mLatStiffX is somewhere between 2 and 3. A good starting value for mLatStiffY is around 18 or so.







These six values describe a graph of friction as a function of longitudinal slip. Vehicle tires have a complicated response to longitudinal slip and this graph attempts to approximate this relationship.

Typically, tires have a linear response at small slips. This means that when the tire is only slightly slipping it is able to generate a response force that grows as the slip increases. At greater values of slip, the force can actually start to decrease from the peak value that occurs at the optimum slip. Beyond the optimum slip the tire eventually stops behaving less and less efficiently and hits a plateau of inefficiency.

The first two values describe the friction at zero tire slip: mFrictionVsSlipGraph[0][0] = 0, and mFrictionVsSlipGraph[0][1] = friction at zero slip.

The next two values describe the optimum slip and the friction at the optimum slip: mFrictionVsSlipGraph[1][0] = optimum slip, mFrictionVsSlipGraph[1][1] = friction at optimum slip.

The last two values describe the slip at which the plateau of inefficiency begins and the value of the friction available at the plateau of inefficiency: mFrictionVsSlipGraph[2][0] = slip at the start of the plateau of inefficiency, mFrictionVsSlipGraph[2][1] = the friction available at the plateau of inefficiency.

In the graph below the following values have been used:

mFrictionVsSlipGraph[0][0] = 0.0

mFrictionVsSlipGraph[0][1] = 0.4

mFrictionVsSlipGraph[1][0] = 0.5

mFrictionVsSlipGraph[1][1] = 1.0

mFrictionVsSlipGraph[2][0] = 0.75

mFrictionVsSlipGraph[2][1] = 0.60


The friction values described here are used to scale the friction of the ground surface. This means they should be in range (0,1) but this is not a strict requirement. Typically, the friction from the graph would be close to 1.0 in order to provide a small correction to the ground surface friction.

A good starting point for this is a flat graph of friction vs slip with these values:








The camber stiffness is analogous to the longitudinal and lateral stiffness, except that it describes the camber thrust force arising per unit camber angle (in radians). Similar to the longitudinal stiffness, a camber stiffness per unit gravity has been introduced to make the camber stiffness robust across different values of gravitational acceleration. The independent camber force is computed as the camber angle multiplied by the camber stiffness multiplied by the gravitational acceleration:

camberTireForce = mCamberStiffnessPerUnitGravity * camberAngle * gravity;


This parameter has been explained in Section Tire Friction on Drivable Surfaces.



This the moment of inertia of the engine around the axis of rotation. Larger values make it harder to accelerate the engine, while lower values make it easier to accelerate the engine. A starting value of 1.0 is a good choice.


This is the maximum torque that is ever available from the engine. This is expressed in Newton metres. A starting value might be around 600.


This is the maximum rotational speed of the engine expressed in radians per second.




These three values are used to compute the damping rate that is applied to the engine. If the clutch is engaged then the damping rate is an interpolation between mDampingRateFullThrottle and mDampingRateZeroThrottleClutchEngaged, where the interpolation is governed by the acceleration control value generated by the gamepad or keyboard. At full throttle mDampingRateFullThrottle is applied, while mDampingRateZeroThrottleClutchEngaged is applied at zero throttle. In neutral gear the damping rate is an interpolation between mDampingRateFullThrottle and mDampingRateZeroThrottleClutchDisengaged.

The three values allow a range of effects to be generated: good accceleration that isn't hampered by strong damping forces, tunable damping forces when temporarily in neutral gear during a gear change, and strong damping forces that will bring the vehicle quickly to rest when it is no longer being driven by the player.

Typical values in range (0.25,3). The simulation can become unstable with damping rates of 0.


This is a graph of peak torque versus engine rotational speed. Cars typically have a range of engine speeds that produce good drive torques, and other ranges of engine speed that produce poor torques. A skilled driver will make good use of the gears to ensure that the car remains in the "good" range where the engine is most responsive. Tuning this graph can have profound effects on gameplay.

The x-axis of the curve is the normalised engine speed; that is, the engine speed divided by the maximum engine speed. The y-axis of the curve is a multiplier in range (0,1) that is used to scale the peak torque.



This is the number of the gears of the vehicle, including reverse and neutral. A standard car with 5 forward gears would, therefore, have a value of 7 after accounting for reverse and neutral.


Each gear requires a gearing ratio. Higher gear ratios result in more torque but lower top speed in that gear. Typically, the higher the gear, the lower the gear ratio. Neutral gear must always be given a value of 0, while reverse gear must have a negative gear ratio. Typical values might be 4 for first gear and 1.1 for fifth gear.


The gear ratio used in the simulator is the gear ratio of the current gear multiplied by the final ratio. The final ratio is a quick and rough way of changing the gearing of a car without having to edit each individual entry. Further, quoted gearing values from manufacturers typically mention ratios for each gear along with a final ratio. A typical value might be around 4.


The switch time describes how long it takes (in seconds) for a gear change to be completed. It is impossible to change gear immediately in a real car. Manual gears, for example, require neutral to be engaged for a short time before engaging the desired target gear. While the gear change is being completed the car will be in neutral. A good trick might be to penalise players that use an automatic gear box by increasing the gear switch time.

If the autobox is enabled it is a good idea to set this value significantly lower than PxVehicleAutoBoxData::setLatency. If the autobox latency is smaller than the gear switch time then the autobox might decide to initiate a downwards gear change immediately after an upwards gear shift has been completed. This situation can leave the car cycling between neutral and first gear with very short interludes in 2nd gear.


The autobox initiates gear changes up or down based on the rotation speed of the engine. If the engine is rotating faster than a threshold value stored in PxVehicleAutoBoxData then a gear increment will be initiated. On the other hand, if the engine is rotating slower than a threshold value then the autobox will initiate a gear decrement. The autobox only initiates gear changes upwards or downwards a single gear at a time.

It is worth noting that if the autobox initiates a gear change then the accelerator pedal is automatically disconnected from the engine for the entire duration of the gear change. Manual gear changes (PxVehicleDriveDynData::startGearChange / PxVehicleDriveDynData::mGearUpPressed / PxVehicleDriveDynData::mGearDownPressed) are not subject to this limitation. This is in keeping with typical real-world autobox behavior. The idea behind this is to stop the engine wildly accelerating during the neutral phase of the gear change, thereby avoiding damaging clutch slip when the clutch re-engages at the end of the gear change.

The autobox will not try to intiate a gear change while an automatic or manual gear change is still active.

If the autobox is too simplistic for the application's requirements then PxVehicleAutoBoxData can be readily disabled. The choices following this are either to revert to a manual gear model or to imlement a custom autobox in the application. A transition to a specific gear can be initiated with PxVehicleDriveDynData::startGearChange, while single gear changes can be initiated with PxVehicleDriveDynData::mGearUpPressed / PxVehicleDriveDynData::mGearDownPressed.

The autobox can be enabled or disabled by toggling PxVehicleDriveDynData::mUseAutoGears.

PxReal mUpRatios[PxVehicleGearsData::eGEARSRATIO_COUNT]:

The autobox will initiate a gear increment if the ratio of the engine rotation speed to the maximum allowed engine rotation speed:

PxVehicleEngineData::mMaxOmega / PxVehicleDriveDynData::getEngineRotationSpeed()

is greater than the value stored in mUpRatios[PxVehicleDriveDynData::getCurrentGear()]

PxReal mDownRatios[PxVehicleGearsData::eGEARSRATIO_COUNT]:

The autobox will initiate a gear decrement if the ratio of the engine rotation speed to the maximum allowed engine rotation speed:

PxVehicleEngineData::mMaxOmega / PxVehicleDriveDynData::getEngineRotationSpeed()

is less than the value stored in mUpRatios[PxVehicleDriveDynData::getCurrentGear()]

void setLatency(const PxReal latency):

After the autobox has initiated a gear change it will not attempt to initiate another gear change until the latency time has passed. It is a good idea to set this value significantly higher than PxVehicleGearsData::mSwitchTime. If the latency is smaller than the gear switch time then the autobox might decide to initiate a downwards gear change immediately after an upwards gear shift has been completed. This situation can leave the car cycling between neutral and first gear with very short interludes in 2nd gear.



This describes how strongly the clutch couples the engine to the wheels and how quickly differences in speed are eliminated by distributing torque to the engine and wheels.

Weaker values will result in more clutch slip, especially after changing gear or stamping on the accelerator. Stronger values will result in reduced clutch slip, and more engine torque delivered to the wheels.

This value is to be edited only for very fine tweaking of the vehicle. Some clutch slip can be attributed to the numerical issues in the simulation at large timesteps, while some is a natural consequence of driving the car in an overly aggressive manner. A value of 10 is a good starting point.



Ackermann correction allows better cornering by steering the left and right wheels with slightly different steer angles, as computed from simple trigonometry. In practice, it is impossible to engineer a steering linkage that will achieve the perfect Ackermann steering correction. This value allows the accuracy of the Ackermann steering correction to be controlled. Choosing a value of 0 completely disables Ackermann steer correction. A value of 1.0, on the other hand, achieves the impossible dream of perfect Ackermann correction.


This is the distance in metres between the two front wheels.


This is the distance in metres between the two rear wheels.


This is the distance in metres between the centre of the front axle and the centre of the rear axle.


This is for very fine control of the handling, and corrects numerical issues inherent in simulations at large timesteps.

At large simulation timesteps the amplitude of motion of the suspension springs is larger than it would be in real-life. This is unfortunately unavoidable. On a bumpy surface this could mean that the simulation lifts the car further from the ground than would really happen. This could be quickly followed by the spring being more compressed than would be experienced with a real vehicle. A consequence of this oscillation is that the load on the tire is more variable than expected, and the available tire forces have more variability than expected. This filter aims to correct this numerical problem by smoothing the tire load with the aim of making the handling smoother and more predictable.

A key concept is that of normalised tire loads. A normalised tire load is just the actual load divided by the load experienced when the vehicle is in its rest configuration. If a tire experiences more load than it does at rest then it has a normalised tire load greater than 1.0. Similarly, if a tire has less load than it does at rest then it has a normalised tire load less than 1.0. At rest, all tires obviously have a normalised tire load of exactly 1.0. The normalised tire load can never be less than zero.

The values here describe points on a 2d graph that generates filtered tire loads from raw tire loads. The x-axis of the graph is "normalised tire load", while the y-axis of the graph is "filtered normalised tire load". Normalised loads less than mMinNormalisedLoad produce a filtered normalised load of mMinFilteredNormalisedLoad. Normalised loads greater than mMaxNormalisedLoad produce a filtered normalised load of mMaxFilteredNormalisedLoad. Loads in-between mMinNormalisedLoad and mMaxNormalisedLoad produce a filtered normalised load in-between mMinFilteredNormalisedLoad and mMaxFilteredNormalisedLoad, as computed by direct interpolation.


Choosing mMaxNormalisedLoad and mMaxFilteredNormalisedLoad limits the maximum load that will ever be used in the simulation. On the other hand, choosing mMinFilteredNormalisedLoad>0 and/or mMinNormalisedLoad>0 allows the tire to potentially generate a non-zero tire force even when the tire is just touching the ground at maximum droop. To disable the correcting effect of this graph choose mMinNormalisedLoad=0, mMaxFilteredNormalisedLoad=0, mMaxNormalisedLoad=1000, and mMaxFilteredNormalisedLoad=1000.



A number of differential types are supported: 4-wheel drive with open differential, 4-wheel drive with limited slip, front-wheel drive with open differential, front-wheel drive with limited slip, rear-wheel drive with open differential, rear-wheel drive with limited slip.


If a 4-wheel drive differential is chosen (open or limited slip) this option allows the drive torque to be split unevenly between the front and rear wheels. Choosing a value of 0.5 delivers an equal split of the torque between the front and rear wheels; that is, the total torque delivered to the front wheels is equal to the total torque delivered to the rear wheels. Choosing a value greater than 0.5 delivers more torque to the front wheels, while choosing a value less than 0.5 delivers more torque to the rear wheels. This value is ignored for front-wheel drive and rear-wheel drive differentials.


This is similar to the Front Rear Split but instead splits the torque that is available for the front wheels between the front-left and front-right wheels. A value greater than 0.5 delivers more torque to the front-left wheel, while a value less than 0.5 delivers more torque to the front-right wheel. This parameter can be used to prevent any torque being delivered to a damaged or disabled wheel. This value is ignored for rear-wheel drive.


This is similar to mFrontLeftRightSplit except that it applies to the rear wheels instead of the front wheels. This value is ignored for front-wheel drive.


Limited slip differentials work by only allowing a certain difference in wheel rotation speed to accumulate. This prevents the situation where one wheel is slipping but ends up taking all the available power. Further, by allowing a small difference in wheel rotation speed to accumulate it is possible for the vehicle to easily corner by permitting the outside wheel to rotate quicker than the inside wheel.

This parameter describes the maximum difference in wheel rotation speed that is allowed to accumulate. The front bias is the maximum of the two front-wheel rotation speeds divided by the minimum of the two front-wheel rotation speeds. When this ratio exceeds the value of the front bias the differential diverts torque from the faster wheel to the slower wheel in an attempt to preserve the maximum allowed wheel rotation speed ratio.

This value is ignored except for front-wheel drive or four wheel drive with limited slip.

A good starting value is around 1.3.


This is similar to mFrontBias except that it refers to the rear wheels.

This value is ignored except for rear-wheel drive or four wheel drive with limited slip.

A good starting value is around 1.3.


This value is similar to the mFrontBias and mRearBias, except that it refers to the sum of the front wheel rotation speeds and the sum of the rear wheel rotation speeds.

This value is ignored except for four wheel drive with limited slip.

A good starting value is around 1.3.


Moment of Inertia:

The moment of inertia of the rigid body is an extremely important parameter when editing vehicles because it affects the turning and rolling of the vehicle.

A good starting point for the moment of inertia of the rigid body is to work out the moment of inertia of the cuboid that bounds the chassis geometry. If the bounding cuboid is W wide, H high, and L long then the moment of inertia for a vehicle of mass M is:

((L*L+H*H)*M/12, (W*W+L*L)*M/12, (H*H+W*W)*M/12)

However, this is only a rough guide. Tweaking each value will modify the motion around the corresponding axis, with higher values making it harder to induce rotational speed from tire and suspension forces.

Providing unphysical values for the moment of inertia will result in either very sluggish behaviour or extremely twitchy and perhaps even unstable behaviour. The moment of inertia must at least approximately reflect the length scales of the suspension and tire force application points.

This parameter should be viewed as one of the first go-to editable values.

Center of mass:

Along with the moment of inertia, the center of mass is one of the first go-to editable values and, as such, has a profound effect on handling.

To discuss the center of mass it is useful to consider a typical 4-wheeled vehicle with a chassis mesh whose origin is at the centre of the four wheels. There is no requirement on the origin being at the center of the four wheels but it does make the following discussion a little simpler. It might be expected that the center of mass lies somewhere near this origin because vehicles are designed in a way that spreads the load almost evenly between the four wheels. More specifically, it might be expected that the center of mass needs to be a little above the base of the chassis rather than at the height of the wheels. After all, vehicles have higher mass density near the bottom of the chassis due to density of the engine and other mechanical systems. As a consequence, it is expected that the center of mass is nearer the bottom of the chassis than the top, but definitely above the bottom. Without a particularly detailed analysis of the chassis density distribution the exact location along the vertical axis is really a little arbitrary and subjective. Along the forward direction it might be expected that the center of mass is a little nearer the front wheels than the rear wheels because of the mass of the front-located engine. Thinking about these factors allows the center of mass to be tweaked along the vertical and forward directions.

Tweaking the center of mass is really all about making incremental changes that tune the handling towards a desired goal. Moving the center of mass forwards should help cornering because more load is distributed to the front tires. However, this comes at the expense of reduced load on the rear tires, meaning that the car might turn more quickly only to spin out because the rear tires lose grip more quickly. Small changes followed by tests on the handling are required.

When setting the center of mass it is important to bear in mind that the suspension sprung mass values might require simultaneous updating. If the center of mass moves nearer the front this means that more mass is supported by the front suspensions and less by the rear suspensions. This change needs to be reflected in a consistent way. It is possible to mathematically describe the relationship between center of mass and the mass split between the suspensions. However, the editing possibilities afforded by breaking this rigid link should allow more tweaking options.


A typical car might have a mass of around 1500kg.

SI Units

The discussion so far has assumed that distance is measured in metres, that mass is measured in kilograms, and that time is measured in seconds. Further, the default values of all relevant vehicle components have been set under the assumption that SI Units will be adopted. An example of such a a default parameter is the maximum braking torque value. Inspection of the constructor for PxVehicleWheelData reveals a value of 1500 for mMaxBrakeTorque. This number actually represents a value of 1500 "Kilogram Metres-Squared Per Second-Squared" (an alternative way of expressing this is 1500 "Newton Metres"). An important question is how to set up a vehicle with meaningful values if SI units are not adopted. The purpose of this Section is to illustrate the required steps required. In particular, the case where distance is measured in centimetres rather than metres will be used as an example. This particular deviation from the adoption of SI Units is probably the most common one in game development, arising from the units of distance in the chosen 3D modelling package.

Vehicle parameters whose value is dependent on the length scale fall into two categories: those that can theoretically be measured with a ruler and those with more complex units involving combinations of other properties such as mass or time or even powers of distance. The former category includes data fields such as wheel radius or maximum suspension droop, while the latter category includes data fields such as maximum braking torque or wheel moment of inertia.

The following is an exhaustive list of vehicle parameters that can theoretically be measured solely from vehicle geometry:












It is useful to note that all the above parameters have default value of zero; that is, independent of length scale they must always be set with measured values in the corresponding length scale if a legal vehicle is to be successfully instantiated.

Setting parameters that involve more complex combinations of length scale require slightly more thought than those featured in the list above. A simple rule of thumb is that any parameter that has units linear with distance must be scaled by the number of length units that is equivalent to 1 metre, while any parameter that has units involving the square of distance must be scaled by the square of the number of length units that is equivalent to 1 metre. A wheel braking torque of 1500 kilograms metres-squared per second-squared, for example, is equivalent to 1500*100*100 kilograms centimetres-squared per second-squared. Consequently, when centimetres is used as the length scale a good initial guess for wheel braking torque is 15000000 [kilograms centimeres-squared per second-squared]. If inches are used as the length scale then a good initial guess for the wheel braking torque would be 1500*45.45*45.45 (= 3098553.75) [kilograms inches-squared per second-squared].

Each non-dimensionless parameter has been described with the corresponding SI Units in PxVehicleComponents.h. The following is an exhaustive list of vehicle parameters that are indirect expressions of distance scale:

PxVehicleEngineData::mMOI       (kg m^2)

PxVehicleEngineData::mPeakTorque        (kg m^2 s^-2)

PxVehicleEngineData::mDampingRateFullThrottle   (kg m^2 s^-1)

PxVehicleEngineData::mDampingRateZeroThrottleClutchEngaged      (kg m^2 s^-1)

PxVehicleEngineData::mDampingRateZeroThrottleClutchDisengaged   (kg m^2 s^-1)

PxVehicleClutchData::mStrength  (kg m^2 s^-1)

PxVehicleWheelData::mDampingRate        (kg m^2 s^-1)

PxVehicleWheelData::mMaxBrakeTorque     (kg m^2 s^-2)

PxVehicleWheelData::mMaxHandBrakeTorque (kg m^2 s^-2)

PxVehicleWheelData::mMOI        (kg m^2)

PxVehicleChassisData::mMOI      (kg m^2)

All but the last three of the above parameters have non-zero initial values in their associated constructors. This means that a good guess for their initial value can be found by multiplying the value expressed in SI Units with either the number of length units equivalent to 1 metre or the square of the number of length units that are equivalent to 1 metre.

It is important to note that the wheel handbrake torque has a default value of zero because not all wheels respond to the handbrake torque. A good guess for the handbrake torque is simply the value of the wheel braking torque, perhaps multiplied by between 1.0 and 2.0 to ensure that the handbrake is stronger than the brake.

The wheel moment of inertia and chassis moment of inertia are typically computed from the wheel radius and chassis dimensions so naturally reflect the length scale that is used in the simulation. If values are taken from manufacturer data it is important to ensure that the units of the manufacturer data are commensurate with the remainder of the vehicle data fields or to perform the appropriate unit conversion.

A number of functions also have parameters that are functions of length scale. The following is an exhaustive list of such functions:



Some care is required to set the threshold speed in PxVehicleWheels::setSubStepCount. Here, it is the case that the default threshold speed is 5.0 metres per second. With centimetres the chosen length scale a value of 500 [centimetres per second] should be passed to achieve the equivalent behaviour, or with inches as the chosen length scale a value of 5*45.45 (= 227.25) [inches per second] is required. The same process must also be applied to PxVehicleWheelsSimData::setMinLongSlipDenominator. Here, the default is 4.0 metres per second. If centimetres is the adopted scale then the equivalent value is 400 [centimetres per second], while 4*45.45 (=181.8) [inches per second] is required if inches is the chosen scale.

The PhysX Vehicle SDK supports any system of units with the caveat that all data supplied must conform to the same unit system. Further, the default data values, although strictly expressed in the SI unit system, can be used as a guide to estimate sensible values in any unit system for almost any conceivable vehicle. A quick way to do this would be to decide if, say, a truck would have a stronger handbrake than the handbrake of a family car. Now, the default data approximates that of a standard family car so it might be a good estimate to start with the truck having a handbrake that is perhaps 25% stronger; that is, 5000 kilograms metres-squared per second-squared. If centimetres are the chosen length scale then a quick conversion can be performed by noting that 1 metre is equal to 100 centimetres, leading to the brake torque being set as 5000*100*100 kilograms centimetres-squared per second-squared. If the natural unit of mass is the gram then noting that 1 kilogram is 1000 grams leads to an equivalent value of 5000*1000 grams metres-squared per second-squared. This rule can be repeated for all vehicle data fields by simply noting the default value and the SI units in the relevant class constructor and then performing the conversion to the chosen unit system.

The PhysX Vehicle SDK depends on a number of threshold values that are functions of length scale. These are set with the function PxInitVehicleSDK and uses the PxTolerancesScale values that have already been already configured for the PhysX SDK. If PxInitVehicleSDK is not called prior to the first call to PxVehicleUpdates a warning will be passed to the PhysX error stream.

Level of Detail

It seems sensible to attempt to save valuable clock cycles for vehicles that are either not visible on the screen or are sufficiently far from the camera that it is hard to tell if their motion is exactly in step with the world geometry. The PhysX vehicles SDK presents a number of options for reducing the computational load for vehicles that require only low levels of detail.


The most obvious strategy for a vehicle that requires only a low level of detail is simply to stop performing raycasts (PxVehicleSuspensionRaycasts) and updates (PxVehicleUpdates) for that vehicle. Instead of computing the ground underneath the vehicle's tires and computing the suspension and tire forces each and every frame it might be acceptable to avoid these steps completely and let the PhysX SDK update the rigid body with the legacy momentum of the rigid body. After several frames the vehicle's wheels will likely either be hovering above the ground or intersecting the ground so there needs to be a strategy to decide how many PhysX SDK updates can pass before the vehicle is once more updated properly by including it in the vehicles array passed to PxVehicleSuspensionRaycasts/PxVehicleUpdates. The details of any such strategy are left to users of the vehicles SDK because it depends on a number of factors such as distance from the camera; the spatial coherence of the world geometry near the vehicle; the speed of the vehicle; and whether the audio or graphics fx for the vehicle play an important role.

Disable Wheels

If there exist vehicles with large wheel counts it might also be possible to reduce the number of wheels that participate in the simulation by calling PxVehicleWheelsSimData::disableWheel. An example might be a truck with 18 wheels. Now, such a truck will clearly need to perform 18 raycasts, 18 tire force calculations and 18 updates of wheel rotation speed in order to complete the vehicle update. If the truck can be reduced to just 4 enabled wheels then it is clear that less computational work is required. It is important to note that when wheels are disabled they no longer participate in supporting the mass of the vehicle's rigid body. In the extreme case of an 18-wheeled truck reduced to just 4 active wheels this will mean that the remaining enabled suspension springs are only configured to support approximately 4/18 of the mass of the vehicle's rigid body. To remedy this problem the mass of the rigid body will need to be re-distributed among the enabled wheels and suspensions, perhaps using PxVehicleComputeSprungMasses. A more complete description of the issues surrounding disabled wheels can be found in Section 3-Wheeled, N-Wheeled Cars, and N-Wheeled Tanks .

Swapping Multiple Vehicle Versions

Instead of diasabling wheels, perhaps a simpler and more effective way of reducing the computational cost is to instantiate two versions of the vehicle with different wheel counts. The two vehicles can be easily swapped in the vehicles array passed to PxVehicleSuspensionRaycasts/PxVehicleUpdates as the required level of detail increases and decreases. It is worth considering how this might work in the case of the 18-wheeled truck mentioned earlier. The simplest strategy would be to first construct the required rigid body and attach a PxShape instance for each of the 18 wheels of the 18-wheeled truck. Instantiating the required 18-wheeled version of the truck with PxVehicleNW::create or PxVehicleNW::setup will automatically pose the shapes of all 18 wheels in the rest pose. The next step is to choose 4 of the 18 wheels to form the 4-wheeled version of the truck. Many choices are available but the most obvious choice would be the front-left/front-right/rear-left/rear-right wheels of the 18-wheeled truck. The 4-wheeled version can then be instantiated using the same rigid body as for the 18-wheeled version. This will pose 4 of the PxShape instances to the rest pose of the 4-wheeled truck. If the wheels of the 4-wheeled version have been set up correctly the rest poses ought to be identical to their counterparts in the 18-wheeled version. A key point to note is that both versions of the vehicle apply forces to the same rigid body. Another key point to note is that when the 4-wheeled vehicle is chosen only 4 of the 18 PxShape instances will have their pose updated, leaving 14 PxShape instances at either the rest local pose or the local pose given to them when the 18-wheeled version was last used. In terms of visible accuracy, these unposed shapes are the main disadvantage of the lower lod vehicle. The differences in handling are much harder to gauge.

A number of useful functions are available to make it easy to swap between two or more versions of the same vehicle:

void PxVehicleComputeSprungMasses(const PxU32 nbSprungMasses, const PxVec3* sprungMassCoordinates, const PxVec3& centreOfMass, const PxReal totalMass, const PxU32 gravityDirection, PxReal* sprungMasses);
void PxVehicleWheelsSimData::copy(const PxVehicleWheelsSimData& src, const PxU32 srcWheel, const PxU32 trgWheel);
void PxVehicleSuspensionData::setMassAndPreserveNaturalFrequency(const PxReal newSprungMass);
void PxVehicleCopyDynamicsData(const PxVehicleCopyDynamicsMap& wheelMap, const PxVehicleWheels& src, PxVehicleWheels* trg);

The following pseudo-code hopefully makes clear how to apply these functions in order to first construct the lower lod vehicle and then swap between the different versions:

PxVehicleDriveNW* instantiate4WVersion(const PxVehicleDriveNW& vehicle18W, PxPhysics& physics)
        //Compute the sprung masses of the 4-wheeled version.
        PxReal sprungMasses[4];
                const PxReal rigidBodyMass = vehicle18W.getRigidDynamicActor()->getMass();
                const PxVec3 wheelCoords[4] =
                const PxU32 upDirection = 1;
                PxVehicleComputeSprungMasses(4, wheelCoords, PxVec3(0,0,0), rigidBodyMass, upDirection, sprungMasses);

        //Set up the wheels simulation data.
        PxVehicleWheelsSimData* wheelsSimData4W = PxVehicleWheelsSimData::allocate(4);
        for(PxU32 i = 0; i < 4; i++)
                wheelsSimData4W->copy(vehicle18W.mWheelsSimData, i, i);

                PxVehicleSuspensionData suspData = wheelsSimData4W->getSuspensionData(i);
                wheelsSimData4W->setSuspensionData(i, suspData);

        //Make sure the correct shapes are posed.

        //Set up the drive simulation data.
        PxVehicleDriveSimDataNW driveSimData4W = vehicle18W.mDriveSimData;
        PxVehicleDifferentialNWData diff4W;
        diff4W.setDrivenWheel(0, true);
        diff4W.setDrivenWheel(1, true);
        diff4W.setDrivenWheel(2, true);
        diff4W.setDrivenWheel(3, true);

        //Instantiate the 4-wheeled version.
        PxRigidDynamic* rigidDynamic = const_cast<PxRigidDynamic*>(vehicle18W.getRigidDynamicActor());
        PxVehicleDriveNW* vehicle4W = PxVehicleDriveNW::create(&physics, rigidDynamic, *wheelsSimData4W, driveSimData4W, 4);

        //Delete the wheels simulation data now that we have copied the data to the instantiated vehicle.

        return vehicle4W;

void swapToLowLodVersion(const PxVehicleDriveNW& vehicle18W, PxVehicleDrive4W* vehicle4W, PxVehicleWheels** vehicles, PxU32 vehicleId)
        vehicles[vehicleId] = vehicle4W;

        PxVehicleCopyDynamicsMap wheelMap;

        PxVehicleCopyDynamicsData(wheelMap, vehicle18W, vehicle4W);

void swapToHighLowVersion(const PxVehicleDriveNW& vehicle4W, PxVehicleDrive4W* vehicle18W, PxVehicleWheels** vehicles, PxU32 vehicleId)
        vehicles[vehicleId] = vehicle18W;

        PxVehicleCopyDynamicsMap wheelMap;

        PxVehicleCopyDynamicsData(wheelMap, vehicle4W, vehicle18W);

Disable Raycasts

In some scenes it might be possible not to issue raycasts for each vehicle prior to each update. Depending on the geometry, this can lead to significant gains.

The PhysX vehicles SDK provides a simple mechanism to disable or enable raycasts per update and per vehicle by specifying an array of booleans as a function argument in PxVehicleSuspensionRaycasts. An alternative to disabling raycasts using the boolean array would be to alter the array of vehicles passed to PxVehicleSuspensionRaycasts so that some vehicles scheduled for update in PxVehicleUpdates do not participate in the batched raycast prior to the update. It is anticipated that using the boolean array will allow the same vehicle array to be passed to both the raycast and update functions, thereby allowing simpler vehicle management.

Vehicles that participate in the batched raycast automatically store raycast hit planes which are re-used each subsequent update until they are relaced by the hit planes of the next raycast. This means that it is not necessary to perform raycasts each update, especially if the vehicle is moving slowly or the vehicle is far from the camera or the vehicle remains on the same plane for several updates in a row. As the frequency of updates preceded by a raycast decreases, the accuracy of the cached hit planes also decreases, meaning that the likelihood of visibly poor wheel placement increases. The lack of accuracy in the cached hit planes means that some wheels might end up hovering or intersecting the ground if raycasts are not performed prior to each update. It is left to users of the sdk to develop their own strategy to decide whether a vehicle requires a fresh raycast or not.

If a raycast is not performed prior to an update then the vehicle will only be able to report a partial description of its interaction with the scene. For example, as a consequence of deletion the actor or shape or material hit by the last suspension raycast may no longer exist in the scene several updates later. For this reason, the vehicle reports NULL pointers for the shapes/actors/materials if a cached plane is used instead of the hit planes of a fresh raycast. The documentation for PxWheelQueryResult describes this in detail.

The first update of any vehicle requires that a raycast is performed prior to the update. If a raycast is not performed prior to the first update then the vehicle will not have been given an opportunity to cache its raycast hit planes. Further, after each call to setToRestState the vehicle also needs to perform a raycast prior to the next update. The reason for this is that setToRestState clears the cached hit planes, meaning that they need to be re-computed once more.

Use The Clutch in Estimate Mode

The vehicle SDK implements a mathematical model for the clutch that has two optional modes of operational accuracy: eESTIMATE and eBEST_POSSIBLE. If eBEST_POSSIBLE is chosen the sdk attempts to accurately update wheel and engine rotation speeds from their coupling through the clutch. It is worth mentioning that the clutch model in PxVehicleDriveTank reduces to a particularly simple set of equations that have fast analytical solution. As a consequence, the vehicle sdk ignores the clutch accuracy model for tanks and instead always opts to compute the best possible solution. In the case of PxVehicle4W only marginal peformance gains can be produced by switching to eESTIMATE because at most only 4 wheels can ever be coupled to the clutch. The real performance gains from the estimated solution are to be had with PxVehicleNW instances with high wheel count.

If eESTIMATE is chosen the quality of the estimate can be tuned with PxVehicleClutchData::mEstimateIterations. As the value of this variable increases the computational cost also increases and the estimated solution approaches the best possible solution. At particularly large values of mEstimateIterations the cost of the estimated solution might even exceed that of the best possible solution but without adding any precision. On the other hand, particularly low values such as 1 might result in weak or inaccurate coupling between the engine and wheels. This can be particularly noticeable after a gear change or at standing starts or when the brakes are aggressively applied. In such situations large angular velocity differences at the clutch result in large torques that require computational effort to resolve. A poor estimate might, for example, result in oscillating engine rotation speeds after a gear change instead of the expected smooth transitions. The magnitude of accuracy loss and its subsequent effect on vehicle behaviour are very difficult to quantify and really need tested for each vehicle and scene.

It is recommended that eBEST_POSSIBLE is chosen for vehicles that require a high level of detail and that eESTIMATE is only chosen for vehicles that require lower levels of detail. Care must be taken when tuning PxVehicleClutchData::mEstimateIterations to ensure that the loss of accuracy is acceptable for the required level of detail. In many cases the lowest possible value of 1 will turn out to provide perfectly acceptable. Smooth and physically believable behavior, however, is only guaranteed if eBEST_POSSIBLE is adopted.


This Section introduces common solutions to common problems with vehicle tuning.

Jittery Vehicles

  1. Have PxInitVehicleSDK and PxVehicleSetBasisVectors been called before the first execution of PxVehicleUpdates? Check the error stream for warnings.
  2. Does the length scale of PxTolerancesScale match the length scale of the vehicle eg 100 if centimetres are used? Update PxTolerancesScale::length as appropriate.
  3. Is the natural frequency of the spring too high/timestep of simulation too small for reliable simulation? See Section Vehicle Suspension for more details and update the natural frequency or timestep accordingly. Remember that the timestep can be updated per vehicle with PxVehicleWheelsSimData::setSubStepCount.
  4. Are the maximum suspension droop and compression set to values that allow some suspension motion?

The Engine Rotation Refuses To Spin Quickly

  1. Are the tires resisting the engine motion through excessive friction forces? Place the car very high above the ground and accelerate the engine to see if the engine and wheels start to spin round.
  2. Do the engine's moment of inertia, peak torque and damping rates reflect the length scale? Note the documented SI units of each variable and recompute the values as appropriate.
  3. Is the moment of inertia too large? A value of 1 or its equivalent in the relevant length scale is a good estimate for testing purposes.
  4. Is the peak torque too small to drive the engine? Scale the default peak torque value with the mass of the vehicle with the knowledge that the default value will drive a standard car of around 1500kg.
  5. Does the torque curve contain sensible values? Try a flat curve with each data point having a y-value of 1.0.
  6. Is the maximum engine angular speed a realistic value? Consult any available manufacturer data for typical values or revert to the default value for testing purposes.
  7. Are any of the damping rates too high? Reduce the damping rates and test.

The Engine Spins But the Wheels Refuse To Spin

  1. Is the vehicle in neutral gear? Connect the engine to the wheels by setting the vehicle to first gear and disabling the autobox.
  2. Does the differential deliver drive torque to the wheels (for PxVehicleNW vehicles only)? Make sure that the differential is properly configured.
  3. Is the brake or handbrake engaged? Ensure that the brake and handbrake are both zero.
  4. Do the wheels' moment of inertia and damping rates reflect the length scale? Note the documented SI units of each variable and recompute the values as appropriate.
  5. Are the wheels' moments of inertia too high? Recompute the wheels' moments of inertia.
  6. Are the wheels' damping rates too high? Reduce the wheels' damping rates.
  7. Are the tires resisting the engine motion through excessive friction forces? Place the car very high above the ground and accelerate the engine to see if the engine and wheels start to spin round.

The Wheels Are Spinning But The Vehicle Does Not Move Forwards

  1. Is the filtering configured so that the vehicle is supported only by suspension forces? Check the filtering configuration for shapes attached to the vehicle's rigid body actor, search for contacts involving shapes attached to the vehicle's actor using PVD.
  2. Is sufficient friction being delivered to the tire contact patch? Query the friction values experienced by the tires during the execution of PxVehicleUpdates using PxVehicleWheelsDynData::getTireFriction.
  3. Do the suspension forces (and the loads on the tires) reflect the mass of the rigid body actor? Query the suspension forces using PxVehicleWheelsDynData::getSuspensionForce. A 4-wheeled vehicle should generate suspension forces of approximately actorMass*gravity/4. Adjust the sprung masses of the vehicle suspensions to ensure that the driven wheels experience significant tire load.
  4. Do the tires generate significant longitudinal tire forces? Use PxVehicleWheelsDynData::getTireLongSlip to check that the longitudinal slip of the tire is non-zero and approaches 1.0 when the wheels are spinning quickly without forward motion. Ensure that PxVehicleSetBasisVectors has been called with the correct forward vector if the longitudinal slip is vanishingly small. Further test that the forward vector has been set correctly by using PxVehicleWheelsDynData::getTireLongitudinalDir.
  5. Is the tire longitudinal stiffness too small? Adjust the longitudinal stiffness back to the default value and test.
  6. Is the mass of the vehicle's rigid body actor too large to be driven by the engine peak torque? Test that the mass of the actor is a sensible value and set accordingly.
  7. Is the rigid body actor in a PhysX scene and is the scene being updated? Ensure that the actor is not asleep and participates in the scene update.

The Vehicle Does Not Steer/Turn

  1. Is the moment of inertia of the vehicle too large so that it resists turning motion? Check that the moment of inertia of the vehicle's rigid body actor is a sensible value. Use the moment of inertia of a box with width/height/length of the vehicle as a starting guess for the moment of inertia of the actor.
  2. Are the steer wheels receiving a steer angle? Check the steer angle with PxVehicleWheelsDynData::getSteer. If the steer angle is zero or smaller than expected check that a steer angle is being passed to the vehicle and that the maximum steer angles of the steer wheels are sensible values.
  3. Do the steer wheels have a sensible lateral slip angle? Use PxVehicleWheelsDynData::getLatSlip to query the slip angles. Ensure that PxVehicleSetBasisVectors has been called with the correct forward and up vectors if the lateral slips are vanishingly small. Further test that the basis vector have been set correctly by using PxVehicleWheelsDynData::getTireLateralDir.
  4. Is the lateral stiffness of the tire configured properly? Reset the tires back to their default values and retest.

The Acceleration Feels Sluggish

  1. Are the damping rates of the engine and wheels too large? Reduce the engine damping rate, then the wheel damping rates and retest each time.
  2. Is the vehicle stuck in the same gear all the time? Disable the autobox and change gears manually to test if the autobox is failing to switch gear. Check the autobox settings to make sure that it will automatically increase the gear at sensible engine rotation speeds.
  3. Is the engine powerful enough to quickly accelerate the car? Increase the peak torque of the engine.
  4. Do the wheels have high moments of inertia that prevent significant longitudinal slips developing? Reduce the moments of inertia of the wheels.

The Vehicle Does Not Slow Down When Not Accelerating

  1. Are the wheel and engine damping rates too small? First increase the engine damping rate, then the wheel damping rates and retest each time.
  2. Does the vehicle's rigid body actor have a velocity damping value? Increase as appropriate.

The Vehicle Turns Too Quickly/Too Slowly

  1. Does the moment of inertia of the rigid body actor need tweaking? Adjust the component of the moment of inertia that corresponds to motion about the up vector. Increasing the moment of inertia will slow the turn rate, decreasing the moment of inertia will increase the turn rate.

The Wheels Spin Too Much Under Acceleration

  1. Is the accelerator pedal value increasing too rapidly from 0 to 1? Slow down the rate of increase of the accelerator pedal value by filtering the controller or keyboard. Remember that aggressively pressing the accelerator pedal on a powerful car ought to lead to wheel spin.
  2. Are the wheel moments of inertia too low? Increase the wheel moments of inertia.

The Wheels Spin Too Much When Cornering

  1. Does the vehicle have a limited slip differential? If applicable set the differential type to limited slip and adjust the differential biases accordingly.

The Vehicle Never Goes Beyond First Gear

  1. Does the vehicle cycle between first gear and neutral? If the autobox is enabled then the problem is probably that the latency of the autobox is shorter than the time spent performing a gear change. The autobox latency controls the minimum time spent in-between automated gear changes. After an automated gear change is initiated the autobox will not make another gear change decision until the latency time has passed. During a gear change the vehicle enters neutral gear and the accelerator pedal is uncoupled from the engine, meaning that the engine will slow down during the gear change. When the vehicle enters the target gear at the end of the gear change the autobox might decide immediately that the engine is too slow for the target gear and immediately initiate a downwards gear change. This will immediately put the car back in neutral, meaning that the car spends a very long time in neutral and never reaches its target gear. This will not happen if the autobox latency (PxVehicleAutoBoxData::setLatency) is set significantly larger than the gear switch time (PxVehicleGearsData::mSwitchTime).

The Vehicle Under-steers Then Over-steers

  1. Is the vehicle on a bumpy surface? Edit the values in PxVehicleTireLoadFilterData so that the filtered normalised tire load has a flatter response to suspension compression.

The Vehicle Slows Down Unnaturally

  1. Does the vehicle not slow down smoothly to rest? Take a look at the longitudinal slip values to see if they are oscillating between positive and negative. If there is a strong oscillation then two options are available that can be used separately or in conjunction with each other. The first option is to use PxVehicleWheelsSimData::setSubStepCount to force more vehicle update sub-steps as the forward speed of the vehicle approaches zero. The second option is to use PxVehicleWheelsSimData::setMinLongSlipDenominator to ensure that the denominator of the longitudinal slip never falls below a specified value.