Home Tutorials Basic Concepts

Basic Concepts

0
1217
12 min read

 

(For more resources related to this topic, see here.)

Scene and Actors

Learn Programming & Development with a Packt Subscription

You must have heard the quote written by William Shakespeare:

“All the world’s a stage, and all the men and women merely players: they have their exits and their entrances; and one man in his time plays many parts, his acts being seven ages.”

As per my interpretation, he wanted to say that this world is like a stage, and human beings are like players or actors who perform our role in it. Every actor may have his own discrete personality and influence, but there is only one stage, with a finite area, predefined props, and lighting conditions.

In the same way, a world in PhysX is known as scene and the players performing their role are known as actors. A scene defines the property of the world in which a simulation takes place, and its characteristics are shared by all of the actors created in the scene. A good example of a scene property is gravity, which affects all of the actors being simulated in a scene. Although different actors can have different properties, independent of the scene. An instance of a scene can be created using the PxScene class.

An actor is an object that can be simulated in a PhysX scene. It can have properties, such as shape, material, transform, and so on. An actor can be further classified as a static or dynamic actor; if it is a static one, think of it as a prop or stationary object on a stage that is always in a static position, immovable by simulation; if it is dynamic, think of it as a human or any other moveable object on the stage that can have its position updated by the simulation. Dynamic actors can have properties like mass, momentum, velocity, or any other rigid body related property. An instance of static actor can be created by calling PxPhysics::createRigidStatic() function, similarly an instance of dynamic actor can be created by calling PxPhysics::createRigidDynamic() function. Both functions require single parameter of PxTransform type, which define the position and orientation of the created actor.

Materials

In PhysX, a material is the property of a physical object that defines the friction and restitution property of an actor, and is used to resolve the collision with other objects. To create a material, call PxPhysics::createMaterial(), which requires three arguments of type PxReal; these represent static friction, dynamic friction and restitution, respectively.

A typical example for creating a PhysX material is as follows:

PxMaterial* mMaterial = gPhysicsSDK->createMaterial(0.5,0.5,0.5);

Static friction represents the friction exerted on a rigid body when it is in a rest position, and its value can vary from 0 to infinity. On the other hand, dynamic friction is applicable to a rigid body only when it is moving, and its value should always be within 0 and 1. Restitution defines the bounciness of a rigid body and its value should always be between 0 and 1; the body will be more bouncy the closer its value is to 1. All of these values can be tweaked to make an object behave as bumpy as a Ping-Pong ball or as slippery as ice when it interacts with other objects.

Shapes

When we create an actor in PhysX, there are some other properties, like its shape and material, that need to be defined and used further as function parameters to create an actor. A shape in PhysX is a collision geometry that defines the collision boundaries for an actor. An actor can have more than one shape to define its collision boundary. Shapes can be created by calling PxRigidActor::createShape(), which needs at least one parameter each of type PxGeometry and PxMaterial respectively.

A typical example of creating a PhysX shape of an actor is as follows:

PxMaterial* mMaterial = gPhysicsSDK->createMaterial(0.5,0.5,0.5); PxRigidDynamic* sphere = gPhysicsSDK->createRigidDynamic(spherePos); sphere->createShape(PxSphereGeometry(0.5f), *mMaterial);

An actor of type PxRigidStatic, which represents static actors, can have shapes such as a sphere, capsule, box, convex mesh, triangular mesh, plane, or height field. Permitted shapes for actors of the PxRigidDynamic type that represents dynamic actors depends on whether the actor is flagged as kinematic or not. If the actor is flagged as kinematic, it can have all of the shapes of an actor of the PxRigidStatic type; otherwise it can have shapes such as a sphere, capsule, box, convex mesh, but not a triangle mesh, a plane, or a height field.

Creating the first PhysX 3 program

Now we have enough understanding to create our first PhysX program. In this program, we initialize PhysX SDK, create a scene, and then add two actors. The first actor will be a static plane that will act as a static ground, and the second will be a dynamic cube positioned a few units above the plane. Once the simulation starts, the cube should fall on to the plane under the effect of gravity.

Because this is our first PhysX code, to keep it simple, we will not draw any actor visually on the screen. We will just print the position of the falling cube on the console until it comes to rest.

We will start our code by including the required header files. PxPhysicsAPI.h is the main header file for PhysX, and includes the entire PhysX API in a single header. Later on, you may want to selectively include only the header files that you need, which will help to reduce the application size. We also load the three most frequently used precompiled PhysX libraries for both the Debug and Release platform configuration of VC++ 2010 Express compiler shown as follows:

In addition to the std namespace, which is a part of standard C++, we also need to add the physx namespace for PhysX, as follows:

#include <iostream> #include <PxPhysicsAPI.h> //PhysX main header file //-------Loading PhysX libraries----------] #ifdef _DEBUG #pragma comment(lib, "PhysX3DEBUG_x86.lib") #pragma comment(lib, "PhysX3CommonDEBUG_x86.lib") #pragma comment(lib, "PhysX3ExtensionsDEBUG.lib") #else #pragma comment(lib, "PhysX3_x86.lib") #pragma comment(lib, "PhysX3Common_x86.lib") #pragma comment(lib, "PhysX3Extensions.lib") #endif using namespace std; using namespace physx;

Initializing PhysX

For initializing PhysX SDK, we first need to create an object of type PxFoundation by calling the PxCreateFoundation() function. This requires three parameters: the version ID, an allocator callback, and an error callback. The first parameter prevents a mismatch between the headers and the corresponding SDK DLL(s).

The allocator callback and error callback are specific to an application, but the SDK also provides a default implementation, which is used in our program. The foundation class is needed to initialize higher-level SDKs.

The code snippet for creating a foundation of PhysX SDK is as follows:

static PxDefaultErrorCallback gDefaultErrorCallback; static PxDefaultAllocator gDefaultAllocatorCallback; static PxFoundation* gFoundation = NULL; //Creating foundation for PhysX gFoundation = PxCreateFoundation (PX_PHYSICS_VERSION, gDefaultAllocatorCallback, gDefaultErrorCallback);

After creating an instance of the foundation class, we finally create an instance of PhysX SDK by calling the PxCreatePhysics() function. This requires three parameters: the version ID, the reference of the PxFoundation object we created earlier, and PxTolerancesScale. The PxTolerancesScale parameter makes it easier to author content on different scales and still have PhysX work as expected; however, to get started, we simply pass a default object of this type. We make sure that the PhysX device is created correctly by comparing it with NULL. If the object is not equal to NULL, the device was created successfully.

The code snippet for creating an instance of PhysX SDK is as follows:

static PxPhysics* gPhysicsSDK = NULL; //Creating instance of PhysX SDK gPhysicsSDK = PxCreatePhysics (PX_PHYSICS_VERSION, *gFoundation, PxTolerancesScale() ); if(gPhysicsSDK == NULL) { cerr<<"Error creating PhysX3 device, Exiting..."<<endl; exit(1); }

Creating scene

Once the PhysX device is created, it’s time to create a PhysX scene and then add the actors to it. You can create a scene by calling PxPhysics::createScene(), which requires an instance of the PxSceneDesc class as a parameter. The object of PxSceneDesc contains the description of the properties that are required to create a scene, such as gravity.

The code snippet for creating an instance of the PhysX scene is given as follows:

PxScene* gScene = NULL; //Creating scene PxSceneDesc sceneDesc(gPhysicsSDK->getTolerancesScale()); sceneDesc.gravity = PxVec3(0.0f, -9.8f, 0.0f); sceneDesc.cpuDispatcher = PxDefaultCpuDispatcherCreate(1); sceneDesc.filterShader = PxDefaultSimulationFilterShader; gScene = gPhysicsSDK->createScene(sceneDesc);

Then, one instance of PxMaterial is created, which will be used as a parameter for creating the actors.

//Creating material PxMaterial* mMaterial = //static friction, dynamic friction, restitution gPhysicsSDK->createMaterial(0.5,0.5,0.5);

Creating actors

Now it’s time to create actors; our first actor is a plane that will act as a ground. When we create a plane in PhysX, its default orientation is vertical, like a wall, but we want it to act like a ground. So, we have to rotate it by 90 degrees so that its normal will face upwards. This can be done using the PxTransform class to position and rotate the actor in 3D world space. Because we want to position the plane at the origin, we put the first parameter of PxTransform as PxVec3(0.0f,0.0f,0.0f); this will position the plane at the origin. We also want to rotate the plane along the z-axis by 90 degrees, so we will use PxQuat(PxHalfPi,PxVec3(0.0f,0.0f,1.0f)) as the second parameter.

Now we have created a rigid static actor, but we don’t have any shape defined for it. So, we will do this by calling the createShape() function and putting PxPlaneGeometry() as the first parameter, which defines the plane shape and a reference to the mMaterial that we created before as the second parameter. Finally, we add the actor by calling PxScene::addActor and putting the reference of plane, as shown in the following code:

//1-Creating static plane PxTransform planePos = PxTransform(PxVec3(0.0f, 0, 0.0f),PxQuat(PxHalfPi, PxVec3(0.0f, 0.0f, 1.0f))); PxRigidStatic* plane = gPhysicsSDK->createRigidStatic(planePos); plane->createShape(PxPlaneGeometry(), *mMaterial); gScene->addActor(*plane);

The next actor we want to create is a dynamic actor having box geometry, situated 10 units above our static plane. A rigid dynamic actor can be created by calling the PxCreateDynamic() function, which requires five parameters of type: PxPhysics, PxTransform, PxGeometry, PxMaterial, and PxReal respectively. Because we want to place it 10 units above the origin, the first parameter of PxTransform will be PxVec3(0.0f,10.0f,0.0f). Notice that they component of the vector is 10, which will place it 10 units above the origin. Also, we want it at its default identity rotation, so we skipped the second parameter of the PxTransform class. An instance of PxBoxGeometry also needs to be created, which requires PxVec3 as a parameter, which describes the dimension of a cube in half extent. We finally add the created actor to the PhysX scene by calling PxScene::addActor() and providing the reference of gBox as the function parameter.

PxRigidDynamic*gBox); //2) Create cube PxTransform boxPos(PxVec3(0.0f, 10.0f, 0.0f)); PxBoxGeometry boxGeometry(PxVec3(0.5f,0.5f,0.5f)); gBox = PxCreateDynamic(*gPhysicsSDK, boxPos, boxGeometry, *mMaterial, 1.0f); gScene->addActor(*gBox);

Simulating PhysX

Simulating a PhysX program requires calculating the new position of all of the PhysX actors that are under the effect of Newton’s law, for the next time frame. Simulating a PhysX program requires a time value, also known as time step, which forwards the time in the PhysX world. We use the PxScene::simulate() method to advance the time in the PhysX world. Its simplest form requires one parameter of type PxReal, which represents the time in seconds, and this should always be more than 0, of else the resulting behavior will be undefined. After this, you need to call PxScene::fetchResults(), which will allow the simulation to finish and return the result. The method requires an optional Boolean parameter, and setting this to true indicates that the simulation should wait until it is completed, so that on return the results are guaranteed to be available.

//Stepping PhysX PxReal myTimestep = 1.0f/60.0f; void StepPhysX() { gScene->simulate(myTimestep); gScene->fetchResults(true); }

We will simulate our PhysX program in a loop until the dynamic actor (box) we created 10 units above the ground falls to the ground and comes to an idle state. The position of the box is printed on the console for each time step of the PhysX simulation. By observing the console, you can see that initially the position of the box is (0, 10, 0), but the y component, which represents the vertical position of the box, is decreasing under the effect of gravity during the simulation. At the end of loop, it can also be observed that the position of the box in each simulation loop is the same; this means the box has hit the ground and is now in an idle state.

//Simulate PhysX 300 times for(int i=0; i<=300; i++) { //Step PhysX simulation if(gScene) StepPhysX(); //Get current position of actor (box) and print it PxVec3 boxPos = gBox->getGlobalPose().p; cout<<"Box current Position ("<<boxPos.x <<" "<<boxPos.y <<" "<<boxPos.z<<")n"; }

Shutting down PhysX

Now that our PhysX simulation is done, we need to destroy the PhysX related objects and release the memory.

Calling the PxScene::release() method will remove all actors, particle systems, and constraint shaders from the scene. Calling PxPhysics::release() will shut down the entire physics. Soon after, you may want to call PxFoundation::release() to release the foundation object, as follows:

void ShutdownPhysX() { gScene->release(); gPhysicsSDK->release(); gFoundation->release(); }

Summary

We finally created our first PhysX program and learned its steps from start to finish. To keep our first PhysX program short and simple, we just used a console to display the actor’s position during simulation, which is not very exciting; but it was the simplest way to start with PhysX.

Resources for Article:


Further resources on this subject:


NO COMMENTS

LEAVE A REPLY

Please enter your comment!
Please enter your name here