PhysX Project
Project Objectives
The objective of this project was to simply integrate the Nvidia PhysX Physics Engine with my personal C++ game engine. This project features the use of Nvidia PhysX as well as the Nvidia Vehicle SDK. The vehicle has a car controller that currently works only with the use of an xbox controller.
The project also features PhysX joints, articulation and other PhysX features.
NOTE: As this project is integrated in my personal C++ engine, it has been added to this project as a submodule. However, there is a simple setup guide that shows how you can get PhysX running in any other VC++ project provided a rendering engine has been implemented. For a guide on how to setup physX on your project, simply follow the guide below. More posts and information will be posted on my website and updated here for more in depth tutorials.
Project License
I will post a formal license soon. Until then please feel free to do what you want with this project's code.
PhysX SDK Setup Guide
In this guide we will discuss how to setup Nvidia PhysX with a Visual C++ project
Sections to this post
- Section 1. Generating Sample Projects
- Section 2. Linking PhysX Libraries
- Section 3. Setting up Runtimes and Configurations
- Section 4. Using PhysX with your project
Section 1. Generating Sample Projects
Before we can use Nvidia PhysX with our project, we need to setup the libraries required with our project. To do this you will need to first download and extract the files for Nvidia PhysX. I used version 4.0 but the steps should be the same for other versions of PhysX
Step 1 : Getting required files and software#
- Download and extract the PhysX SDK from Nvidia's website
- Install CMake
- Install Phython
- Add Python to your system path if it doesn't already exist
Step 2 : Generating all projects#
- Run the
generate_projects.bat
file to create the visual studio project files for all of PhysX's projects - After you have the generated the project files successfully, run the sample projects and make sure everything is working
- Once we have the sample files running, we can move on to the next step which is setting up the libraries required
Section 2. Linking PhysX Libraries
Now that we have all the source files, lib files and dll files required to use PhysX, we can start linking the libraries required to our projects.
There are different approaches to doing this, one of which is to compile the source code of PhysX with your project. Using source code can be helpful in cases where we would want to view and modify the inner workings of PhysX. For this guide however, we will not be compiling the source code of PhysX, instead we will link the static libraries provided by PhysX to our project in Visual Studio.
Step 1 : Finding the library files we need#
PhysX has a ton of features and while these features allow us to explore many interesting usecases, it is recommended to only use the libraries you need and link other libraries as and when they will be required by your project.
This guide will continue to showcase some features of PhysX and hence demonstrate the use of numerous libraries. You may wish to not use some of these libraries or link more libraries depending on your needs.
-
Find your libraries in the
physx/bin
folder with your respective build configuration (I used Windows so my library files were in thewin.x86_64.vc141.mt
folder) -
To use physx, we will need to tell the compiler what files to include when building our projects.
-
The include files are in the
physx/include
folder -
You will need to add this folder to the
additional include directiories
path in your project- To do that, you will need to open your visual studio project and right click on your solution, select properties
- On the properties window select linker in the left tab of configuration properties
- You will see an
additional include directories
option, add your include path as a new entry in the list
Step 2 : Telling the linker what we need#
Now that we have added the include directories, we will need to inform the linker to link with these libraries during compile time.
This can be done in 2 ways:
1. You can open the properties panel for your project and setup `additional dependencies`
- Properties->Linker->Input->Additional Dependencies
2. You can link to the libraries using code.
- For this you will need to add the following to your code
//PhysX Pragma Comments
#if ( defined( _WIN64 ) & defined( _DEBUG ) )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysX_64.lib" )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysXCommon_64.lib" )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysXCooking_64.lib" )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysXExtensions_static_64.lib" )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysXFoundation_64.lib" )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysXPvdSDK_static_64.lib" )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysXVehicle_static_64.lib" )
#elif ( defined ( _WIN64 ) & defined( NDEBUG ) )
#pragma comment( lib, "PhysX/lib/release_x64/PhysX_64.lib" )
#pragma comment( lib, "PhysX/lib/release_x64/PhysXCommon_64.lib" )
#pragma comment( lib, "PhysX/lib/release_x64/PhysXCooking_64.lib" )
#pragma comment( lib, "PhysX/lib/release_x64/PhysXExtensions_static_64.lib" )
#pragma comment( lib, "PhysX/lib/release_x64/PhysXFoundation_64.lib" )
#pragma comment( lib, "PhysX/lib/release_x64/PhysXPvdSDK_static_64.lib" )
#pragma comment( lib, "PhysX/lib/release_x64/PhysXVehicle_static_64.lib" )
#endif
NOTE In the code snippet above, the #pragma comment(lib, < file_path >) tells us what libraries we wish to link for the x64 platform for the Debug and Release build configurations. You will need to set this up for the different configurations we intend to use with our project. The next section will go into more detail on configurations and how you may set them up
Section 3. Setting up Runtimes and Configurations
In this section we will talk about the runtimes we will use with PhysX and the configuration options we may use with our project.
Step 1 : Runtimes for Build configurations
Your project will have 1 or more build configurations depending on your project setup. For my project, I have setup a Debug and a Release build configurations for both the windows x86 and x64 platforms. Your configurations may vary depending on your usecase but this guide assumes you have atleast one build configuration setup.
To setup a new build configuration, you may perform the steps below:
- Right click on your project and select properties
- Click on the Configuration Manager button on the top right corner
- In your Configuration Manager window, you may use the drop downs on
solution_configuration
andsolution_platform
to create a new configuration of your choice
NOTE:
PhysX supports 4 different build configurations. They are Debug, Release, Checked and Profile. Information on the various build configurations supported can be found on the PhysX API documentation online.
Step 2 : Code generation settings
Once you have your build configurations setup, you will need to setup your `runtime_library` settings for the project.
To do so, follow the steps below:
- Open your project properties tab
- Select the C/C++ drop down in the configuration properties tab on the left
- Select Code Generation option
NOTE
Now there are a few things to consider when you are in this window. You will need to setup different runtime options for your different build configurations. The Release code for PhysX uses Multi Threaded (/MT)
setting while the Debug configuration uses Multi Threaded Debug (/MTd)
option.
- Select your build configuration from the drop down on the top left
- Setup the appropriate
Runtime Library
option for your configuration (/MT or /MTd)
With that you have your runtime libraries setup for PhysX. You may compile your code to make sure your existing code base is working as exepected.
Step 3 : Linking required libraries appropriately
Now although we performed the linking of libraries in the previous section, we will double check that we are still linking liraries for our new build configurations for all our supported platforms.
To do so, please refer again to Section 2, step 2 to make sure you have your libraries setup for your different configurations. The code I used is as follows:
//PhysX Pragma Comments
#if ( defined( _WIN64 ) & defined( _DEBUG ) )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysX_64.lib" )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysXCommon_64.lib" )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysXCooking_64.lib" )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysXExtensions_static_64.lib" )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysXFoundation_64.lib" )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysXPvdSDK_static_64.lib" )
#pragma comment( lib, "PhysX/lib/debug_x64/PhysXVehicle_static_64.lib" )
#elif ( defined ( _WIN64 ) & defined( NDEBUG ) )
#pragma comment( lib, "PhysX/lib/release_x64/PhysX_64.lib" )
#pragma comment( lib, "PhysX/lib/release_x64/PhysXCommon_64.lib" )
#pragma comment( lib, "PhysX/lib/release_x64/PhysXCooking_64.lib" )
#pragma comment( lib, "PhysX/lib/release_x64/PhysXExtensions_static_64.lib" )
#pragma comment( lib, "PhysX/lib/release_x64/PhysXFoundation_64.lib" )
#pragma comment( lib, "PhysX/lib/release_x64/PhysXPvdSDK_static_64.lib" )
#pragma comment( lib, "PhysX/lib/release_x64/PhysXVehicle_static_64.lib" )
#elif ( defined( _WIN32 ) & defined( _DEBUG ) )
#pragma comment( lib, "PhysX/lib/debug_x86/PhysX_32.lib" )
#pragma comment( lib, "PhysX/lib/debug_x86/PhysXCommon_32.lib" )
#pragma comment( lib, "PhysX/lib/debug_x86/PhysXCooking_32.lib" )
#pragma comment( lib, "PhysX/lib/debug_x86/PhysXExtensions_static_32.lib" )
#pragma comment( lib, "PhysX/lib/debug_x86/PhysXFoundation_32.lib" )
#pragma comment( lib, "PhysX/lib/debug_x86/PhysXPvdSDK_static_32.lib" )
#pragma comment( lib, "PhysX/lib/debug_x86/PhysXVehicle_static_32.lib" )
#elif ( defined( _WIN32 ) & defined( NDEBUG ) )
#pragma comment( lib, "PhysX/lib/release_x86/PhysX_32.lib" )
#pragma comment( lib, "PhysX/lib/release_x86/PhysXCommon_32.lib" )
#pragma comment( lib, "PhysX/lib/release_x86/PhysXCooking_32.lib" )
#pragma comment( lib, "PhysX/lib/release_x86/PhysXExtensions_static_32.lib" )
#pragma comment( lib, "PhysX/lib/release_x86/PhysXFoundation_32.lib" )
#pragma comment( lib, "PhysX/lib/release_x86/PhysXPvdSDK_static_32.lib" )
#pragma comment( lib, "PhysX/lib/release_x86/PhysXVehicle_static_32.lib" )
#endif
The above snippet shows the code required to link the libraries for both the Release and Debug configurations for the x86 or x64 platforms respectively. Your setup may vary.
With this you should have the required Nvidia PhysX static libraries setup with your project.
Section 4. Using PhysX with your project
This is where things get fun. Setting up PhysX is simple and easy, all we need to do is make sure we start up PhysX when our program starts, run some logic to update PhysX every frame and finally have a shut down step that will close PhysX.
Step 1 : The Start up logic
First we will include the required headers for PhysX. You may include each module seperately but for this guide, I will simply include the PxPhysicsAPI.h
header to include all the PhysX files we need in our code base
//PhysX API
#include "ThirdParty/PhysX/include/PxPhysicsAPI.h"
Next we will run an initialization function that will initialize PhysX at the start of our project. This is what my startup function looks like for reference.
//------------------------------------------------------------------------------------------------------------------------------
// Variables we will need
PxDefaultAllocator m_PxAllocator;
PxDefaultErrorCallback m_PXErrorCallback;
PxFoundation* m_PxFoundation = nullptr;
PxCooking* m_PxCooking = nullptr;
PxPhysics* m_PhysX = nullptr;
PxDefaultCpuDispatcher* m_PxDispatcher = nullptr;
PxScene* m_PxScene = nullptr;
PxMaterial* m_PxDefaultMaterial = nullptr;
//PhysX Visual Debugger
PxPvd* m_Pvd = nullptr;
std::string m_pvdIPAddress = "127.0.0.1";
int m_pvdPortNumber = 5425;
uint m_pvdTimeOutSeconds = 10;
//Default values for PhysX properties to use
float m_defaultStaticFriction = 0.5f;
float m_defaultDynamicFriction = 0.5f;
float m_defaultRestitution = 0.6f;
float m_defaultDensity = 10.f;
float m_defaultAngularDamping = 0.5f;
//------------------------------------------------------------------------------------------------------------------------------
void PhysXSystem::StartUp()
{
//PhysX starts off by setting up a Physics Foundation
m_PxFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, m_PxAllocator, m_PXErrorCallback);
//Setup PhysX cooking if you need convex hull cooking support or other cooking features
m_PxCooking = PxCreateCooking(PX_PHYSICS_VERSION, *m_PxFoundation, PxCookingParams(PxTolerancesScale()));
//Create the PhysX Visual Debugger by giving it the current foundation
m_Pvd = PxCreatePvd(*m_PxFoundation);
//The PVD needs connection via a socket. It will run on the Address defined, in our case it's our machine
PxPvdTransport* transport = PxDefaultPvdSocketTransportCreate(m_pvdIPAddress.c_str(), m_pvdPortNumber, m_pvdTimeOutSeconds);
m_Pvd->connect(*transport, PxPvdInstrumentationFlag::eALL);
//Create Physics! This creates an instance of the PhysX SDK
m_PhysX = PxCreatePhysics(PX_PHYSICS_VERSION, *m_PxFoundation, PxTolerancesScale(), true, m_Pvd);
PxInitExtensions(*m_PhysX, m_Pvd);
//What is the description of this PhysX scene?
PxSceneDesc sceneDesc(m_PhysX->getTolerancesScale());
sceneDesc.gravity = PxVec3(0.0f, -9.81f, 0.0f);
//This creates CPU dispatcher threads or worker threads. We will make 2
m_PxDispatcher = PxDefaultCpuDispatcherCreate(2);
sceneDesc.cpuDispatcher = m_PxDispatcher;
sceneDesc.filterShader = PxDefaultSimulationFilterShader;
//Create the scene now by passing the scene's description
m_PxScene = m_PhysX->createScene(sceneDesc);
PxPvdSceneClient* pvdClient = m_PxScene->getScenePvdClient();
if (pvdClient)
{
//I have a PVD client, so set some flags that it needs
pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS, true);
pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONTACTS, true);
pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES, true);
}
m_PxDefaultMaterial = m_PhysX->createMaterial(m_defaultStaticFriction, m_defaultDynamicFriction, m_defaultRestitution);
}
NOTE
You will find the PhysX samples we compiled helpful here. You can find the startup logic for various different projects and how PhysX is initialized for differnet features.
Step 2 : Frame Update Logic
During each frame of our program we will need to call some functions to tell physx to update. This logic will allow PhysX to update the various parameters for it's rigidbodies and other physics objects.
To do so, we simply call 2 lines of code in our update function like so:
//------------------------------------------------------------------------------------------------------------------------------
void PhysXSystem::Update(float deltaTime)
{
m_PxScene->simulate(deltaTime);
m_PxScene->fetchResults(true);
}
The simulate
function runs a simulation for the time we pass in as a parameter. In this case we will pass the deltaTime
which is the time taken by each frame of our program.
The fetchResults
function will fetch simulation results from PhysX for all objects in our scene.
Step 3 : Shutting down PhysX
Finally when we are closing our program, we want to shut down PhysX appropriately
To do so, we will need to release the memory taken up by some PhysX system objects. To simplify our code, we can use a macro that performs the release of memeory for any object passed to it.
The macro is as follows (can be found in the sample code provided by PhysX)
#define PX_RELEASE(x) if(x) { x->release(); x = NULL; }
Now we will simply create a ShutDown function that is called when our program is closing. The function looks as follows:
//------------------------------------------------------------------------------------------------------------------------------
void PhysXSystem::ShutDown()
{
PX_RELEASE(m_PxScene);
PX_RELEASE(m_PxDispatcher);
PX_RELEASE(m_PhysX);
PX_RELEASE(m_PxCooking);
if (m_Pvd)
{
PxPvdTransport* transport = m_Pvd->getTransport();
m_Pvd->release(); m_Pvd = NULL;
PX_RELEASE(transport);
}
//Release the PhysX foundation last as other things depend on it
PX_RELEASE(m_PxFoundation);
}
NOTE
We want to release the PhysX Foundation last as other PhysX objects depend on it.
With this logic added to our program, we are successfully initializing, using and shutting down our physics simulation. Compile your program to make sure everything is working as expected. In the next post, we will discuss how to setup a simple PhysX simulation using rigidbodies in the world.# ProtoPhysX
This is a PhysX integration project using the D3D11 Engine