Vehicle Dynamics

The Vehicle Dynamics extension provides tools for creating vehicle simulations that include tire, engine, clutch, transmission and suspension models.

Getting Started

To use the full feature set of the vehicle dynamics simulation, the vehicle extension must be enabled in the Window > Extensions window. Search for the omni.physx.vehicle extension and enable it.

Vehicle Dynamics Introduction

The Vehicle Extension provides some sample scenes that can be loaded quickly to see how the vehicle drives and works in Create.

Open the Physics Demo Scenes: Window > Physics > Demo Scenes

In the Physics Demo Scenes Tab, click on the Vehicle Samples list to show the list of available samples.

VehicleSamples

As an introduction, select the Vehicle sounds sample and click Load scene.

VehicleLoadScene

If you have a gamepad connected to your PC, and would like to drive the vehicle with it, click on the settings gear in the top left corner of the Viewport window. Then, uncheck the Gamepad Camera Control setting. This allows the gamepad to control the vehicle, instead of the camera. If you would prefer to control the vehicle with the keyboard arrow keys, this step can be skipped.

VehicleGamepad

Add a camera that can follow the vehicle from a chase vehicle. Click on the Stage Tab, expand the World list, and select the Vehicle itself.

VehicleStage

Now, click the Property Tab, scroll to the Vehicle Cameras section and press the Add Follow Camera button.

VehicleAddFollowCameraButton

A camera will appear in the Viewport. To view the scene through that camera, click on the camera icon in the top left corner of the Viewport window, click on the Camera button and select the VehicleFollowCamera.

VehicleSelectFollowCamera

This demo plays engine and tire sounds interactively as you control the vehicle. To hear it, make sure your PC audio is not muted and is set to a reasonable volume. Press the Play button on the left side of Create.

VehiclePlay

If you are using a gamepad, use the right trigger to accelerate, the left trigger to brake and the left analog joystick to steer the vehicle left and right. If you are using a keyboard, use the up arrow key to accelerate, the down arrow key to brake and the left and right arrow keys to steer the vehicle. Press the Stop button to end the simulation.

Creating a Vehicle Using the Vehicle Wizard

Introduction

The physical simulation of a vehicle requires many components and settings that must be created and tuned. To make the process a bit easier, Create provides a Vehicle Wizard that asks for some initial parameters as inputs and creates a working vehicle simulation. If the first vehicle created has to be changed, it can be deleted and the Vehicle Wizard settings can be adjusted to try again.

Alternatively, Create provides a couple of Vehicle Samples in the Physics Demo Scenes that can be used as a starting point for a vehicle simulation. One uses a Basic Drive setup and the other uses a Standard Drive setup. The differences between these two types are discussed in the Vehicle Wizard Basic Page documentation, below.

Authoring the Vehicle’s Rendered Mesh

The Vehicle Wizard and the Vehicle Samples create a vehicle out of primitive geometric shapes, a box for the chassis and four cylinders for the wheels. An artist authored vehicle mesh used for rendering can then be linked to these transforms and animated.

VehicleGeometricPrimitives

The physics representation created by Create and the rendered vehicle mesh should be authored in the same coordinate system and units. This will avoid the need to add additional rotations or scale to make the two representations match. The Vehicle Samples are hard coded to use the Y axis as the up axis. However, the Vehicle Wizard can be configured to match any coordinate system. Prior to using the Vehicle Wizard, open the Edit > Preferences window and select the Default Up Axis to Y or Z to match the coordinate system used to author the rendered vehicle (the setting can be found under Stage). Create a new stage (File > New) to make sure the changed defaults take effect.

VehicleEditPreferences

There are many different types of vehicle suspensions and there are several ways to articulate them. A full simulation of the suspension using rigid-bodies and constraints is a separate topic and will not be discussed here. Simulating the wheels and then animating the suspension pieces to maintain a connection between the wheels and the chassis is a simpler, faster, technique. Animating the suspension in this way can also be accomplished in different ways depending on the need for accuracy.

The easiest way to animate the tires on the rendered vehicle is to connect them to the tires animated by the physics simulation. (Refer to Animating the Wheels to see how to do this.) The drawback of this method is that the suspension parts themselves will be rigidly connected to the chassis and will not animate. This is useful if the vehicles are not the primary focus of the project. If this technique is suitable, the only parts of the rendered mesh that must be separated from the original mesh are the rotating parts of the wheel and tire. All the other parts can remain part of the chassis mesh.

For a higher fidelity animation of the vehicle suspension, the mesh will need to be further subdivided. The rendered mesh will need to be broken apart into parts of the wheel that steer and move up and down with the suspension, but do not rotate and parts of the wheel that do rotate. Next, separate all the suspension parts that connect the non-rotating wheel parts to the chassis. Do this for each wheel. Although there are many different suspension configurations, the following primitive hierarchy should work for most:

  • Chassis (All vehicle parts that are rigidly connected to the body of the vehicle.)

    • FrontLeftSuspension (All non-rotating parts that steer and move vertically as the suspension compresses.)

      • FrontLeftWheel (All parts of the wheel and tire that rotate.)

Duplicate this hierarchy for each of the wheels and tires.

The Wheel and Tire transforms should be positioned in the exact center of rotation for each wheel. The Suspension Part transforms should be centered on their axes of rotation if they rotate.

It is recommended, but not required, that the vehicle mesh is loaded into the stage prior to using the Vehicle Wizard so that all primitives are on the same layer. This makes it possible to simply drag and drop the appropriate mesh pieces onto the appropriate physics vehicle hierarchy nodes. If the Vehicle Wizard is used first, the vehicle mesh may end up being referenced on a different layer. This prevents it from being separated and linked to different pieces of the physics hierarchy.

The next step is to create or load the physics representation of the vehicle by using the Vehicle Wizard or loading one of the Vehicle Samples. Refer to the Animating the Vehicle’s Rendered Mesh section, below, for the next steps to connecting the rendered mesh to the vehicle’s physics simulation.

Vehicle Wizard Description

The Vehicle Wizard can be opened through the Create > Physics > Vehicle menu command.

The Vehicle Wizard contains three pages. The first asks for the basic dimensions and configuration of the vehicle, the second sets the dimensions of the tires and suspension settings. The last page provides a check list of optional Next Steps to complete the setup of the vehicle and its physics.

The vehicle created by the wizard will be oriented according to Create’s coordinate frame. If Z-up is selected, the positive X or Y axes can be the forward direction. If Y-up is selected, the X or Z axes can be forward. The Vehicle Wizard will create a PhysicsScene primitive with the PhysxVehicleContextAPI applied which will reflect the chosen configuration. However, if there already exists a physics scene primitive with PhysxVehicleContextAPI applied in the stage, its up axis and forward axis information will be used to orient the new vehicle.

Basic Page

VehicleBasicPage

In the Chassis Box panel, enter the Length, Width and Height in the same units that are being used in Create to model the world. These dimensions will be used to create a collision box that should contain most of the vehicle mesh geometry and will also be used to compute the mass distribution, or moments of inertia, of the vehicle. The collision box that is created by the wizard can be deleted and replaced with a more form fitting representation later, if desired. However, these inputs are still required to compute the vehicle’s basic mass properties.

Enter the weight of the vehicle in the Mass edit box in kilograms if you want to deviate from the automatically computed value.

Position the center of the chassis box by entering the X, Y and Z positions in the appropriate fields. The forward direction of the vehicle can be selected from the Forward Axis drop down list. If the prim with PhysxVehicleContextAPI applied exists on the stage, the Forward Axis drop down will be disabled and the PhysxVehicleContextAPI forward axis will be displayed. If a forward direction was not previously selected, the available options will be presented. For a Y up axis, the X and Z options will be available. For a Z up axis, X and Y can be selected.

An option to automatically fill in most of these settings is available by selecting a set of prims from the stage that represent the chassis and then pressing the Scan button. Make sure to define the Forward Axis as desired prior to running the scan. The Vehicle Wizard will fit an axis aligned bounding box to the selected prims and fill in the dimensions and position seetings. The mass will get adjusted too unless the field was previously overridden. Any of the automated entries can be adjusted after the scan is complete. Prims have a purpose that describes what they are used for, such as render, guide, default and proxy. Only render and default prims are scanned. Also, only geometric meshes and shapes can be used to fit a bounding box.

The Drive panel asks for one of three methods to propel the vehicle: Standard, Basic or None.

The Number of Axles edit box sets the number of tires on the vehicle. The tire settings are tuned on the next page.

Press the Next button to access these settings. Alternatively, the Create button can be pressed to use the Axle Page’s default settings to create the vehicle. Press Reset at any time to reset the wizard settings to their default values (this will also clear fields overriden by the user and return to computing those values automatically).

Property

Description

Length

The longitudinal dimension of the vehicle chassis in world units

Width

The lateral dimension of the vehicle chassis in world units

Height

The vertical dimension of the vehicle chassis in world units

Mass

The weight of the entire vehicle in kg

X

The world X position of the center of the chassis in world units

Y

The world Y position of the center of the chassis in world units

Z

The world Z position of the center of the chassis in world units

Forward Axis

The forward direction of the vehicle

Scan Button

Computes an axis aligned bounding box around the selected prims and fills in the dimensions,
position and mass settings automatically.

Type

Selects which propulsion model is used to generate tire torques (Standard, Basic, None)

Horsepower

For the Standard and Basic drive type, specifies how much power the engine can generate at its peak

RPM

For the Standard drive type, specifies the maximum revolutions per minute the engine can reach

Number of Gears

For the Standard drive type, specifies the number of gears the transmission has.

Number of Axles

Specifies the number of pairs of tires the vehicle has.

Create Shareable Components

Vehicle components like wheel, tire, suspension, engine etc. will each get their own prim

Reset Button

Reset the wizard settings to their default values

Create Button

Create a vehicle simulation using the current settings

Next Button

Go to the Axles page

Standard Drive

The Standard drive type utilizes an engine and transmission to transfer torque to the driven tires. When using this Drive Type, enter the maximum engine Horsepower, the maximum engine RPM and the Number of Gears in the transmission.

The vehicle engine will be configured to follow an horsepower versus engine RPM curve that is defined by the following reference points:

RPM

Horsepower (HP)

0

80% of max HP

33% of max RPM

100% of max HP

max RPM

80% of max HP

This power curve outputs peak horsepower when the engine reaches a third of its maximum RPM and drops off to 80% at engine idle and max RPM. This curve is not very realistic, but it generates more power at idle for better acceleration from rest.

The Number of Gears specify how many gears are in the transmission. The top gear is always 1:1 and the 1st gear ratio is set to the same value as the number of gears, 5:1 in a transmission with 5 gears, for example. The remaining gears evenly reduce the gear ratios between the 1st gear and the top gear. The final gear ratio is set to 4:1.

Gears scale the torque generated by the engine before it is applied to the tires. However, the higher the gear ratio, the sooner the transmission must shift up to the next gear before the engine reaches its maximum RPM. Heavier vehicles require higher gear ratios to generate more tire torque to accelerate but require additional gears and more time shifting to compensate.

The vehicle’s top speed will be determined by the RPM and to some degree by the Horsepower setting, while the vehicle’s acceleration profile will be controlled by the Number of Gears and Horsepower setting. If increasing the Horsepower does not increase the vehicle acceleration, then it is likely that the driven tires are spinning or burning out. To further increase the vehicle acceleration, the tire longitudinal stiffness or friction will need to be increased.

Basic Drive

The Basic drive type works by simply setting a maximum torque that is applied to the driven tires. No engine or transmission is created. The torque is scaled by the amount of throttle applied. The input device is also used to control the steer angle of the tires that steer. This Drive Type is useful for simulating electric vehicles. The maximum torque is computed from the Horsepower setting (and the RPM value which is kept locked when this drive type is selected: Horsepower * 7120 / RPM).

No Drive (None)

When the None drive type is selected, no tire torques are passed to the tires by the vehicle. The tire torques and steer angles are set manually, instead. This allows customized user control of each tire independently. This can be useful for robotics applications, for example, where tires may be rotated in opposite directions to spin the robot in place.

Axles Page

VehicleAxlesPage

The Axles panel is used to specify which tires can steer in the Standard and Basic drive types and how much those tires steer. Tire torques are passed to all the tires that have the Driven check box selected. These settings are disabled for the, None drive type. Check the front axle for front wheel drive, check the rear axle for rear wheel drive, or check both axles for all wheel drive.

The Weight Distribution edit box specifies the percentage of the vehicle weight that rests on each axle. Best results are observed when the weight is evenly distributed, with slight adjustments. The percentages should all sum to 100%, but if they do not, each percentage will be normalized.

The Suspension Damping is used to control the bounce of the vehicle after it is disturbed, like going over a bump. A normal value of 0.3 is the default. This setting causes the vehicle to bounce once before settling. Higher values damp out oscillations faster. A setting of 0.7 causes the vehicle to settle without a single oscillation. For a bouncy ride, try settings between 0.1 and 0.3.

Scan Selected Prims buttons labeled Left and Right are provided to measure and automatically fill in the Radius, Width and Mass settings for each of the tires scanned. Just like the chassis box on the Basic Page, only render and default purpose prims are scanned and only geometric meshes and shapes can be used to fit a bounding box. Make sure to define the Forward Axis on the Basic Page as desired prior to running the scan. Once a tire is scanned, its dimensions will appear below the Default wheel settings in the Wheels panel.

Inside the Wheels panel on the Axles page, select the tire Tire Type to use. The racing Slicks are stickiest on smooth surfaces. Summer tires provide a good grip on smooth, dry surfaces while All Season tires have the least grip on smooth, dry surfaces. The Enable Collisions check box is provided to generate collision objects for the tires themselves. This is useful for open wheeled vehicles, for example, but requires filtering and distinction between geometry to collide against and geometry to drive on. Also note that if the chassis collision box encloses the tires, performance can be improved by disabling the tire collisions.

The Default tire Radius and Width, entered in the same units used to specify the chassis dimensions, are used to set the size of the tire used in the tire physics and tire collision detection. The Mass includes the wheel and tire. Any wheels that were scanned in the Axles panel will be presented below the Default settings. Any wheels that are not scanned will use the defaults.

Press, the Back button to re-adjust the Basic page settings. Changing the number of axles will clear all scanned wheels. The Create and Reset button behave the same as on the Basic Page.

Property

Description

Steer Angle

The maximum angle the tire can rotate to one side, in degrees.

Driven

When checked, the tires on this axle receive a drive torque in the Standard or Basic drive types.

Weight Distribution

Specifies the percentage of the vehicle weight supported by this axle. The percentages should sum
to 100% but will be normalized if they do not.

Suspension Damping

The “damping ratio” of the shock absorbers, from 0 to 1. A value of 0.3 is normal, a smaller
number yields more bounce, larger is more rigid.

Left Button

Computes an axis aligned bounding box around the selected prims and fills in the Radius, Width and Mass
settings for the left wheel on the specified axle. The dimensions and mass can be adjusted after the scan is complete.

Right Button

Same as Left Button but applying to the right wheel on the specified axle.

Tire Type

The type of tire to use on the vehicle, which principally sets the tire friction.

Enable Collisions

When checked, collision objects for the tires will be created.

Query Type

The type of scene query to use for detecting collision of the wheel with the ground surface.

Default Radius

The vertical size of the tire from its center to an edge in world units

Default Width

The lateral size of the tire from edge to edge in world units

Default Mass

The mass of the wheel and tire, in kg

# Left

The Radius and Width, in world units, and the Mass, in kg, of scanned wheels on the left side of the
specified axle. Unscanned wheels will use the Default settings.

# Right

Same as # Left but applying to the right wheel on the specified axle.

Reset Button

Reset the wizard settings to their default values

Create Button

Create a vehicle simulation using the current settings

Back Button

Return to the Basic page

Next Button

Go to the Next Steps page.

Next Steps Page

VehicleNextStepsPage

Once the Vehicle Wizard has created all of the needed prims to run a vehicle simulation, there is still more work to do. The rendered mesh must be animated by linking it to the vehicle created by the wizard, a ground surface to drive on is needed and the physics settings of the vehicle can be tuned. The Next Steps page will be updated and provides additional detail.

Property

Description

Reset Button

Reset the wizard settings to their default values

Create Button

Create a vehicle simulation using the current settings

Back Button

Return to the Axles page

Vehicle Wizard Collision Groups

The Vehicle Wizard does not create a drivable surface, but it does create collision groups to prevent the wheels of the car from colliding with the chassis, for example. One of the collision groups created is called GroundSurfaceCollisionGroup, which is set up to prevent collisions with objects in the VehicleWheelCollisionGroup. This prevents the wheels from colliding with the ground surface (instead, tire collisions with the surface are detected using ray casts or sweeps). If Enable Collisions was ticked, be sure the driving surface is a member of the GroundSurfaceCollisionGroup.

To add a simple, drivable surface for testing, select Create > Physics > Ground Plane. If Enable Collisions was ticked, the surface must be added to the GroundSurfaceCollisionGroup. Select the GroundSurfaceCollisionGroup prim on the stage. In the Property panel, find the Collision Group section and add the created collision plane under Includes. When using custom drivable surfaces, ensure they are all members of the GroundSurfaceCollisionGroup.

VehicleCollisionGroup

Animating the Vehicle’s Rendered Mesh

Once the physics representation of the vehicle has been created, the vehicle’s rendered mesh can be linked to it. Select the Chassis root primitive, created as described in the Authoring the Vehicle’s Rendered Mesh section, above, and child it to the /WizardVehicle/Vehicle primitive, if the vehicle was created with the Vehicle Wizard, or the /Vehicle primitive if a Vehicle Sample was loaded. The Chassis transform may need to be moved so the rendered vehicle lines up with the physics box as much as possible. The rendered vehicle mesh should now move around with the vehicle.

The Vehicle Wizard creates a box to visualize the chassis when no other mesh is connected to the vehicle. It can be removed by selecting the /WizardVehicle/Vehicle/ChassisRender primitive on the stage and deleting it or hiding it, by clicking on the eye icon, next to it.

Aligning the Tires with the Vehicle

If the Vehicle Wizard tire scan feature was not used to measure the position and radius of each tire, the physics representation of the tires will not align properly with the rendered, mesh representation. Move the physics representation of the tires by selecting each of them in the Viewport window and then dragging them into position.

For improved accuracy, the X, Y and Z position of each wheel in the rendered mesh hierarchy can be copied into the physics Position transforms. The Vehicle Wizard primitives that need updating are named, “/WizardVehicle1/Vehicle/LeftWheel1References”, “/WizardVehicle1/Vehicle/RightWheel1References”, “/WizardVehicle1/Vehicle/LeftWheel2References”, “/WizardVehicle1/Vehicle/RightWheel2References” etc. The Vehicle Sample wheels are named “FrontLeftWheel”, “FrontRightWheel”, “RearLeftWheel” and “RearRightWheel”.

Adjusting the Tire Radii

Some vehicles have different tire radii for the front and rear tires. If the Vehicle Wizard tire scan feature was not used, only a single tire radius is used for all of the tires. The tire radii that are different must be manually adjusted. Find the wheel components created by the wizard and adjust their tire radii. The wheel components are called “/WizardVehicle1/LeftWheel1”, “/WizardVehicle1/RightWheel1”, “/WizardVehicle1/LeftWheel2” and “/WizardVehicle1/RightWheel2” (or the “/WizardVehicle1/Vehicle/__Wheel_References” prims in case the wizard was told to not create shareable components). Each have a radius edit box that can be adjusted.

The vehicle wheels also have a render and potentially a collision representation whose radii should be modified, too. Click the “/WizardVehicle1/Vehicle/LeftWheel1References/Render” primitive (or the appropriate primitive for the tire that must be changed), select the Property panel and scroll to the bottom to find the radius.

Likewise, to update the collision tire radius, find the “/WizardVehicle1/Vehicle/LeftWheel1References/Collision” primitive (or the appropriate primitive for the tire that must be changed), select the Property panel and scroll to the bottom to find the radius.

VehicleTireRadius

Authoring Tool

Whenever the physics tires are moved, however, there are physics properties that must be updated. A tool has been provided to simplify this process. First select the Vehicle primitive, click on the Property tab and then click on the Vehicle Authoring Helpers panel Apply button. The physics properties that are updated by the Authoring tool include the point at which the suspension and tire forces are applied as well as the wheel rest position.

VehicleAuthoring

Once the tires align with the physics representation, child all the non-rotating parts to their respective “Wheel” transforms and all of the rotating parts to their respective “Tire” transforms. The transforms themselves will have to be animated through a script, which will be described in the next section.

If the tire render primitives are no longer needed, they can be deleted or hidden.

Animating the Wheels

If it is satisfactory to allow the physics to animate the wheels and tires, all of the rotating wheel and tire parts of the rendered mesh can simply be linked to the “/Vehicle/LeftWheel1References”, “/Vehicle/RightWheel1References”, “/Vehicle/LeftWheel2References”, “/Vehicle/RightWheel2References” etc. primitives.

However, if it is preferred to have non-rotating wheel parts that steer and compress, but not rotate with the tires, then a more sophisticated approach is required and the more complicated hierarchy described in Authoring the Vehicle’s Rendered Mesh and animated in Copying Transforms is required.

Copying Transforms

The position and orientation of the physics representation of the wheels must be broken apart into three components and copied into the rendered mesh representation after each simulation step, or update.

This can only be accomplished using a Python script. The exact math to do this is different for every vehicle and suspension configuration, but the two very common configurations are provided as an example in the following script for a double wishbone and a swing arm configuration. The front tires use a double wishbone and simply move up and down to compress the tire. The rear tires use a swing arm configuration. The angle of the swingarm is calculated from the compression of the tires and the swing arm radius. This code is used to load a USD file, register for an update callback, and then animate the suspension.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
 import math

 import omni.kit.app
 import omni.usd
 import omni.physx
 from omni.physx.bindings._physx import SimulationEvent

 from pxr import Usd, UsdGeom, Sdf, Gf


 # Put the name of your USD file here.
 myStageName = "C:/Users/username/Desktop/PodWorkVehicle.usd"

 myVehiclePrimName = "/World/WizardVehicle1/Vehicle"

 myFrontLeftName = "/LeftWheel1References"
 myFrontRightName = "/RightWheel1References"
 myRearLeftName = "/LeftWheel2References"
 myRearRightName = "/RightWheel2References"

 frontLeftTirePath = myVehiclePrimName + myFrontLeftName
 frontRightTirePath = myVehiclePrimName + myFrontRightName
 rearLeftTirePath = myVehiclePrimName + myRearLeftName
 rearRightTirePath = myVehiclePrimName + myRearRightName

 myFrontLeftSuspensionName = "/FrontLeftSuspension"
 myFrontRightSuspensionName = "/FrontRightSuspension"
 myRearLeftSuspensionName = "/RearLeftSuspension"
 myRearRightSuspensionName = "/RearRightSuspension"

 frontLeftSuspensionPath = myVehiclePrimName + myFrontLeftSuspensionName
 frontRightSuspensionPath = myVehiclePrimName + myFrontRightSuspensionName
 rearLeftSuspensionPath = myVehiclePrimName + myRearLeftSuspensionName
 rearRightSuspensionPath = myVehiclePrimName + myRearRightSuspensionName

 myFrontLeftWheelName = "/FrontLeftWheel"
 myFrontRightWheelName = "/FrontRightWheel"
 myRearLeftWheelName = "/RearLeftWheel"
 myRearRightWheelName = "/RearRightWheel"

 frontLeftWheelPath = frontLeftSuspensionPath + myFrontLeftWheelName
 frontRightWheelPath = frontRightSuspensionPath + myFrontRightWheelName
 rearLeftWheelPath = rearLeftSuspensionPath + myRearLeftWheelName
 rearRightWheelPath = rearRightSuspensionPath + myRearRightWheelName


 class AnimatedVehicleClass:

     def load_animation(self):
         # Get all of the necessary prims
         self._vehiclePrim = self._stage.GetPrimAtPath(myVehiclePrimName)

         self._frontLeftTire = self._stage.GetPrimAtPath(frontLeftTirePath)
         self._frontLeftSuspension = self._stage.GetPrimAtPath(frontLeftSuspensionPath)
         self._frontLeftWheel = self._stage.GetPrimAtPath(frontLeftWheelPath)

         self._frontLeftSuspension.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
         self._frontLeftWheel.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
         self._frontLeftSuspension.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)
         self._frontLeftWheel.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)

         self._frontRightTire = self._stage.GetPrimAtPath(frontRightTirePath)
         self._frontRightSuspension = self._stage.GetPrimAtPath(frontRightSuspensionPath)
         self._frontRightWheel = self._stage.GetPrimAtPath(frontRightWheelPath)

         self._frontRightSuspension.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
         self._frontRightWheel.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
         self._frontRightSuspension.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)
         self._frontRightWheel.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)

         self._rearLeftTire = self._stage.GetPrimAtPath(rearLeftTirePath)
         self._rearLeftSuspension = self._stage.GetPrimAtPath(rearLeftSuspensionPath)
         self._rearLeftWheel = self._stage.GetPrimAtPath(rearLeftWheelPath)

         self._rearLeftSuspension.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
         self._rearLeftWheel.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
         self._rearLeftSuspension.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)
         self._rearLeftWheel.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)

         self._rearRightTire = self._stage.GetPrimAtPath(rearRightTirePath)
         self._rearRightSuspension = self._stage.GetPrimAtPath(rearRightSuspensionPath)
         self._rearRightWheel = self._stage.GetPrimAtPath(rearRightWheelPath)

         self._rearRightSuspension.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
         self._rearRightWheel.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
         self._rearRightSuspension.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)
         self._rearRightWheel.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)

         rearRightWheelPosition = self._rearRightTire.GetAttribute("xformOp:translate").Get()
         self._rearWheelRestHeight = rearRightWheelPosition[2]
         self._swingArmRadius = rearRightWheelPosition.GetLength()

     def load_stage(self, success, errorMsg):
         if success == True:
             self._stage = self._usd_context.get_stage()
             self._physxVehicleInterface = omni.physxvehicle.get_physx_vehicle_interface()

             self.load_animation()

             # Subscribe to events
             self._physxSimEventSubscription = omni.physx.get_physx_interface().get_simulation_event_stream().create_subscription_to_pop(
                 self._on_simulation_event
             )
             self._stageEventSubscription = self._usd_context.get_stage_event_stream().create_subscription_to_pop(self.on_stage_event)
             self._updateEventSubscription = omni.kit.app.get_app().get_update_event_stream().create_subscription_to_pop(self.update)

     def __init__(self):
         self._simStarted = False

         print("Loading Stage " + myStageName)

         self._usd_context = omni.usd.get_context()

         self._physxVehicleInterface = None

         # Load the level
         self._usd_context.open_stage(myStageName, self.load_stage)

     def _on_simulation_event(self, event):
         if event.type == int(SimulationEvent.RESUMED):
             self._simStarted = True
         elif event.type == int(SimulationEvent.STOPPED):
             self._simStarted = False

     def on_stage_event(self, event):
         if (event.type == int(omni.usd.StageEventType.CLOSING)):
             self.shutdown()

     def shutdown(self):
         self._physxSimEventSubscription = None
         self._updateEventSubscription = None
         self._stageEventSubscription = None

     def animate(self):

         # Front Left
         wheelQueryResults = self._physxVehicleInterface.get_wheel_query_results(frontLeftTirePath)
         position = wheelQueryResults["localPosePosition"]
         rotation = wheelQueryResults["localPoseQuaternion"]
         frontLeftPosition = Gf.Vec3f(position[0], position[1], position[2])
         frontLeftRotation = Gf.Quatf(rotation[3], rotation[0], rotation[1], rotation[2])

         self._frontLeftSuspension.GetAttribute("xformOp:translate").Set(frontLeftPosition)

         frontLeftSuspensionRotation = Gf.Quatf(rotation[3], 0.0, 0.0, rotation[2])
         frontLeftSuspensionRotation.Normalize()
         self._frontLeftSuspension.GetAttribute("xformOp:orient").Set(frontLeftSuspensionRotation)

         frontLeftWheelRotation = Gf.Quatf(rotation[3], 0.0, rotation[1], 0.0)
         frontLeftWheelRotation.Normalize()
         self._frontLeftWheel.GetAttribute("xformOp:orient").Set(frontLeftWheelRotation)

         # Front Right
         wheelQueryResults = self._physxVehicleInterface.get_wheel_query_results(frontRightTirePath)
         position = wheelQueryResults["localPosePosition"]
         rotation = wheelQueryResults["localPoseQuaternion"]
         frontRightPosition = Gf.Vec3f(position[0], position[1], position[2])
         frontRightRotation = Gf.Quatf(rotation[3], rotation[0], rotation[1], rotation[2])

         self._frontRightSuspension.GetAttribute("xformOp:translate").Set(frontRightPosition)

         frontRightSuspensionRotation = Gf.Quatf(rotation[3], 0.0, 0.0, rotation[2])
         frontRightSuspensionRotation.Normalize()
         self._frontRightSuspension.GetAttribute("xformOp:orient").Set(frontRightSuspensionRotation)

         frontRightWheelRotation = Gf.Quatf(rotation[3], 0.0, rotation[1], 0.0)
         frontRightWheelRotation.Normalize()
         self._frontRightWheel.GetAttribute("xformOp:orient").Set(frontRightWheelRotation)

         # Rear Left
         wheelQueryResults = self._physxVehicleInterface.get_wheel_query_results(rearLeftTirePath)
         position = wheelQueryResults["localPosePosition"]
         rotation = wheelQueryResults["localPoseQuaternion"]

         angle = (position[2] - self._rearWheelRestHeight) / self._swingArmRadius
         rearLeftSuspensionRotation = Gf.Quatf(math.cos(0.5 * angle), 0.0, math.sin(0.5 * angle), 0.0)
         self._rearLeftSuspension.GetAttribute("xformOp:orient").Set(rearLeftSuspensionRotation)

         rearLeftWheelRotation = Gf.Quatf(rotation[3], 0.0, rotation[1], 0.0)
         rearLeftWheelRotation.Normalize()
         self._rearLeftWheel.GetAttribute("xformOp:orient").Set(rearLeftWheelRotation)

         # Rear Right
         wheelQueryResults = self._physxVehicleInterface.get_wheel_query_results(rearRightTirePath)
         position = wheelQueryResults["localPosePosition"]
         rotation = wheelQueryResults["localPoseQuaternion"]

         angle = (position[2] - self._rearWheelRestHeight) / self._swingArmRadius
         rearRightSuspensionRotation = Gf.Quatf(math.cos(0.5 * angle), 0.0, math.sin(0.5 * angle), 0.0)
         self._rearRightSuspension.GetAttribute("xformOp:orient").Set(rearRightSuspensionRotation)

         rearRightWheelRotation = Gf.Quatf(rotation[3], 0.0, rotation[1], 0.0)
         rearRightWheelRotation.Normalize()
         self._rearRightWheel.GetAttribute("xformOp:orient").Set(rearRightWheelRotation)

     def update(self, event):
         if (self._simStarted):
             self.animate()

 animatedVehicle = AnimatedVehicleClass()

Driving the Vehicle

After the Vehicle Wizard has created the vehicle, try driving it around a bit to see how it behaves. The vehicle can be controlled using the keyboard or a connected gamepad.

Every vehicle has some additional properties to control it. On the stage, find and select the vehicle to adjust. In the Property panel, scroll down to the Vehicle Controller Settings window. To select which vehicle to control using the input system, check or un-check the Input Enabled check box. To enable the mouse, check the Mouse Enabled check box. If it is undesirable to automatically go into reverse when the brake is held while at rest, un-check the Auto Reverse Enabled checkbox.

VehiclePhysXProperties

Vehicle Cameras

Driving a vehicle around the Create Viewport can be difficult because the vehicle will quickly drive out of frame. A couple of simple camera controllers were created to help with this problem. To add one, select the vehicle to follow, click on the Property tab, scroll down to the Vehicle Cameras window and click the Add Follow Camera or Add Drone Camera button. The Follow Camera creates a camera that simulates following the vehicle from a chase vehicle and the Drone Camera simulates following the vehicle from the air, in a drone. Multiple cameras can be created for each vehicle and their settings can be tuned for different behaviors.

VehicleAddCameras

To select the camera from which to follow the vehicle, click on the camera button in the top left corner of the Viewport window, then click on the Camera in the pop up menu and select the vehicle’s follow or drone camera. The camera position is not updated until the simulation is started by pressing the Play button, so the initial view may be black, but press Play, and the camera will move into position.

VehicleSelectCamera

Dynamic Vehicle Authoring

Vehicles can be created and destroyed while the simulation is running. This is called Dynamic Vehicle Authoring. This feature can be used to warp vehicles from one end of a street to the other in order to create the illusion of an endless stream of city traffic, for example.

Presently, there are a few restrictions with how this is done:

  1. All of the vehicle primitives (prims) must be created within a single simulation time step when creating a vehicle. Many of these prims reference other prims and the vehicle creation process will fail if the referenced prims are missing.

  2. A Physics Scene is required before the simulation is started. An error will be generated if a vehicle is created without one.

  3. To destroy a vehicle, all of the vehicle prims must be deleted within a single simulation time step.

Vehicle cameras can also be created and destroyed while the simulation is running by using the physxVehicleInterface.add_follow_camera or physxVehicleInterface.add_drone_camera Python scripts or through the User Interface Add Follow Camera or Add Drone Camera buttons. To remove the cameras, delete the camera prims themselves. When deleting the vehicle, delete all of the vehicle prims along with the vehicle’s camera prims.