// Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. // **************************************************************************** // This snippet illustrates simple use of the physx vehicle sdk and demonstrates // how to simulate a vehicle with a fully featured drivetrain comprising engine, // clutch, differential and gears. The snippet uses only parameters, states and // components maintained by the PhysX Vehicle SDK. // Vehicles are made of parameters, states and components. // Parameters describe the configuration of a vehicle. Examples are vehicle mass, wheel radius // and suspension stiffness. // States describe the instantaneous dynamic state of a vehicle. Examples are engine revs, wheel // yaw angle and tire slip angles. // Components forward integrate the dynamic state of the vehicle, given the previous vehicle state // and the vehicle's parameterisation. // Components update dynamic state by invoking reusable functions in a particular sequence. // An example component is a rigid body component that updates the linear and angular velocity of // the vehicle's rigid body given the instantaneous forces and torques of the suspension and tire // states. // The pipeline of vehicle computation is a sequence of components that run in order. For example, // one component might compute the plane under the wheel by performing a scene query against the // world geometry. The next component in the sequence might compute the suspension compression required // to place the wheel on the surface of the hit plane. Following this, another component might compute // the suspension force that arises from that compression. The rigid body component, as discussed earlier, // can then forward integrate the rigid body's linear velocity using the suspension force. // Custom combinations of parameter, state and component allow different behaviours to be simulated with // different simulation fidelities. For example, a suspension component that implements a linear force // response with respect to its compression state could be replaced with one that imlements a non-linear // response. The replacement component would consume the same suspension compression state data and // would output the same suspension force data structure. In this example, the change has been localised // to the component that converts suspension compression to force and to the parameterisation that governs // that conversion. // Another combination example could be the replacement of the tire component from a low fidelity model to // a high fidelty model such as Pacejka. The low and high fidelity components consume the same state data // (tire slip, load, friction) and output the same state data for the tire forces. Again, the // change has been localised to the component that converts slip angle to tire force and the // parameterisation that governs the conversion. //The PhysX Vehicle SDK presents a maintained set of parameters, states and components. The maintained //set of parameters, states and components may be combined on their own or combined with custom parameters, //states and components. //This snippet breaks the vehicle into into three distinct models: //1) a base vehicle model that describes the mechanical configuration of suspensions, tires, wheels and an // associated rigid body. //2) a drivetrain model that forwards input controls to wheel torques via a drivetrain model // that includes engine, clutch, differential and gears. //3) a physx integration model that provides a representation of the vehicle in an associated physx scene. // It is a good idea to record and playback with pvd (PhysX Visual Debugger). // **************************************************************************** #include #include "PxPhysicsAPI.h" #include "../snippetvehiclecommon/enginedrivetrain/EngineDrivetrain.h" #include "../snippetvehiclecommon/serialization/BaseSerialization.h" #include "../snippetvehiclecommon/serialization/EngineDrivetrainSerialization.h" #include "../snippetvehiclecommon/SnippetVehicleHelpers.h" #include "../snippetcommon/SnippetPVD.h" using namespace physx; using namespace physx::vehicle2; using namespace snippetvehicle; //PhysX management class instances. PxDefaultAllocator gAllocator; PxDefaultErrorCallback gErrorCallback; PxFoundation* gFoundation = NULL; PxPhysics* gPhysics = NULL; PxDefaultCpuDispatcher* gDispatcher = NULL; PxScene* gScene = NULL; PxMaterial* gMaterial = NULL; PxPvd* gPvd = NULL; //The path to the vehicle json files to be loaded. const char* gVehicleDataPath = NULL; //The vehicle with engine drivetrain EngineDriveVehicle gVehicle; //Vehicle simulation needs a simulation context //to store global parameters of the simulation such as //gravitational acceleration. PxVehiclePhysXSimulationContext gVehicleSimulationContext; //Gravitational acceleration const PxVec3 gGravity(0.0f, -9.81f, 0.0f); //The mapping between PxMaterial and friction. PxVehiclePhysXMaterialFriction gPhysXMaterialFrictions[16]; PxU32 gNbPhysXMaterialFrictions = 0; PxReal gPhysXDefaultMaterialFriction = 1.0f; //Give the vehicle a name so it can be identified in PVD. const char gVehicleName[] = "engineDrive"; //Commands are issued to the vehicle in a pre-choreographed sequence. struct Command { PxF32 brake; PxF32 throttle; PxF32 steer; PxU32 gear; PxF32 duration; }; const PxU32 gTargetGearCommand = PxVehicleEngineDriveTransmissionCommandState::eAUTOMATIC_GEAR; Command gCommands[] = { {0.5f, 0.0f, 0.0f, gTargetGearCommand, 2.0f}, //brake on and come to rest for 2 seconds {0.0f, 0.65f, 0.0f, gTargetGearCommand, 5.0f}, //throttle for 5 seconds {0.5f, 0.0f, 0.0f, gTargetGearCommand, 5.0f}, //brake for 5 seconds {0.0f, 0.75f, 0.0f, gTargetGearCommand, 5.0f}, //throttle for 5 seconds {0.0f, 0.25f, 0.5f, gTargetGearCommand, 5.0f} //light throttle and steer for 5 seconds. }; const PxU32 gNbCommands = sizeof(gCommands) / sizeof(Command); PxReal gCommandTime = 0.0f; //Time spent on current command PxU32 gCommandProgress = 0; //The id of the current command. //A ground plane to drive on. PxRigidStatic* gGroundPlane = NULL; void initPhysX() { gFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gAllocator, gErrorCallback); gPvd = PxCreatePvd(*gFoundation); PxPvdTransport* transport = PxDefaultPvdSocketTransportCreate(PVD_HOST, 5425, 10); gPvd->connect(*transport,PxPvdInstrumentationFlag::eALL); gPhysics = PxCreatePhysics(PX_PHYSICS_VERSION, *gFoundation, PxTolerancesScale(), true, gPvd); PxSceneDesc sceneDesc(gPhysics->getTolerancesScale()); sceneDesc.gravity = gGravity; PxU32 numWorkers = 1; gDispatcher = PxDefaultCpuDispatcherCreate(numWorkers); sceneDesc.cpuDispatcher = gDispatcher; sceneDesc.filterShader = VehicleFilterShader; gScene = gPhysics->createScene(sceneDesc); PxPvdSceneClient* pvdClient = gScene->getScenePvdClient(); if(pvdClient) { pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS, true); pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONTACTS, true); pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES, true); } gMaterial = gPhysics->createMaterial(0.5f, 0.5f, 0.6f); PxInitVehicleExtension(*gFoundation); } void cleanupPhysX() { PxCloseVehicleExtension(); PX_RELEASE(gMaterial); PX_RELEASE(gScene); PX_RELEASE(gDispatcher); PX_RELEASE(gPhysics); if (gPvd) { PxPvdTransport* transport = gPvd->getTransport(); PX_RELEASE(gPvd); PX_RELEASE(transport); } PX_RELEASE(gFoundation); } void initGroundPlane() { gGroundPlane = PxCreatePlane(*gPhysics, PxPlane(0, 1, 0, 0), *gMaterial); for (PxU32 i = 0; i < gGroundPlane->getNbShapes(); i++) { PxShape* shape = NULL; gGroundPlane->getShapes(&shape, 1, i); shape->setFlag(PxShapeFlag::eSCENE_QUERY_SHAPE, true); shape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, false); shape->setFlag(PxShapeFlag::eTRIGGER_SHAPE, false); } gScene->addActor(*gGroundPlane); } void cleanupGroundPlane() { gGroundPlane->release(); } void initMaterialFrictionTable() { //Each physx material can be mapped to a tire friction value on a per tire basis. //If a material is encountered that is not mapped to a friction value, the friction value used is the specified default value. //In this snippet there is only a single material so there can only be a single mapping between material and friction. //In this snippet the same mapping is used by all tires. gPhysXMaterialFrictions[0].friction = 1.0f; gPhysXMaterialFrictions[0].material = gMaterial; gPhysXDefaultMaterialFriction = 1.0f; gNbPhysXMaterialFrictions = 1; } bool initVehicles() { //Load the params from json or set directly. readBaseParamsFromJsonFile(gVehicleDataPath, "Base.json", gVehicle.mBaseParams); setPhysXIntegrationParams(gVehicle.mBaseParams.axleDescription, gPhysXMaterialFrictions, gNbPhysXMaterialFrictions, gPhysXDefaultMaterialFriction, gVehicle.mPhysXParams); readEngineDrivetrainParamsFromJsonFile(gVehicleDataPath, "EngineDrive.json", gVehicle.mEngineDriveParams); //Set the states to default. if (!gVehicle.initialize(*gPhysics, PxCookingParams(PxTolerancesScale()), *gMaterial, EngineDriveVehicle::eDIFFTYPE_FOURWHEELDRIVE)) { return false; } //Apply a start pose to the physx actor and add it to the physx scene. PxTransform pose(PxVec3(0.000000000f, -0.0500000119f, -1.59399998f), PxQuat(PxIdentity)); gVehicle.setUpActor(*gScene, pose, gVehicleName); //Set the vehicle in 1st gear. gVehicle.mEngineDriveState.gearboxState.currentGear = gVehicle.mEngineDriveParams.gearBoxParams.neutralGear + 1; gVehicle.mEngineDriveState.gearboxState.targetGear = gVehicle.mEngineDriveParams.gearBoxParams.neutralGear + 1; //Set the vehicle to use the automatic gearbox. gVehicle.mTransmissionCommandState.targetGear = PxVehicleEngineDriveTransmissionCommandState::eAUTOMATIC_GEAR; //Set up the simulation context. //The snippet is set up with //a) z as the longitudinal axis //b) x as the lateral axis //c) y as the vertical axis. //d) metres as the lengthscale. gVehicleSimulationContext.setToDefault(); gVehicleSimulationContext.frame.lngAxis = PxVehicleAxes::ePosZ; gVehicleSimulationContext.frame.latAxis = PxVehicleAxes::ePosX; gVehicleSimulationContext.frame.vrtAxis = PxVehicleAxes::ePosY; gVehicleSimulationContext.scale.scale = 1.0f; gVehicleSimulationContext.gravity = gGravity; gVehicleSimulationContext.physxScene = gScene; gVehicleSimulationContext.physxActorUpdateMode = PxVehiclePhysXActorUpdateMode::eAPPLY_ACCELERATION; return true; } void cleanupVehicles() { gVehicle.destroy(); } bool initPhysics() { initPhysX(); initGroundPlane(); initMaterialFrictionTable(); if (!initVehicles()) return false; return true; } void cleanupPhysics() { cleanupVehicles(); cleanupGroundPlane(); cleanupPhysX(); printf("SnippetVehicleFourWheelDrive done.\n"); } void stepPhysics() { if (gNbCommands == gCommandProgress) return; const PxReal timestep = 1.0f/60.0f; //Apply the brake, throttle and steer to the command state of the vehicle. const Command& command = gCommands[gCommandProgress]; gVehicle.mCommandState.brakes[0] = command.brake; gVehicle.mCommandState.nbBrakes = 1; gVehicle.mCommandState.throttle = command.throttle; gVehicle.mCommandState.steer = command.steer; gVehicle.mTransmissionCommandState.targetGear = command.gear; //Forward integrate the vehicle by a single timestep. //Apply substepping at low forward speed to improve simulation fidelity. const PxVec3 linVel = gVehicle.mPhysXState.physxActor.rigidBody->getLinearVelocity(); const PxVec3 forwardDir = gVehicle.mPhysXState.physxActor.rigidBody->getGlobalPose().q.getBasisVector2(); const PxReal forwardSpeed = linVel.dot(forwardDir); const PxU8 nbSubsteps = (forwardSpeed < 5.0f ? 3 : 1); gVehicle.mComponentSequence.setSubsteps(gVehicle.mComponentSequenceSubstepGroupHandle, nbSubsteps); gVehicle.step(timestep, gVehicleSimulationContext); //Forward integrate the phsyx scene by a single timestep. gScene->simulate(timestep); gScene->fetchResults(true); //Increment the time spent on the current command. //Move to the next command in the list if enough time has lapsed. gCommandTime += timestep; if (gCommandTime > gCommands[gCommandProgress].duration) { gCommandProgress++; gCommandTime = 0.0f; } } int snippetMain(int argc, const char*const* argv) { if (!parseVehicleDataPath(argc, argv, "SnippetVehicleFourWheelDrive", gVehicleDataPath)) return 1; //Check that we can read from the json file before continuing. BaseVehicleParams baseParams; if (!readBaseParamsFromJsonFile(gVehicleDataPath, "Base.json", baseParams)) return 1; //Check that we can read from the json file before continuing. EngineDrivetrainParams engineDrivetrainParams; if (!readEngineDrivetrainParamsFromJsonFile(gVehicleDataPath, "EngineDrive.json", engineDrivetrainParams)) return 1; #ifdef RENDER_SNIPPET extern void renderLoop(const char*); renderLoop("PhysX Snippet Vehicle Four-Wheel Drive"); #else if (initPhysics()) { while (gCommandProgress != gNbCommands) { stepPhysics(); } cleanupPhysics(); } #endif return 0; }