This commit is contained in:
2025-11-28 23:13:44 +05:30
commit a3a8e79709
7360 changed files with 1156074 additions and 0 deletions

View File

@ -0,0 +1,201 @@
.. _API:
-------------
The PhysX API
-------------
============
Introduction
============
This chapter covers the basic patterns common to the PhysX application programming interface (API.) We are committed to keeping this API stable and backwards-compatible from one minor release to the next, to protect the investment you make in your integration code.
The PhysX API is composed primarily of abstract interface classes. Classes, enumerations and functions defined by the API have the prefix Px.
.. note:: There is currently one section of the public API which does not have the Px prefix: the PhysX Visual Debugger connection library which has the prefix Pvd.
The PhysX libraries also expose some classes and functions that are not part of the public API. These are primarily containers and platform abstractions that are required to build the PhysX libraries which are distributed as source, and are also used in the samples. They can be recognized because they do not have the Px prefix. Even though they are in principle accessible to users, they are largely undocumented and we do not maintain compatibility of this code between PhysX versions. For that reason we recommend strongly against their use in applications.
.. _MemoryManagement:
=================
Memory Management
=================
PhysX performs all allocations via the *PxAllocatorCallback* interface. You must implement this interface in order to initialize PhysX::
class PxAllocatorCallback
{
public:
virtual ~PxAllocatorCallback() {}
virtual void* allocate(size_t size, const char* typeName, const char* filename,
int line) = 0;
virtual void deallocate(void* ptr) = 0;
};
The size of the request is specified in bytes, and PhysX requires that the memory that is returned be 16-byte aligned. On many platforms malloc() returns memory that is 16-byte aligned, and on Windows the system function _aligned_malloc() provides this capability. The other parameters to allocate() are a string which identifies the type of allocation, and the __FILE__ and __LINE__ location inside PhysX code where the allocation was made. Refer to PxAllocatorCallback::allocate() to find out more about them.
A simple implementation of the allocator callback class can be found in the PhysX Extensions library, see class *PxDefaultAllocatorCallback*.
.. note:: On some platforms PhysX uses system library calls to determine the correct type name, and the system function that returns the type name may call the system memory allocator. If you are instrumenting system memory allocations, you may observe this behavior. To prevent PhysX requesting type names, disable allocation names using the method PxFoundation::setReportAllocationNames().
You can place PhysX objects in memory owned by the application using PhysX' binary deserialization mechanism. See :ref:`serialization` for details.
As an alternative to instrumenting the allocator, you can obtain detailed information about memory allocation in the PhysX Visual Debugger (see: :ref:`physxvisualdebugger`)
.. _ErrorReporting:
===============
Error Reporting
===============
PhysX logs all error messages through the *PxErrorCallback* interface. You must implement this interface in order to initialize PhysX::
class UserErrorCallback : public PxErrorCallback
{
public:
virtual void reportError(PxErrorCode::Enum code, const char* message, const char* file,
int line)
{
// error processing implementation
...
}
};
There is only a single function to implement, *reportError*. This function should log the passed message, or print it on the application's output console. For the more serious error codes *eABORT*, *eINVALID_PARAMETER*, *eINVALID_OPERATION*, *eINTERNAL_ERROR* and *eOUT_OF_MEMORY*, breaking into the debugger may be a more appropriate choice. Whatever you do, do not just ignore the messages.
A simple implementation of the error callback class can be found in the PhysX Extensions library, see class *PxDefaultErrorCallback*.
.. _MathTypes:
===============================
Math Classes
===============================
The common math classes used in PhysX are PxVec2, PxVec3, PxVec4, PxMat33, PxMat44, PxTransform, PxPlane and PxQuat, which are are defined in their respective header files, e.g. (SDKRoot)/Include/foundation/PxVec3.h. The types support standard operator overloads and typical math operations. Zero and identity objects where appropriate can be constructed by passing the arguments PxZero and PxIdentity respectively.
Some points to note are:
* PxTransform is a representation of a rigid body transform as a rotation quaternion and a position vector, and PhysX functions which take transforms all use this type.
* PxPlane is a homogeneous plane equation: that is, the constructor PxPlane(n, d) represents the equation n.x + d = 0.
PxMat33 and PxMat44 matrices represent transformations with basis vectors in the columns (pre-multiply with matrix on the left hand side) and are stored in column-major order. This format is layout compatible with popular graphics APIs such as OpenGL and Direct3D. For example, to set the model transformation for a rigid body in OpenGL::
// retrieve world space transform of rigid body
PxTransform t = rigidActor.getGlobalPose();
// convert to matrix form
PxMat44 m = PxMat44(t);
// set to OpenGL
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
// PxMat44::front() returns a pointer to the first matrix element
glMultMatrixf(m.front());
// draw model
glPopMatrix()
DirectX uses row-major storage for matrices by default (D3DMATRIX), but also stores basis vectors in rows (post-multiply on the right), so PxMat44 may be used in place of D3DXMATRIX types directly.
.. _UserData:
======================================================
Connecting PhysX Objects with User Application Objects
======================================================
Often an application needs to associate PhysX objects with application objects for application logic or rendering purposes. An easy way to connect a single user application object with a PhysX object is to use the *userData* member provided by the most important PhysX classes (*PxActor::userData*, *PxShape::userData*, *PxMaterial::userData*, ...). The *userData* member is a *void\** pointer which is reserved for application use. Each class only has one *userData* field, so to manage multiple associations another mechanism must be used.
============
Type Casting
============
PhysX API interface classes inherit from a top-level interface called PxBase, which provides mechanisms for type-safe down-casting between interface types. For example, to cast from a PxActor to a PxRigidDynamic, use the following idiom::
PxActor* actor = <...>
PxRigidDynamic* myActor = actor->is<PxRigidDynamic>();
const PxActor* actor = <...>
const PxRigidDynamic* myActor = actor->is<PxRigidDynamic>();
This pattern can be used to cast to intermediate types in the hierarchy such as PxRigidActor, but this is somewhat slower than casting to concrete types. In addition, PxBase provides the following capabilities:
* getConcreteType() provides an integer value which corresponds to the concrete type of an object
* getConcreteTypeName() provides a string name of the concrete type
* isKindOf() provides string-based testing of inheritance
.. _BasicReferenceCounting:
==================
Reference Counting
==================
Some PhysX objects are designed to be shared and referenced multiple times in a PhysX scene graph. For example, a PxConvexMesh may be referenced by multiple PxShape objects, each sharing the same geometry but associated with different actors. The specific types are PxTriangleMesh, PxHeightField, PxConvexMesh, PxMaterial, and PxShape. Each object of these types has a reference count. The rules for reference counting are as follows:
* when an object is created from PxPhysics, it has a reference count of 1.
* when an object's reference count reaches 0, the object is destroyed.
* when a new counted reference is created, the reference count is incremented. Counted references are as follows:
* when a PxShape references a PxConvexMesh, PxHeightfield, or PxTriangleMesh.
* when a PxShape references a PxMaterial.
* when a PxRigidActor references a PxShape.
* when a counted reference is destroyed, or the object's release() method is called, the reference count is decremented.
* when an object is created through deserialization, its reference count is 1, plus the number of counted references that exist to the object.
The initial reference count of 1 ensures the object is not destroyed until the application allows it by calling release() - thereafter it will be destroyed when no remaining counted references to it exist.
For example, if you create a shape using PxPhysics::createShape() and attach it to an actor with PxRigidActor::attachShape(), it has a reference count of 2. If you then call the shape's
release() method, it has a reference count of 1. When the actor is destroyed, or the shape is detached from the actor, the reference count is decremented, and since it is now 0, the shape is destroyed.
The acquireReference() method increments the reference count of an object. For example, when a spatial query returns a reference to a mesh shape, and you want to pass that result to another thread for deferred processing, incrementing the reference count will ensure that even if the shape referencing the mesh is released, the mesh continues to exist.
.. note:: subtypes of PxGeometry do not have counted references to the meshes to which they point, e.g. when PxConvexMeshGeometry points to a PxConvexMesh. A counted reference exists only when the geometry is within a PxShape.
.. note:: shapes are often created using the utility method PxRigidActorExt::createExclusiveShape(). Take special care when deserializing such actors (see :ref:`RigidBodyCollisionShapes` and :ref:`DeserializeReferenceCounting`)
.. _PxToleranceScale:
=====================
Using Different Units
=====================
PhysX is designed to produce correct results regardless of the units of length or mass, so long as inputs use those units consistently. However, there are certain tolerances values whose defaults need to be adjusted depending on the units. In order to ensure that these tolerances default to reasonable values, adjust the values in PxTolerancesScale when creating the PxPhysics and PxCooking interfaces. Tolerances for objects are set at creation time, and may then be overridden by the application.
You should set tolerances based on the typical size of objects in your simulation. For example, if you are working with objects of size approximately one meter, but in units of centimeters, you should set the scale as follows::
PxFoundation* foundation = ...;
PxTolerancesScale scale;
scale.length = 100; // typical length of an object
scale.speed = 981; // typical speed of an object, gravity*1s is a reasonable choice
PxPhysics *p = PxCreatePhysics(PX_PHYSICS_VERSION, *foundation, scale, ...);
This will result in the defaults for values like PxShape::contactDistance being scaled appropriately for your objects.
You can also set the typical object mass in PxTolerancesScale.
It is important to use the same PxTolerances value for initialization of PxCooking and PxPhysics, and also when creating PxSceneDesc objects.
.. _Assertions:
==========
Assertions
==========
PhysX uses the PX_DEBUG macro to enable or disable assertions. This macro is not set in the PhysXCore and PhysXCommon libraries, and so by default these libraries will not trigger assertions, however you may configure the libraries provided as source to enable them. When an assert is triggered, PhysX calls an assert handler. By default the assert handler will trigger a debug breakpoint. However, you may call the function PxSetAssertHandler() to customize the assert handler.
===========
Determinism
===========
PhysX is deterministic in the sense it will produce identical simulation results from the same sequence of API calls applied from the point where a scene is originally created (and the same responses from simulation callbacks which modify data). Note that removing all the objects from a scene is not in general sufficient to reinitialize it for this purpose.
PhysX simulation behavior is not sensitive to the number of CPU worker threads used.
An important caveat to determinism is the state of the x87 FPU on 32-bit Intel/AMD platforms. Some compilers produce x87 floating point instructions even when configured to prefer SSE instructions, and the results of those operations may depend on the state of the x87 control word. Since it is too expensive to modify the x87 FPU state at every PhysX entry point, this is delegated to the application if necessary. PhysX operations do not result in changes to the x87 control word, but certain other libraries (including DirectX) may modify it.
Configurations in which this is known to be a issue are all 32-bit MSVC debug configurations, and all MSVC 32-bit checked, release and profile configurations prior to Visual Studio 2012.

View File

@ -0,0 +1,393 @@
.. _AdvancedCollisionDetection:
--------------------------------
Advanced Collision Detection
--------------------------------
.. _ShapeCollisionTuning:
=================================
Tuning Shape Collision Behavior
=================================
Shapes used for contact generation influence the motion of the dynamic rigid bodies they are attached to through contact points. The constraint solver generates impulsive forces at the contact points to keep the shapes resting or moving without passing through each other. Shapes have two important parameters that control how collision detection generates contact points between them, which in turn are central for their behavior when colliding or stacking: contactOffset and restOffset. They are set using PxShape::setContactOffset() and PxShape::setRestOffset() respectively. Neither of these values is used directly. Collision detection always operates on a pair of potentially colliding shapes, and it always considers the sum of the offsets of the two shapes. We call these the contactDistance and restDistance respectively.
Collision detection is able to generate contact points between two shapes when they are still a distance apart, when they are exactly touching, or when they are inter-penetrating. To make the discussion simpler, we treat interpenetration as a negative distance. So the distance between two shapes can be positive, zero, or negative. SeparationDistance is the distance at which collision detection will start to generate contacts. It has to be greater than zero, meaning that PhysX will always generate contacts when two shapes are penetrating (unless collision detection between the two shapes is in some way completely disabled, such as with filtering). By default, when using metric units and default scaling in PxTolerancesScale, contactOffset is 0.02, which means contactDistance will work out to 4 centimeters. So when two shapes approach each other within 4 centimeters, contacts will be generated until they are again moved further apart than 4 centimeters.
The generation of contact points does not however mean that a large impulse will immediately be applied at these locations to separate the shapes, or to even prevent further motion in the direction of penetration. This would make the simulation jitter unless the simulation time step is selected to be tiny, which is not desirable for real time performance. Instead, we want the force at the contact to smoothly increase as penetration increases until it reaches a value sufficiently high to stop any further penetrating motion. The distance at which this maximum force is reached is the restDistance, because at this distance two shapes stacked on each other will reach static equilibrium and come to rest. When the shapes are for some reason pushed together so much that they have a distance below restDistacnce, an even greater force is applied to push them apart until they are at restDistance again. The variation of force applied as the distance changes is not necessarily linear, but it is smooth and continuous which results in a pleasing simulation even at large time steps.
There are a few different things to consider when choosing contactOffset and restOffset for shapes. Typically the same values can be used for all shapes in a simulation. It makes sense to determine restOffset first. The goal is typically to have the graphics shapes appear to stack such that they are exactly touching, like bodies do in real life. If the collision shapes are sized to be the exact same size as the graphics shapes, a restOffset of zero is needed. If the collision shapes are an epsilon bigger than the graphics shapes, a restOffset of negative epsilon is correct. This will let the larger collision shapes sink into each other until the smaller graphics shapes touch too. restOffsets that are larger than zero are practical for example if there are problems with sliding on triangle geometry where the penetration based contact generation has more trouble producing smooth contact points than a separation one, resulting in a smoother slide.
Once the restOffset is determined, the contactOffset should be chosen to be a value a slightly larger. The rule of thumb is to make the difference between the two as small as possible that still effectively avoids jitter at the time step size the simulation uses. A larger time step will need the difference to be larger. The drawback of setting it too large is that contacts will be generated sooner as two shapes approach, which drives up the total number of contacts that the simulation has to worry about. This will decrease in performance. Also, the simulation code often makes the assumption that contact points are close to the convex shapes' surface. If the contact offset is very large this assumption breaks down which could lead to behavior artefacts.
.. _Contact Modification:
===============================
Contact Modification
===============================
Under certain circumstances, it may be necessary to specialize contact behavior. For example to implement sticky contacts, give objects the appearance of floating or swimming inside each other, or making objects go through apparent holes in walls. A simple approach to achieve such effects is to let the user change the properties of contacts after they have been generated by collision detection, but before the contact solver. Because both of these steps occur within the scene simulate() function, a callback must be used.
The callback occurs for all pairs of colliding shapes for which the user has specified the pair flag PxPairFlag::eMODIFY_CONTACTS in the filter shader.
To listen to these modify callbacks, derive from the class PxContactModifyCallback::
class MyContactModification : public PxContactModifyCallback
{
...
void onContactModify(PxContactModifyPair* const pairs, PxU32 count);
};
And then implement the function onContactModify of PxContactModifyCallback::
void MyContactModification::onContactModify(PxContactModifyPair *const pairs, PxU32 count)
{
for(PxU32 i=0; i<count; i++)
{
...
}
}
Every pair of shapes comes with an array of contact points, that have a number of properties that can be modified, such as position, contact normal, and separation. For the time being, restitution and friction properties of the contacts cannot be modified. See PxModifiableContact and PxContactSet for properties that can be modified.
In addition to modifying contact properties, it is possible to:
* Set target velocities for each contact
* Limit the maximum impulse applied at each contact
* Adjust inverse mass and inverse inertia scales separately for each body
Conveyor belt-like effects can be achieved by setting target velocities. Best results are achieved by having target velocities running in tangential directions to the contact normal but the solver does also support target velocities in the direction of the contact normal.
The user can limit the impulse applied at each contact by limiting the maximum impulse applied at each contact. This can be useful to produce "soft" contact effects, e.g. to give the impression of energy dissipation due to compression or to limit the impulse applied on a dynamic body due to a kinematic collision. Note that limiting the maximum impulse can potentially lead to additional penetration and bodies passing through each-other.
Adjusting mass and inertia scales can be used to tune how contacts between a pair of bodies affect the bodies' linear and angular velocities respectively. Each body in the contact pair has a separate inverse mass and inverse inertia scale. These scales are initialized to 1 and can be adjusted as part of the callback. Note that these values perform local mass modification within the contact pair and affect all contacts within the pair.
Uniformly scaling a body's inverse mass and inverse inertia by the same value results in the body behaving like a body that is either heavier or lighter depending on the values used. Providing inverse mass/inverse inertia scales < 1 results in the body appearing heavier; providing scales > 1 result in the body appearing lighter. For example, inverse mass/inertia scales of 0.5 result in the body appearing to have double the mass. Providing inverse mass/inertia scales of 4 would result in the body appearing to have a quarter of its original mass. Providing inverse mass/inertia scale of 0 results in the body behaving as if it has infinite mass.
However, it is also possible to non-uniform scale a body's inverse mass and inverse inertia by providing different values to a body's inverse mass and inverse inertia scale. For example, it is possible to reduce or increase the amount of angular velocity change as a result of contacts by adjusting just the inverse inertia scale. The use-cases for this kind of modification are extremely application-dependent but may involve, for example, tuning interactions between a player's vehicle and traffic vehicles in an arcade-style driving game, where the player's car is expected to be bumped by traffic vehicles but where it would be extremely frustrating to the player if the car was to spin-out as a result of the collision. This could also be achieved by making the traffic vehicles much lighter than the player's vehicle but this may make the traffic vehicles appear "too light" and therefore damage the player's immersion.
When performing local mass modification, the impulse reported in PxSimulationEventCallback::onContact() will be relative to the locally scaled masses of the bodies involved in that contact. Therefore, this reported impulse may no longer accurately reflect the change in momentum caused by a given contact. In order to resolve this issue, we have provided the following methods in the rigid body extensions to extract the linear and angular impulse and velocity change caused by a contact using local mass modification::
static void computeLinearAngularImpulse(const PxRigidBody& body, const PxTransform& globalPose,
const PxVec3& point, const PxVec3& impulse, const PxReal invMassScale,
const PxReal invInertiaScale, PxVec3& linearImpulse, PxVec3& angularImpulse);
static void computeVelocityDeltaFromImpulse(const PxRigidBody& body,
const PxTransform& globalPose, const PxVec3& point, const PxVec3& impulse,
const PxReal invMassScale, const PxReal invInertiaScale, PxVec3& deltaLinearVelocity,
PxVec3& deltaAngularVelocity);
These methods return separate linear and angular impulse and velocity change values to reflect the fact that the mass and inertia may have been non-uniformly scaled. When local mass modification has been used, it may be necessary to extract separate linear and angular impulses for each contact point, for each body in the pair. Please note that these helper functions are provided to provide users with accurate impulse values and are by no means mandatory. For simple use-cases, e.g. triggering effects or damage based on impulse thresholds, the single impulse value reported by the contact report should be perfectly acceptable even when local mass modification has been used. However, if local mass modification has been used and the impulse values are being used for more complex behaviors, e.g. balance control for a ragdoll, then these helper functions will most-likely be required to achieve correct behavior. Please note that, in the case of articulations, computeLinearAngularImpulse will return the correct impulse applied on respective articulation link. However, computeVelocityDeltaFromImpulse will not return the correct velocity changes for an articulation link because it does not take the effect of any other links of the articulation into account.
In addition, the following considerations must be made when using local mass modification:
* Force thresholding for callbacks will be based on the scalar impulse value in contact reports. This was calculated using the scaled mass/inertias of the bodies so using mass scaling may require these thresholds to be re-tuned.
* Maximum impulse clamping occurs in the solver on an impulse value operating on the scaled masses/inertias. As a result, the magnitude of applied impulses calculated from computeLinearAngularImpulse(...) may exceed the maxImpulse in situations where mass scaling was used. In situations where uniform mass scaling was used, the magnitude of the magnitude of linear impulse will not exceed massScale * maxImpulse and angular impulse will not exceed inertiaScale * maxImpulse.
There are a couple of special requirements for the callback due to the fact that it is coming from deep inside the SDK. In particular, the callback should be thread safe and reentrant. In other words, the SDK may call onContactModify() from any thread and it may be called concurrently (i.e., asked to process sets of contact modification pairs simultaneously).
The contact modification callback can be set using the contactModifyCallback member of PxSceneDesc or the setContactModifyCallback() method of PxScene.
===============================
Contact reporting
===============================
Here is an example for a contact event function from SampleSubmarine::
void SampleSubmarine::onContact(const PxContactPairHeader& pairHeader,
const PxContactPair* pairs, PxU32 nbPairs)
{
for(PxU32 i=0; i < nbPairs; i++)
{
const PxContactPair& cp = pairs[i];
if(cp.events & PxPairFlag::eNOTIFY_TOUCH_FOUND)
{
if((pairHeader.actors[0] == mSubmarineActor) ||
(pairHeader.actors[1] == mSubmarineActor))
{
PxActor* otherActor = (mSubmarineActor == pairHeader.actors[0]) ?
pairHeader.actors[1] : pairHeader.actors[0];
Seamine* mine = reinterpret_cast<Seamine*>(otherActor->userData);
// insert only once
if(std::find(mMinesToExplode.begin(), mMinesToExplode.end(), mine) ==
mMinesToExplode.end())
mMinesToExplode.push_back(mine);
break;
}
}
}
}
SampleSubmarine is a subclass of PxSimulationEventCallback. onContact receives the pair for which the requested contact events have been triggered. The above function is only interested in eNOTIFY_TOUCH_FOUND events, which are raised whenever two shapes start to touch. In fact it is only interested in touch events of the submarine -- which is checked in the second if-statement. It then goes on to assume that the second actor is a mine (which works in this example because the sample is configured such that no other contact reports will get sent when a submarine actor is involved). After that, it adds the mine to a set of mines that should explode during the next update.
.. note:: By default collisions between kinematic rigid bodies and kinematic and static rigid bodies will not get reported. To enable these reports use the PxSceneDesc::kineKineFilteringMode and PxSceneDesc::staticKineFilteringMode parameters when creating a scene.
Frequently, users are only interested in contact reports, if the force of impact is larger than a certain threshold. This allows to reduce the amount of reported pairs which need to get processed. To take advantage of this option the following additional configurations are necessary:
* Use PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND, ::eNOTIFY_THRESHOLD_FORCE_PERSISTS, ::eNOTIFY_THRESHOLD_FORCE_LOST instead of ::eNOTIFY_TOUCH_FOUND etc.
* Specify the threshold force for a dynamic rigid body through PxRigidDynamic::setContactReportThreshold(). If the body collides with an other object and the contact force is above the threshold, a report will get sent (if enabled according to the PxPairFlag setting of the pair). If two colliding dynamic bodies both have a force threshold specified then the lower threshold will be used.
.. note:: If a dynamic rigid body collides with multiple static objects, then the impact force of all those contacts will get summed up and used to compare against the force threshold. In other words, even if the impact force against each individual static object is below the threshold, the contact reports will still get sent for each pair if the sum of those forces exceeds the threshold.
Contact Reports and CCD
========================
If continuous collision detection (CCD) with multiple passes is enabled, then a fast moving object might bounce on and off the same object multiple times during a single simulation step. By default, only the first impact will get reported as a *eNOTIFY_TOUCH_FOUND* event in this case. To get events for the other impacts too, the *PxPairFlag* *eNOTIFY_TOUCH_CCD* has to be raised for the collision pair. This will trigger *eNOTIFY_TOUCH_CCD* events for the non primary impacts. For performance reasons, the system can not always tell whether the contact pair lost touch in one of the previous CCD passes and thus can also not always tell whether the contact is new or has persisted. *eNOTIFY_TOUCH_CCD* just reports when the two collision objects were detected as being in contact during a CCD pass.
===============================
Extracting Contact information
===============================
The onContact simulation event permits read-only access to all contact points for a given PxContactPair. In previous releases, these were available as a flattened array of PxContactPoint objects. However, PhysX 3.3 introduces a new format for this data: the compressed contact stream. The contact information is now compressed into an appropriate format for a given PxContactPair depending on certain properties, e.g. depending on the shapes involved, the properties of the contacts, materials and whether the contacts are modifiable.
As there are a large number of combinations of different formats, the user is provided with two built-in mechanisms to access the contact data. The first approach provides a mechanism to extract contacts from a user buffer and can be used as below::
void MySimulationCallback::onContact(const PxContactPairHeader& pairHeader,
const PxContactPair* pairs, PxU32 nbPairs)
{
const PxU32 bufferSize = 64;
PxContactPairPoint contacts[bufferSize];
for(PxU32 i=0; i < nbPairs; i++)
{
const PxContactPair& cp = pairs[i];
PxU32 nbContacts = pairs[i].extractContacts(contacts, bufferSize);
for(PxU32 j=0; j < nbContacts; j++)
{
PxVec3 point = contacts[j].position;
PxVec3 impulse = contacts[j].impulse;
PxU32 internalFaceIndex0 = contacts[j].internalFaceIndex0;
PxU32 internalFaceIndex1 = contacts[j].internalFaceIndex1;
//...
}
}
}
This approach requires copying data to a temporary buffer in order to access it. The second approach allows the user to iterate over the contact information without extracting their own copy::
void MySimulationCallback::onContact(const PxContactPairHeader& pairHeader,
const PxContactPair* pairs, PxU32 nbPairs)
{
for(PxU32 i=0; i < nbPairs; i++)
{
const PxContactPair& cp = pairs[i];
PxContactStreamIterator iter(cp.contactPatches, cp.contactPoints, cp.getInternalFaceIndices(), cp.patchCount, cp.contactCount);
const PxReal* impulses = cp.contactImpulses;
PxU32 flippedContacts = (cp.flags & PxContactPairFlag::eINTERNAL_CONTACTS_ARE_FLIPPED);
PxU32 hasImpulses = (cp.flags & PxContactPairFlag::eINTERNAL_HAS_IMPULSES);
PxU32 nbContacts = 0;
while(iter.hasNextPatch())
{
iter.nextPatch();
while(iter.hasNextContact())
{
iter.nextContact();
PxVec3 point = iter.getContactPoint();
PxVec3 impulse = hasImpulses ? dst.normal * impulses[nbContacts] : PxVec3(0.f);
PxU32 internalFaceIndex0 = flippedContacts ?
iter.getFaceIndex1() : iter.getFaceIndex0();
PxU32 internalFaceIndex1 = flippedContacts ?
iter.getFaceIndex0() : iter.getFaceIndex1();
//...
nbContacts++;
}
}
}
}
This approach is slightly more involved because it requires the user to not only iterate over all of the data but also consider conditions like whether the pair has been flipped or whether impulses have been reported with the pair. However, this approach of iterating over the data in-place may be more efficient because it doesn't require copying data.
Extra Contact Data
===================
Since pointers to the actors of a contact pair are provided in contact reports, actor properties can be read directly within the callback. However, the pose and the velocity of an actor usually refer to the time of impact. If for some reasons the velocity after collision response is of interest, then the actor can not provide that information. Similarly, it is not possible to get the actor velocity or the pose at impact if those properties were changed by the user while the simulation was running (in such a case the newly set property values will be returned). Last but not least, if CCD with multiple passes is enabled, then a fast moving object might bounce on and off the same object multiple times. The object poses and velocities for each such impact can not get extracted from the actor pointers in the callback. For these scenarios, the PhysX SDK provides an additional contact stream that can hold all sorts of extra information related to the contact pair. This extra information is requested per pair through the pair flags *PxPairFlags* (see the API documentation of *PxPairFlag::ePRE_SOLVER_VELOCITY*, *::ePOST_SOLVER_VELOCITY*, *::eCONTACT_EVENT_POSE* for details). If requested, the extra data stream will be available as a member of the *PxContactPairHeader* structure. The stream can then be parsed by using the predefined iterator *PxContactPairExtraDataIterator* or by some custom parsing code (see the implementation of *PxContactPairExtraDataIterator* for details about the format of the stream).
Example code::
void MySimulationCallback::onContact(const PxContactPairHeader& pairHeader,
const PxContactPair* pairs, PxU32 nbPairs)
{
PxContactPairExtraDataIterator iter(pairHeader.extraDataStream,
pairHeader.extraDataStreamSize);
while(iter.nextItemSet())
{
if (iter.postSolverVelocity)
{
PxVec3 linearVelocityActor0 = iter.postSolverVelocity->linearVelocity[0];
PxVec3 linearVelocityActor1 = iter.postSolverVelocity->linearVelocity[1];
...
}
}
}
.. _Continuous Collision Detection:
===============================
Continuous Collision Detection
===============================
When continuous collision detection (or CCD) is turned on, the affected rigid bodies will not go through other objects at high velocities (a problem also known as tunnelling). To enable CCD, three things need to be happen:
1) CCD needs to be turned on at scene level::
PxPhysics* physx;
...
PxSceneDesc desc;
desc.flags |= PxSceneFlag::eENABLE_CCD;
...
2) Pairwise CCD needs to be enabled in the pair filter::
static PxFilterFlags filterShader(
PxFilterObjectAttributes attributes0,
PxFilterData filterData0,
PxFilterObjectAttributes attributes1,
PxFilterData filterData1,
PxPairFlags& pairFlags,
const void* constantBlock,
PxU32 constantBlockSize)
{
pairFlags = PxPairFlag::eSOLVE_CONTACT;
pairFlags |= PxPairFlag::eDETECT_DISCRETE_CONTACT;
pairFlags |= PxPairFlag::eDETECT_CCD_CONTACT;
return PxFilterFlags();
}
...
desc.filterShader = testCCDFilterShader;
physx->createScene(desc);
3) CCD need to be enabled for each PxRigidBody that requires CCD::
PxRigidBody* body;
...
body->setRigidBodyFlag(PxRigidBodyFlag::eENABLE_CCD, true);
Once enabled, CCD only activates between shapes whose relative speeds are above the sum of their respective CCD velocity thresholds. These velocity thresholds are automatically calculated based on the shape's properties and support non-uniform scales.
Contact Notification and Modification
=====================================
CCD supports the full set of contact notification events that are supported with the discrete collision detection. For details on contact notification, see the documentation for Callbacks.
CCD supports contact modification. To listen to these modify callbacks, derive from the class PxCCDContactModifyCallback::
class MyCCDContactModification : public PxCCDContactModifyCallback
{
...
void onCCDContactModify(PxContactModifyPair* const pairs, PxU32 count);
};
And then implement the function onContactModify of PxContactModifyCallback::
void MyContactModification::onContactModify(PxContactModifyPair *const pairs, PxU32 count)
{
for(PxU32 i=0; i<count; i++)
{
...
}
}
This onContactModify callback operates using the same semantics as the discrete collision detection contact modification callbacks. For further details, please refer to the documentation on Callbacks.
As with discrete collision detection, CCD will only emit contact modification events for a given pair if the user has specified the pair flag PxPairFlag::eMODIFY_CONTACTS in the filter shader.
Triggers
========
Currently, shapes flagged with PxShapeFlag::eTRIGGER_SHAPE will not be included in CCD. However, it is possible to get trigger events from CCD by not flagging trigger shapes as PxShapeFlag::eTRIGGER_SHAPE and instead configuring the filter shaders to return the following state for pairs involving trigger shapes:
pairFlags = PxPairFlag::eTRIGGER_DEFAULT | PxPairFlag::eDETECT_CCD_CONTACT;
return PxFilterFlag::eDEFAULT;
It should be noted that not flagging shapes as PxShapeFlag::eTRIGGER_SHAPE can result in the triggers being more expensive. Therefore, this workaround should be reserved for use only in situations where important trigger events will be missed without CCD.
Tuning CCD
==========
The CCD should generally work without any tuning. However, there are 4 properties that can be adjusted:
1) PxSceneDesc.ccdMaxPasses: This variable controls the number of CCD passes we perform. This is defaulted to 1, meaning that all objects are attempted to be updated to the TOI of their first contact. Any remaining time after the TOI of their first contact will be dropped. Increasing this value permits the CCD to run multiple passes. This reduces the likelihood of time being dropped but can increase the cost of the CCD.
2) PxRigidBody::setMinCCDAdvanceCoefficient(PxReal advanceCoefficient): This method allows you to adjust the amount by which the CCD advances objects in a given pass. By default, this value is 0.15, meaning that CCD will advance an object by the 0.15 * ccdThreshold, where ccdThreshold is a value computed per-shape that acts as a lower-bound of the maximum amount of time that could be consumed that before there is a chance that the object could have tunnelled. The default value of 0.15 improves the fluidity of motion without risking missed collisions. Reducing this value can negatively impact fluidity but will reduce the likelihood of objects clipping at the end of a frame. Increasing this value may increase the likelihood of objects tunnelling. This value should only be set in the range [0,1].
3) Enabling the flag PxSceneFlag::eDISABLE_CCD_RESWEEP on the PxSceneDesc.flags: Enabling this flag disables CCD resweeps. This can result in missed collisions as the result of ricochets but has the potential to reduce the overhead of the CCD. In general, enabling this advancement mode still guarantees that objects will not pass through the static environment but no longer guarantees that dynamic objects with CCD enabled will not pass through each-other.
4) PxRigidBody::setRigidBodyFlag(PxRigidBodyFlag::eENABLE_CCD_FRICTION, true): Enabling this flag enables the application of friction forces in the CCD. This is disabled by default. As the CCD operates using only linear motion, enabling friction inside CCD can cause visual artefacts.
Performance Implications
========================
Enabling CCD on a scene/all bodies in a scene should be relatively efficient but it will have some performance impact even when all the objects in the scene are moving relatively slowly. A great deal of effort has been put into optimizing the CCD and as a result, this additional overhead should only constitute a very small portion of the overall simulation time when the objects are moving slowly. As the objects' velocities increase, the CCD overhead will increase, especially if there are a lot of high-speed objects in close proximity. Increasing the number of CCD passes can make the CCD more expensive although the CCD will terminate early if the additional passes aren't required.
Limitations
===========
The CCD system is a best-effort conservative advancement scheme. It runs a finite number of CCD substeps (defaulted to 1) and drops any remaining time. Usually, time is only dropped on high-speed objects at the moment of impact so it is not noticeable. However, this artefact can become noticeable if you simulate an object that is sufficiently small/thin relative to the simulation time-step that the object could tunnel if it was accelerated by gravity from rest for 1 frame, i.e. a paper-thin rigid body. Such an object would always be moving at above its CCD velocity threshold and could result in a large proportion of simulation time being dropped for that object and any objects in the same island as it (any objects whose bounds overlap the bounds of that object). This could cause a noticeable slow-down/stuttering effect caused by the objects in that island becoming noticeably out-of-sync with the rest of the simulation. It is therefore recommended that paper-thin/tiny objects should be avoided if possible.
It is also recommended that you filter away CCD interactions between bodies that are constrained together, e.g. limbs in the same ragdoll. Allowing CCD interactions between limbs of the same ragdoll could increase the cost of CCD and also potentially cause time to be dropped unnecessarily. CCD interactions are automatically disabled between links in an articulation.
============
Raycast CCD
============
The PhysX SDK supports an alternative CCD implementation based on simple raycasts. This "raycast CCD" algorithm is available in PhysX Extensions, and it is demonstrated in a snippet ("SnippetRaycastCCD"). Contrary to the built-in CCD algorithm implemented within the PhysX SDK, this cheaper and simpler alternative version is fully implemented outside of the SDK itself.
After the traditional simulate/fetchResults calls, the system performs raycasts from the shapes' center positions to double-check that they did not tunnel. If tunnelling is detected for an object, it is moved back to a previous position along the ray, in an overlap position. Then next frame, the SDK's contact generation takes over and generates a convincing motion. There are some subtle details not described here, but this is how it works in a nutshell.
Since it is raycast-based, the solution is not perfect. In particular, small dynamic objects can still go through the static world if the ray goes through a crack between edges, or a small hole in the world (like the keyhole from a door). Also, dynamic-vs-dynamic CCD is very approximate. It only works well for fast-moving dynamic objects colliding against slow-moving dynamic objects. Other known limitations are that it is currently only implemented for PxRigidDynamic objects (not for PxArticulationLink), and for simple actors with one shape (not for "compounds").
However the implementation should be able to prevent important objects from leaving the world, provided the world is watertight. The code is very small, easy to follow or modify, and its performance is often better overall than for the built-in CCD. So it can be a valuable alternative if the default CCD becomes too expensive.
====================================================
Speculative CCD
====================================================
In addition to sweep-based CCD, PhysX also provides a cheaper but less robust approach called speculative CCD. This approach functions differently to the sweep-based CCD in that it operates entirely as part of the discrete simulation by inflating contact offsets based on object motion and depending on the constraint solver to ensure that objects do not tunnel through each-other.
This approach generally works well and, unlike the sweep-based CCD, it is legal to enable speculative CCD on kinematic actors. However, there are cases where it can fail to ensure objects do not pass through each-other. As an example, if the constraint solver accelerates an actor (as a result of a collision or joint) such that the actor passes entirely through objects during that time-step, speculative CCD can result in tunneling.
To enable this feature, raise PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD on the rigid body that requires CCD::
PxRigidBody* body;
...
body->setRigidBodyFlag(PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD, true);
Unlike the sweep-based CCD, this form of CCD does not require settings to be raised on either the scene or on the pair in the filter shader.
Note that this approach works best with PCM collision detection. It may not function as well if the legacy SAT-based collision detection approach is used.
This feature can work in conjunction with the sweep-based CCD, e.g. if a fast-moving kinematic has speculative CCD enabled but dynamic rigid bodies use sweep-based CCD. However, if speculative CCD is used on kinematics in conjunction with sweep-based CCD, it is important to ensure that interactions between the kinematic actor using speculative contacts and the CCD-enabled dynamic actors do not also enable sweep-based CCD interactions otherwise the sweep-based CCD may overrule the speculative CCD, leading to poor behavior.
====================================================
Persistent Contact Manifold (PCM)
====================================================
The PhysX SDK provides two types of collision detection:
(1) Default collision detection
The default collision detection system uses a mixture of SAT (Separating Axis Theorem) and distance-based collision detection to generate full contact manifolds. It generates all the potential contacts in one frame, so it lends itself better to stable stacking. This approach is stable for small contact offsets and rest offsets but may not generate the correct contact points when large offsets are used because it approximates the contact points in these situations by plane shifting.
(2) Persistent Contact Manifold (PCM)
PCM is a fully distance-based collision detection system. PCM generates a full manifold of contacts when two shapes first come into contact. It recycles and updates the existing contacts from the previous frame in the manifold and then it generates a new contact in the subsequent frame if the shapes move relative to each-other more than a threshold amount or if a contact was dropped from the manifold. If too many contacts are dropped from the manifold due to a large amount of relative motion in a frame, then full manifold generation is re-run. This approach is quite efficient in terms of performance and memory. However, because PCM potentially generates fewer contacts than the default collision detection, it might reduce stacking stability when simulating tall stacks with insufficient solver iterations. As this approach is distance-based, it will generate the correct contact points for arbitrary contact offsets/rest offsets.
To enable PCM, set the flag in the PxSceneDesc::flags::
PxSceneDesc sceneDesc;
sceneDesc.flags |= PxSceneFlag::eENABLE_PCM;

View File

@ -0,0 +1,373 @@
.. _Articulations:
--------------
Articulations
--------------
An articulation is a single actor comprising a set of links (each of which behaves like a rigid body) connected together with special joints. Every articulation has a tree-like structure - so there can be no loops or breaks. Their primary use is modelling physically actuated characters. They support higher mass ratios, more accurate drive models, have better dynamic stability and a more robust recovery from joint separation than standard PhysX joints. However, they are considerably more expensive to simulate.
Although articulations do not directly build on joints, they use very similar configuration mechanisms. In this section we assume familiarity with PhysX joints.
----------------------------------------------
Maximal Coordinate and Reduced Articulations
----------------------------------------------
PhysX provides two articulation implementations: maximal and reduced/generalized coordinates.
The Maximal coordinate articulation implementation is designed for game use-cases, specifically powered ragdoll simulations. As such, it supports only spherical joints with limits. This articulation implementation makes some accuracy sacrifices in favor of robustness.
The reduced coordinate articulation implementation is designed for robotics use-cases. It supports revolute, prismatic, fixed and spherical joints. It was written to favor accurate simulation and produces results that are very close to analytic models of kinematic chains. In addition, it provides inverse dynamics functionality.
----------------------------------
Maximal Coordinate Articulations
----------------------------------
Creating an Articulation
========================
To create an articulation, first create the articulation actor without links::
PxArticulation* articulation = physics.createArticulation();
Then add links one by one, each time specifying a parent link (NULL for the parent of the initial link), and the pose of the new link::
PxArticulationLink* link = articulation->createLink(parent, linkPose);
PxRigidActorExt::createExclusiveShape(*link, linkGeometry, material);
PxRigidBodyExt::updateMassAndInertia(*link, 1.0f);
Articulation links have a restricted subset of the functionality of rigid bodies. They may not be kinematic, and they do not support damping, velocity clamping, or contact force thresholds. Sleep state and solver iteration counts are properties of the entire articulation rather than the individual links.
Each time a link is created beyond the first, a *PxArticulationJoint* is created between it and its parent. Specify the joint frames for each joint, in exactly the same way as for a PxJoint::
PxArticulationJoint* joint = static_cast<PxArticulationJoint*>(link->getInboundJoint());
joint->setParentPose(parentAttachment);
joint->setChildPose(childAttachment);
Finally, add the articulation to the scene::
scene.addArticulation(articulation);
Articulation Joints
===================
The only form of articulation joint currently supported is an anatomical joint, whose properties are similar to D6 joint configured for a typical rag doll (see :ref:`PxD6Joint`). Specifically, the joint is a spherical joint, with angular drive, a twist limit around the child joint frame's x-axis, and an elliptical swing cone limit around the parent joint frame's x-axis. The configuration of these properties is very similar to a D6 or spherical joint, but the options provided are slightly different.
The swing limit is a hard elliptical cone limit which does not support spring or restitution from movement perpendicular to the limit surface. You can set the limit ellipse angle as follows::
joint->setSwingLimit(yAngle, zAngle);
for the limit angles around y and z. Unlike the PxJoint cone limit the limit provides a tangential spring to limit movement of the axis along the limit surface. Once configured, enable the swing limit::
joint->setSwingLimitEnabled(true);
The twist limit allows configuration of upper and lower angles::
joint->setTwistLimit(lower, upper);
and again you must explicitly enable it::
joint->setTwistLimitEnabled(true);
As usual with joint limits, it is good practice to use a sufficient limit contactDistance value that the solver will start to enforce the limit before the limit threshold is exceeded.
Articulation joints are not breakable, and it is not possible to retrieve the constraint force applied at the joint.
Driving an Articulation
=======================
Articulations are driven through joint acceleration springs. You can set an orientation target, an angular velocity target, and spring and damping parameters that control how strongly the joint drives towards the target. You can also set compliance values, indicating how strongly a joint resists acceleration. A compliance near zero indicates very strong resistance, and a compliance of 1 indicates no resistance.
Articulations are driven in two phases. First the joint spring forces are applied (we use the term *internal* forces for these) and then any *external* forces such as gravity and contact forces. You may supply different compliance values at each joint for each phase.
Note that with joint acceleration springs, the required strength of the spring is estimated using just the mass of the two bodies connected by the joint. By contrast, articulation drive springs account for the masses of all the bodies in the articulation, and any stiffness from actuation at other joints. This estimation is an iterative process, controlled using the *externalDriveIterations* and *internalDriveIterations* properties of the PxArticulation class.
Instead of setting the target quaternion for the joint drive, it is possible to set the orientation error term directly as a rotation vector. The value is set as the imaginary part of the target quaternion, with the real part set to 0.
joint->setDriveType(PxArticulationJointDriveType::eERROR);
joint->setTargetOrientation(PxQuat(error.x, error.y, error.z, 0));
This allows the spring to be driven with a larger positional error than can be generated by the difference between 2 quaternions. Obtain the same behavior as with target quaternions by computing the error from the target quaternion, link frames, and joint frames as follows::
PxTransform cA2w = parentPose.transform(joint.parentPose); // parent attachment frame
PxTransform cB2w = childPose.transform(joint.childPose); // child attachment frame
transforms.cB2cA = transforms.cA2w.transformInv(transforms.cB2w); // relative transform
if(transforms.cB2cA.q.w<0) // shortest path path
transforms.cB2cA.q = -transforms.cB2cA.q;
// rotation vector from relative transform to drive pose
PxVec3 error = log(j.targetPosition * cB2cA.q.getConjugate());
Articulation Projection
=======================
When any of the joints in an articulation separate beyond a specified threshold, the articulation is projected back together automatically. Projection is an iterative process, and the PxArticulation functions *PxArticulation::setSeparationTolerance()* and *PxArticulation::setMaxProjectionIterations()* control when projection occurs and trade cost for robustness.
Articulations and Sleeping
==========================
Like rigid dynamic objects, articulations are also put into a sleep state if their energy falls below a certain threshold for a period of time. In general, all the points in the section :ref:`sleeping` apply to articulations as well. The main difference is that articulations can only go to sleep if each individual articulation link fulfills the sleep criteria.
----------------------------------
Reduced Coordinate Articulations
----------------------------------
The PhysX reduced coordinate articulation provide a wide range of functionality required for the robotics simulation and other simulations that require accurate simulations of mechanical structures. The key difference between reduced coordinates and maximal coordinate articulations is that reduced coordinate articulations are guaranteed to exhibit no joint error.
The simulation may also be suitable for use in games to simulate humanoids. However, the introducing of velocity clamps or damping may be required to ensure stability of the simulation at large angular velocities, which would not be required when using the maximal coordinate articulations. The reason for this is the explicit integration of coriolis and centrifugal forces combined with the lack of automatic joint or body damping.
This technique has different performance properties to the maximal coordinate articulations or rigid bodies and joints. In a simulation without a significant number of contacts affecting the articulation, the simulation cost is generally proportional to the number of degrees of freedom, rather than the number of links. Therefore, in common robotics applications, where most joints have 0-1 degrees of freedom, the cost of simulating using reduced coordinate articulations is often lower than the cost of simulating the same robot using either maximal coordinate articulations or even rigid bodies and joints.
Creating a Reduced Coordinate Articulation
==========================================
The process of creating a reduced coordinate articulation is very similar to the process of creating a maximal articulation. To create an articulation, first create the articulation actor without links::
PxArticulationReducedCoordinate* articulation = physics.createReducedCoordinateArticulation();
Then add links one by one, each time specifying a parent link (NULL for the parent of the initial link), and the pose of the new link::
PxArticulationLink* link = articulation->createLink(parent, linkPose);
PxRigidActorExt::createExclusiveShape(*link, linkGeometry, material);
PxRigidBodyExt::updateMassAndInertia(*link, 1.0f);
Articulation links have a restricted subset of the functionality of rigid bodies. They may not be kinematic, and they do not support damping, velocity clamping, or contact force thresholds. Sleep state and solver iteration counts are properties of the entire articulation rather than the individual links.
Each time a link is created beyond the first, a *PxArticulationJoint* is created between it and its parent. Specify the joint frames for each joint, in exactly the same way as for a PxJoint::
PxArticulationJointReducedCoordinate* joint = static_cast<PxArticulationJointReducedCoordinate*>(link->getInboundJoint());
joint->setParentPose(parentAttachment);
joint->setChildPose(childAttachment);
Finally, add the articulation to the scene::
scene.addArticulation(articulation);
Articulation Joints
===================
The PxArticulationJointReducedCoordinate provides an interface that is similar to the PxD6Joint interface for rigid body joints. However, it incurs certain limitations. By default, all axes are locked and the joint type is undefined. The first requirement is that the user defines the joint type. This can currently be any of the following:
* PxArticulationJointType::eFIX - a fixed joint with 0 degrees of freedom
* PxArticulationJointType::ePRISMATIC - a prismatic (sliding) joint with 1 degree of freedom
* PxArticulationJointType::eREVOLUTE - a revolute (hinge) joint with 1 degree of freedom
* PxArticulationJointType::eSPHERICAL - a spherical (ball-in-socket) joint with up to 3 degrees of freedom
The joint type can be set below using the following code. Here will define the joint to be a revolute (hinge) joint::
joint->setJointType(PxArticulationJointType::eREVOLUTE);
After defining the joint type, you must enable motion on the required axes. Motion can be either locked (default), free or limited. Here, we set the twist axis to be limited::
joint->setMotion(PxArticulationAxis::eTWIST, PxArticulationMotion::eLIMITED);
If a joint is limited, you can define the limit range. Limit ranges are defined using a min/max pair. Here, we set a limit in the range (-0.2, 0.5)::
PxReal lowLimit = -0.2f;
PxReal highLimit = 0.5f;
joint->setLimit(PxArticulationAxis::eTWIST, lowLimit, highLimit);
In addition, if required, a drive can be added to the joint. As with limits, this is defined on a per-axis basis. The drives operate like a PD controller and have 2 terms: stiffness and damping. Stiffness controls how strongly the drive drives towards a target joint position/angle and damping controls how strongly the joint drives towards the target velocity. In this case, we set a stiffness value of 10 and ad damping value of 0.1. In addition, we define the maximum motor force to be PX_MAX_F32::
PxReal stiffness = 10.f;
PxReal damping = 0.1f;
PxReal forceLimit = PX_MAX_F32;
joint->setDrive(PxArticulationAxis::eTWIST, stiffness, damping, forceLimit)
With these parameters the joint described will limit motion within (-0.2, 0.5) and drive towards a joint angle value of 0 with a stiffness of 10. The joint can be made to drive towards a target angle of 0.1 using the code below::
PxReal driveTargetAngle = 0.1f;
joint->setDriveTarget(PxArticulationAxis::eTWIST, driveTargetAngle);
Similarly, the joint can be made to drive towards a target velocity of -0.2 using the code below::
PxReal driveTargetVelocity = -0.2f;
joint->setDriveVelocity(PxArticulationAxis::eTWIST, driveTargetVelocity);
Articulation joints are not breakable, and it is not possible to retrieve the constraint force applied at the joint.
It should be noted that spherical joints are special-case handled by the reduced coordinate articulations. When there is just 1 degree of freedom, behavior is equivalent to a revolute joint type, but performance will be worse so it is recommended to use revolute joints instead of spherical joints wherever possible. When there are 2 or more degrees of freedom unlocked, rotational motion is integrated by rotating by decomposed quaternions rather than by euler angles to avoid gimbal lock. However, this technique can lead to rotational axis drift, which is corrected by additional constraints in the simulation, which could lead to slight movement on the remaining locked rotational axis.
The root link in a reduced coordinate articulation can be made to operate like a static body. This is advantageous over constraining the root link using a joint because the immoveable property of the root link is solved perfectly. The following code makes the root link of an articulation be fixed::
articulation->setArticulationFlag(PxArticulationFlag::eFIX_BASE, true);
Joint positions and velocities
==============================
Reduced coordinate articulations internally keep track of scalar joint positions, velocities and accelerations, with 1 entry corresponding to each degree of freedom. Joint position represents the relative offset between a parent and child link along/around a degree of freedom. If it is a rotational axis, this would represent an angle in radians. If it is a translational axis, this would represent a distance in whatever units the simulation is being performed in. Similarly, joint velocities represent a velocities around/along the degree of freedom in either radians/s or units/s, where units corresponds to the distance unit being used in the simulation.
Joint position values are used to integrate the links' poses. The root link's world space pose provides a reference frame from which the poses for all other links in the articulation are calculated. This ensures that there will never be any joint separation or rotations around locked axes.
The caveat with this approach is that it is not possible to simply update the pose of links in an articulation because this new transform could violate locked axes and would differ from the joint position. Instead, it is necessary to update the joint positions, which triggers new link poses to be computed. This ensures that the internal data from which poses are computed is internally consistent at all times.
Link velocities and joint velocities share a similar relationship. A link's world-space velocities are derived from the world-space velocity of their parent link and the current joint velocity. This means that the root link stores a world-space velocity and all other links' velocities are computed by propagating this velocity and the respective joint velocities through the articulation. As such, it is not possible to directly set the velocity of links in an articulation. Instead, it is necessary to modify the joint velocities from which the links' velocities are calculated. It is legal to apply world-space impulses on links. These will be propagated through the articulation as part of forward dynamics.
In addition to applying impulses and setting joint positions and velocities, it is possible to interact with the articulation links through the application of joint forces/torques, which behave like an actuator.
PxArticulationCache
===================
Direct access to joint positions, velocities and forces are provided through a class called PxArticulationCache. This class exposes direct access to the scalar values that control a reduced coordinate articulation.
You create an articulation cache like this::
PxArticulationCache* cache = articulation->createCache();
This method creates a cache with which to store internal articulation state. This cache is constructed specifically for a given articulation and contains the exact space to store data about that articulation. It should not be shared between different articulations. Similarly, if the properties of an articulation change (a link is added/removed, degrees of freedom are changed etc.), it is necessary to destroy the cache and recreate it.
Once a cache has been created, you must copy data to the cache. In this case, we copy all data to the cache, but you can filter and only copy the specific data you are interested in::
articulation->copyInternalStateToCache(*cache, PxArticulationCache::eALL);
The cache stores its own copy of the internal state of the articulation. Any modifications to the state in the cache do not alter the internal state of the articulation that the cache was created from. To update the internal state of the articulation, it is necessary to apply the cache. In this example, we apply all state to the articulation, but only a subset of the state can be applied by passing different flags::
articulation->applyCache(*cache, PxArticulationCache::eALL);
It is not legal to copy state to a cache or apply a cache while the simulation is running.
A cache stores sufficient information to be able to record the state of an entire articulation at a snapshot in time and then reset the articulation back to that state. It is legal to create and maintain multiple articulation caches for a given articulation.
A cache provides access to the root link's state, including transform, velocities and accelerations. It also provides access to jointVelocity, jointAcceleration, jointPosition and jointForce.
Simple operations like resetting an articulation back to its original configuration or zeroing joint velocity can be done with the following code snippet::
PxMemZero(cache->jointVelocity, sizeof(PxReal)*articulation->getDofs());
Joint data (velocity, position etc.) provide one entry per degree of freedom. These values are stored in a specific order to facilitate propagation throughout the articulation. This order may be different from the order in which the joints were originally defined. PhysX provides some utility functions to compute the index of the a joint in these arrays. To compute the link's low-level index, call:
PxU32 llIndex = articulationLink->getLinkIndex();
To compute the number of degrees of freedom for the link, call::
PxU32 dofs = articulationLink->getInboundJointDof();
The location of the entries corresponding to a given link can be found by summing the number of degrees of freedom of all preceding links. A snippet providing an example of how to do this is provided below::
PxU32 dofStarts[TotalLinkCount];
dofStarts[0] = 0; //We know that the root link does not have a joint
for(PxU32 i = 1; i < TotalLinkCount; ++i)
{
PxU32 llIndex = links[i]->getLinkIndex();
PxU32 dofs = links[i]->getInboundJointDof();
dofStarts[llIndex] = dofs;
}
PxU32 count = 0;
for(PxU32 i = 1; i < TotalLinkCount; ++i)
{
PxU32 dofs = dofStarts[i];
dofStarts[i] = count;
count += dofs;
}
Setting a particular joint position for a given link's inbound joint can be done using the snippet below::
cache->jointPosition[dofStarts[link->getLinkIndex()]] = newJointPosition;
In addition, it contains several fields related to inverse dynamics, which will be described later in this document.
The Jacobian matrix is an important concept to roboticists. Multiplication with the Jacobian matrix maps the joint space velocities of the robot to world space velocities. The Jacobian matrix of an articulation can be computed using::
PxU32 nRows;
PxU32 nCols;
articulation->computeDenseJacobian(*cache, nRows, nCols);
This will write the Jacobian matrix to PxArticulationCache::denseJacobian; the dimensions of the matrix are written to the two unsigned integers. Note how the Jacobian matrix is a sparse triangular matrix so such explicit dense representation is in general not an optimal use of memory. PhysX does not use this representation internally.
Inverse Dynamics
================
In addition to forward dynamics, reduced coordinate articulations offer inverse dynamics functionality. This is a suite of utility functions to compute the joint forces required to counteract gravity, coriolis/centrifugal force, external forces and contacts/constraints. In addition, there are utility functions to compute kinematic jacobians, the mass matrix, coefficient matrix and lambda values.
The following description assumes knowledge of inverse dynamics concepts.
Prior to performing any inverse dynamics calculations, it is necessary to ensure that constant joint data has been computed. In order to do this, call::
articulation->commonInit();
The inverse dynamics functions operate on an articulation and a PxArticulationCache. The majority of properties in PxArticulationCache are stored in a reduced/generalized coordinate space, where one entry corresponds to a degree of freedom. To simplify working in this space, PhysX provides the following methods to pack and unpack data to convert between reduced/generalized and maximal coordinates.
To unpack data from reduced/generalized coordinates to maximal coordinates::
PxReal maximalJointForces[NbLinks*6]; //There are 6 dofs (x,y,z,twist,swing1,swing2) per link
articulation->unpackJointData(cache->jointForces, maximalJointForces);
To pack data from maximal coordinates to reduced/generalized coordinates::
articulation->packJointData(maximalJointForces, cache->jointForces);
The following method computes the joint force required to counteract gravity and deposits the set of joint forces in the force buffer in a PxArticulationCache::
articulation->computeGeneralizedGravityForce(*cache);
To compute the joint force required to counteract coriolis and centrifugal force, we must first provide the joint velocities of the articulation because coriolis/centrifugal forces are dependent on those values. In this example, we extract the velocities from the articulation before computing coriolis/centrifugal force::
articulation->copyInternalStateToCache(*cache, PxArticulationCache::eVELOCTY);
articulation->computeCoriolisAndCentrifugalForce(*cache);
To compute the joint acceleration for the articulation, call::
articulation->computeJointAcceleration(*cache);
Joint acceleration is dependent on gravity and current link velocity. The resulting acceleration can be used to compute a set of joint forces capable of maintaining the current joint velocities.
To convert a joint acceleration into a joint velocity. Input is cache.jointAcceleration, output is cache.jointForce::
articulation->computeJointForce(*cache);
The generalized mass matrix can be computed using the following method. Output is placed in cache.massMatrix. This matrix represents the joint space inertia of the articulation and can be used to convert joint accelerations into joint forces/torques::
articulation->computeGeneralizedMassMatrix(*cache);
Articulations are tree structures and do not support closed loops natively. PhysX can simulate closed loop systems by the use of joints. Additionally, contacts between links and other rigid bodies (e.g. the ground) can form loops if more than one link has contacts. These joints and contacts are solved by the rigid body solver during PhysX simulation, but it is often desirable to factor these constraints into inverse dynamics.
To facilitate this, PhysX provides a mechanism to register loop constraints with the articulation::
PxJoint* joint = ...;
articulation->addLoopJoint(joint);
articulation->removeLoopJoint(joint);
In order to represent contacts, use PxContactJoint : this is a new addition specifically for inverse dynamics that represents a contact as an extension to the joint system. Currently, the following features are restricted to the use of PxContactJoint. Support for additional joint types will be added in the near future.
Inverse dynamics provides functionality to calculate the coefficient matrix. This matrix is an NxM matrix, where N is the number of degrees of freedom in the articulation and M is the number constraint rows. This matrix represents the joint force caused by a unit impulse applied to each constraint::
PxU32 coefficientMatrixSize = articulation->getCoefficentMatrixSize();
PxReal* coefficientMatrix = new PxReal[coefficientMatrixSize];
cache->coefficientMatrix = coefficientMatrix;
articulation->computeCoefficientMatrix(*cache);
The coefficient matrix can be used to convert a set of lambda values (impulses applied by the respective constraints) into a set of joint forces to counteract these impulses. However, the lambda values are only known after a frame's simulation occurs, so it may be necessary to know these lambda values before they are applied in the solver. However, even if these lambda values are known ahead-of-time, applying a counteracting force may not yield the desired results because the act of applying additional force on the joints may influence the lambda values, resulting in a different set of joint forces.
In order to overcome this feedback-loop, inverse dynamics provides a function to compute the lambda values for the loop constraints, assuming that the joint forces required to counteract these lambdas are also applied. This technique is an iterative process that converges on a stable set of joint forces.
First, we must allocate sufficient space for the lambda values
PxReal* lambda = new PxReal[articulation->getNbLoopJoints()];
cache->lambda = lambda;
In order to compute the lambda values, we need to have 2 caches: one to store the initial state, and one to calculate the final state::
PxArticulationCache* initialState = articulation->createCache()
articulation->copyInternalStateToCache(*initialState, PxArticulationCache::eALL);
Now we can compute the joint forces values. In addition, we must compute the internal and external joint forces caused by gravity and coriolis/centrifugal force to ensure that we converge on a result that will match forward dynamics. The joint force values for internal/external accelerations can be calculated using the methods outlined earlier. The method is iterative and terminates either after reaching convergence or when a maximum number of iterations is run (in this case 200). Resulting joint force is output in cache.jointForce::
const PxU32 maxIterations = 200;
articulation->computeLambda(*cache, *initialState, jointForces, maxIterations);
Computes the dense representation of an inherently sparse matrix. Multiplication with this matrix maps joint space velocities to 6DOF world space linear and angular velocities::
articulation->getDenseJacobian(*cache, nRows, nCols);

View File

@ -0,0 +1,395 @@
.. _BestPractices:
------------------------
Best Practices Guide
------------------------
=========================
Introduction
=========================
This chapter covers a number of best practices for the PhysX SDK to assist in diagnosing and fixing frequently encountered issues.
=========================
Debugging
=========================
The PhysX SDK contains a few debugging helpers. They can be used to make sure the scenes are properly set up.
Use checked builds and the error stream
=======================================
The PhysX SDK has different build configurations: Debug, Checked, Release, Profile. To make sure that the scene is properly set up without warnings or errors, use either the Debug or Checked builds, and monitor the error callback. Please refer to the :ref:`ErrorReporting` chapter for details. Note that some checks can be expensive and thus they are not performed in Release or Profile builds. If the SDK silently fails or even crashes in a Release build, please switch to Debug or Checked builds to ensure this is not caused by an uncaught error.
Visualizing physics data
========================
Use the PhysX Visual Debugger (PVD) to see what PhysX is seeing and make sure the physics data is what you expect it to be. Please refer to the :ref:`physxvisualdebugger` chapter for details. Note that this is only available in Debug, Checked and Profile builds.
Visualizing physics data (2)
============================
An alternative to PVD is the built-in debug visualization system. Please refer to the :ref:`debugvisualization` chapter for details. This option is available with all build configurations.
Limiting coordinates
====================
Bugs in applications, or issues in content creation, can sometimes result in object placement at unexpected coordinates. We recommend the use of PxSceneDesc::sanityBounds, to generate reports when objects are inserted at positions beyond what your application expects, or when application code moves them to such unexpected positions. Note that these bounds only apply to application updates of actor coordinates, not updates by the simulation engine.
=========================
Performance Issues
=========================
The PhysX SDK has been optimized a lot in the past dot releases. However, there still exist various performance pitfalls that the user should be aware of.
Use profile builds to identify performance bottlenecks
======================================================
The PhysX SDK has different build configurations: Debug, Checked, Release, Profile. To identify performance bottlenecks, please use Profile builds and PVD. Use the *PxPvdInstrumentationFlag::ePROFILE* only, since enabling the other connection flags might negatively affect performance. Please refer to the :ref:`physxvisualdebugger` chapter for details.
Use release builds for final performance tests
==============================================
The PhysX SDK has different build configurations: Debug, Checked, Release, Profile. The Release builds are the most optimal. If you encounter a performance issue while using other builds, please switch to Release builds and check if the problem is still there.
Disable debug visualization in final/release builds
===================================================
Debug visualization is great for debugging but it can have a significant performance impact. Make sure it is disabled in your final/release builds. Please refer to the :ref:`debugvisualization` chapter for details.
Debug visualization is very slow
================================
Debug visualization can be very slow, because both the code gathering the debug data and the code rendering it is usually not optimal. Use a culling box to limit the amount of data the SDK gathers and sends to the renderer. Please refer to the :ref:`debugvisualization` chapter for details.
Consider using tight bounds for convex meshes
=============================================
By default PhysX computes approximate (loose) bounds around convex objects. Using PxConvexMeshGeometryFlag::eTIGHT_BOUNDS enables smaller/tighter bounds, which are more expensive to compute but can result in improved simulation performance when a lot of convex objects are interacting with each other. Please refer to the :ref:`Geometry` chapter for details.
Use scratch buffers
===================
The PxScene::simulate function accepts optional scratch buffers that can be used to reduce temporary allocations and improve simulation performance. Please refer to the :ref:`Simulation` chapter for details.
Use the proper mid-phase algorithm
==================================
PxCookingParams::midphaseDesc can be used to select the desired mid-phase structure. It is a good idea to try the different options and see which one works best for you. Generally speaking the new PxMeshMidPhase::eBVH34 introduced in PhysX 3.4 has better performance for scene queries against large triangle meshes. Please refer to the :ref:`Geometry` chapter for details.
Use the proper narrow-phase algorithm
=====================================
PxSceneFlag::eENABLE_PCM enables an incremental "persistent contact manifold" algorithm, which is often faster than the previous implementation. PCM should be the default algorithm since PhysX 3.4, but you can also try to enable it in previous versions like 3.3.
Use the proper broad-phase algorithm
====================================
PhysX also supports three different broad-phase implementations, selected with PxSceneDesc::broadPhaseType. The different implementations have various performance characteristics, and it is a good idea to experiment with them and find which one works best for you. Please refer to the :ref:`RigidBodyCollision` chapter for details about the different broad-phases.
Use the scene-query and simulation flags
========================================
If a shape is only used for scene-queries (raycasts, etc), disable its simulation flag. If a shape is only used for simulation (e.g. it will never be raycasted against), disable its scene-query flag. This is good for both memory usage and performance. Please refer to the :ref:`RigidBodyCollision` chapter for details.
Tweak the dynamic tree rebuild rate
===================================
If the PxScene::fetchResults call takes a significant amount of time in scenes containing a lot of dynamic objects, try to increase the PxSceneDesc::dynamicTreeRebuildRateHint parameter. Please refer to the :ref:`SceneQueries` chapter for details.
Use the insertion callback when cooking at runtime
==================================================
Use PxPhysicsInsertionCallback for objects that are cooked at runtime. This is faster than first writing the data to a file or a memory buffer, and then passing the data to PhysX.
Kill kinematic pairs early
==========================
If you do not need them, use PxPairFilteringMode::eKILL to disable kinematic pairs earlier in the pipeline. This is especially effective with the PxBroadPhaseType::eABP broadphase. Sometimes it is not possible to disable all kinematic pairs globally because a few of them are necessary. In that case the PxRigidBodyFlag::eFORCE_KINE_KINE_NOTIFICATIONS and PxRigidBodyFlag::eFORCE_STATIC_KINE_NOTIFICATIONS flags can help.
Beware of PxScene::getActors()
==============================
There is an implementation issue in this function that makes it run in O(n^2) time when called for one object at a time. It is fine to call it to retrieve all objects at once though. Or at least call it to retrieve a batch of objects (maybe 64 or 128) at the same time, to limit the performance problem.
The "Well of Despair"
==========================
One common use-case for a physics engine is to simulate fixed-size time-steps independent of the frame rate that the application is rendered at. If the application is capable of being rendered at a higher frequency than the simulation frequency, the user has the option to render the same simulation state, interpolate frames etc. However, sometimes it is not possible to render the scene at a frequency higher-or-equal to the simulation frequency. At this point, the options are to either run the physics simulation with a larger time-step or to simulate multiple, smaller sub-steps. The latter is generally a preferable solution because changing the size of time-steps in a physics simulation can significantly change perceived behavior. However, when using a sub-stepping approach, one must always be aware of the potential that this has to damage performance.
As an example, let's imagine a game that is running using v-sync at 60FPS. This game is simulating a large number of physics bodies and, as a result, the physics is relatively expensive. In order to meet the 60FPS requirement, the entire frame must be completed within ~16ms. As already mentioned, the physics is reasonably expensive and, in this scenario, takes 9ms to simulate 1/60th of a second. If the game was to suddenly spike, e.g. as a result of some OS activity, saving a check-point or loading a new section of the level, we may miss the deadline for 60FPS. If this happens, we must run additional sub-steps in the physics to catch up the missed time in the next frame. Assuming that the previous frame took 50ms instead of 16ms, we must now simulate 3 sub-steps to be able to simulate all the elapsed time. However, each sub-step takes ~9ms, which means that we will take ~27ms to simulate 50ms. As a result, this frame also misses our 16ms deadline for 60FPS, meaning that the frame including v-sync took 33ms (i.e. 30Hz). We must now simulate 2 sub-steps in the next frame, which takes ~18ms and also misses our 16ms deadline. As a result, we never manage to recover back to 60FPS. In this scenario, our decision to sub-step as a result of a spike has resulted in our application being stuck in a performance trough indefinitely. The application is capable of simulating and rendering at 60FPS but becomes stuck in the so-called "physics well of despair" as a result of substepping.
Problems like this can be alleviated in several ways:
* Decouple the physics simulation from the application's update/render loop. In this case, the physics simulation becomes a scheduled event that occurs at a fixed frequency. This can make player interaction in the scene more difficult and may introduce latency so must be well-thought through. However, using multiple scenes (one synchronous for "important" objects, one asynchronous for "unimportant" objects) can help.
* Permit the application to "drop" time when faced with a short-term spike. This may introduce visible motion artifacts if spikes occur frequently.
* Introduce slight variations in time-step (e.g. instead of simulating at 1/60th, consider simulating a range between 1/50th and 1/60th). This can introduce non-determinism into the simulation so should be used with caution. If this is done, additional time that must be simulated can potentially be amortized over several frames by simulating slightly larger time-steps.
* Consider simplifying the physics scene, e.g. reducing object count, shape complexity, adjusting iteration counts etc. Provided physics simulation is a small portion of the total frame time, the application should find it easier to recover from spikes.
Pruner Performance for Streamed Environments
=============================================
PhysX provides multiple types of pruners, each of which aimed at specific applications. These are:
* Static AABB tree
* Dynamic AABB tree
By default, the static AABB tree is used for the static objects in the environment and the dynamics AABB tree is used for the dynamic objects in the environment. In general, this approach works well but it must be noted that creating the static AABB tree can be very expensive. As a result, adding, removing or moving any static objects in the environment will result in the static AABB tree being fully recomputed, which can introduce significant performance cost. As a result, we recommend the use of dynamics AABB trees for both static and dynamic pruners in applications which stream in the static environment. Additionaly scene query performance against newly added objects can be improved by using PxPruningStructure, which can precompute the AABB structure of inserted objects in offline.
Performance Implications for Multi-Threading
=============================================
The PhysX engine is designed from the ground-up to take advantage of multi-core architectures to accelerate physics simulation. However, this does not mean that more threads are always better. When simulating extremely simple scenes, introducing additional worker threads can detrimentally affect performance. This is because, at its core, PhysX operates around a task queue. When a frame's simulation is started, PhysX dispatches a chain of tasks that encapsulate that frame of physics simulation. At various stages of the physics pipeline, work can be performed in parallel on multiple worker threads. However, if there is insufficient work, there will be little or no parallel execution. In this case, the use of additional worker threads may detrimentally affect performance because the various phases of the pipeline may be run by different worker threads, which may incur some additional overhead depending on the CPU architecture compared to running on just a single worker thread. As a result, developers should measure the performance of the engine with their expected physics loads with different numbers of threads to maximize their performance and make sure that they are making the most of the available processing resources for their application.
Memory allocation
==========================
Minimizing dynamic allocation is an important aspect of performance tuning, and PhysX provides several mechanisms to control memory usage.
Reduce allocation used for tracking objects by presizing the capacities of scene data structures, using either PxSceneDesc::limits before creating the scene or the function PxScene::setLimits(). When resizing, the new capacities will be at least as large as required to deal with the objects currently in the scene. These values are only for preallocation and do not represent hard limits, so if you add more objects to the scene than the capacity limits you have set, PhysX will allocate more space.
Much of the memory PhysX uses for simulation is held in a pool of blocks, each 16K in size. You can control the current and maximum size of the pool with the nbContactDataBlocks and maxNbContactDataBlocks members of PxSceneDesc. PhysX will never allocate more than the maximum number of blocks specified, and if there is insufficient memory it will instead simply drop contacts or joint constraints. You can find out how many blocks are currently in use with the getNbContactBlocksUsed() method, and find out the maximum number that have ever been used with the getMaxNbContactDataBlocksUsed() method.
Use PxScene::flushSimulation() to reclaim unused blocks, and to shrink the size of scene data structures to the size presently required.
To reduce temporary allocation performed during simulation, provide physx with a memory block in the simulate() call. The block may be reused by the application after the fetchResults() call which marks the end of simulation. The size of the block must be a multiple of 16K, and it must be 16-byte aligned.
==========================================================================================================
Character Controller Systems using Scene Queries and Penetration Depth Computation
==========================================================================================================
Implementing a Character Controller (CCT) is a common use case for the PhysX Scene Query (SQ) system. A popular approach is to use sweeps to implement movement logic,
and to improve robustness by using Geometry Queries (GQ) to compute and resolve any penetrations that occur due to object movement that does not account for the presence of the controller, or due to numerical precision issues.
**Basic Algorithm:**
1. Call a SQ-Sweep from the current position of the CCT shape to its goal position.
2. If no initial overlap is detected, move the CCT shape to the position of the first hit, and adjust the trajectory of the CCT by removing the motion relative to the contact normal of the hit.
3. Repeat Steps 1 and 2 until the goal is reached, or until an SQ-Sweep in Step 1 detects an initial overlap.
4. If an SQ-Sweep in Step 1 detects an initial overlap, use the GQ Penetration Depth computation function to generate a direction for depenetration. Move the CCT shape out of penetration and begin again with Step 1.
**Limitations and Problems**
Step 4 of the algorithm above can sometimes run into trouble due to implementation differences in SQ-Sweep, SQ-Overlap and and GQ-Penetration Depth queries. Under certain initial conditions
it is possible that the SQ system will determine that a pair of objects is initially overlapping while the GQ -Penetration Depth computation will report them as disjoint (or vice-versa). Penetration depth calculations involving convex hulls operate by shrinking the convex hull and performing distance calculations between a shape and the shrunken convex hull. To understand the conditions under which this occurs and how to resolve the artefacts, please refer to the diagrams and discussion below. Each diagram represents the initial conditions of two shapes, a Character Controller shape (red boxes), a convex obstacle (black boxes), at the time that Step 1 of the
algorithm above is executed. In the diagrams, the outermost rectangular black box is the convex hull as seen by the SQ algorithms; the inner black box with a dashed line represents the shrunken convex shape
and the black box with rounded corners is the shrunken convex shape inflated by the amount by which we shrunk. These three black boxes are used by the GQ-Penetration Depth computation. Although the example refers to convex hull obstacles, the issue is not exclusive to the convex hull shapes;
the problem is similar for other shape types as well.
.. figure:: ../images/SQAndMTDMismatched1.png
:align: center
**Diagram 1: CCT Shape Barely Touches an Obstacle**
In **Diagram 1**, the red box of the CCT is barely touching the outermost black box of the convex obstacle. In this situation the SQ-Sweep will report an initial overlap but the GQ-Penetration Depth function will report no hit, because the red box is not touching the black box with rounded corners.
To resolve this, inflate the CCT shape for the GQ-Penetration Depth calculation to ensure that it detects an overlap and returns a valid normal. Note that after inflating the CCT shape, the GQ-Penetration Depth function will report that the shapes are penetrated more deeply than they actually are, so take this additional penetration into account when depenetrating in Step 4. This may result in some clipping around the corners and edges of convex objects but the CCT's motion should be acceptable. As the corners/edges become more acute, the amount of clipping will increase.
.. figure:: ../images/SQAndMTDMismatched2.png
:align: center
**Diagram 2: CCT Overlaps an Obstacle Slightly**
**Diagram 2** shows a case where the CCT initially overlaps the outer black box seen by the SQ system, but does not overlap the shrunken shape seen by the GQ-Penetration Depth calculator. The GQ-Penetration Depth system will return the penetration from point c to point b but not from point c to point a. Therefore the CCT may clip through the corner of the convex hull after depenetration. This can be corrected in Step 4.
.. figure:: ../images/SQAndMTDMismatched3.png
:align: center
**Diagram 3: CCT Overlaps an Obstacle Significantly**
As can been seen from **Diagram 3**, if the CCT penetrates sufficiently that it overlaps with the shrunken shape seen by GQ, the GQ-Penetration Depth calculator will return the penetration from point c to point a. In this case, the GQ-Penetration Depth value can be used without modification in Step 4. However, as this condition would be difficult to categorize without additional computational cost, it is best to inflate the shape as recommended in Step 4 and then subtract this inflation from the returned penetration depth.
**Unified MTD Sweep**
A recent addition to the scene query sweeps is the flag PxHitFlag::eMTD. This can be used in conjunction with default sweeps to generate the MTD (Minimum Translation Direction) when an initial overlap is detected by a sweep. This flag is guaranteed to generate an appropriate normal under all circumstances, including cases where the sweep may detect an initial overlap but calling a stand-alone MTD function may report no hits. It still may suffer from accuracy issues with penetration depths but, in the cases outlined above around corners/edges, it will report a distance of 0 and the correct contact normal. This can be used to remove components of the sweep moving into the normal direction and then re-sweeping when attempting to implement a CCT. This also generates compound MTDs for meshes/heightfields, which means that it reports an MTD that de-penetrates the shape from the entire mesh rather than just an individual triangle, if such an MTD exists.
==========================================================================================================
Quantizing HeightField Samples
==========================================================================================================
Heightfield samples are encoded using signed 16-bit integers for the y-height that are then converted to a float
and multiplied by PxHeightFieldGeometry::heightScale to obtain local space scaled coordinates. Shape transform is then applied on top to obtain world space location.
The transformation is performed as follows (in pseudo-code)::
localScaledVertex = PxVec3(row * desc.rowScale, PxF32(heightSample) * heightScale,
col * desc.columnScale)
worldVertex = shapeTransform( localScaledVertex )
The following code snippet shows one possible way to build quantized unscaled local space heightfield coordinates from world space grid heights stored in terrainData.verts::
const PxU32 ts = ...; // user heightfield dimensions (ts = terrain samples)
// create the actor for heightfield
PxRigidStatic* actor = physics.createRigidStatic(PxTransform(PxIdentity));
// iterate over source data points and find minimum and maximum heights
PxReal minHeight = PX_MAX_F32;
PxReal maxHeight = -PX_MAX_F32;
for(PxU32 s=0; s < ts * ts; s++)
{
minHeight = PxMin(minHeight, terrainData.verts[s].y);
maxHeight = PxMax(maxHeight, terrainData.verts[s].y);
}
// compute maximum height difference
PxReal deltaHeight = maxHeight - minHeight;
// maximum positive value that can be represented with signed 16 bit integer
PxReal quantization = (PxReal)0x7fff;
// compute heightScale such that the forward transform will generate the closest point
// to the source
// clamp to at least PX_MIN_HEIGHTFIELD_Y_SCALE to respect the PhysX API specs
PxReal heightScale = PxMax(deltaHeight / quantization, PX_MIN_HEIGHTFIELD_Y_SCALE);
PxU32* hfSamples = new PxU32[ts * ts];
PxU32 index = 0;
for(PxU32 col=0; col < ts; col++)
{
for(PxU32 row=0; row < ts; row++)
{
PxI16 height;
height = PxI16(quantization * ((terrainData.verts[(col*ts) + row].y - minHeight) /
deltaHeight));
PxHeightFieldSample& smp = (PxHeightFieldSample&)(hfSamples[(row*ts) + col]);
smp.height = height;
smp.materialIndex0 = userValue0;
smp.materialIndex1 = userValue1;
if (userFlipEdge)
smp.setTessFlag();
}
}
// Build PxHeightFieldDesc from samples
PxHeightFieldDesc terrainDesc;
terrainDesc.format = PxHeightFieldFormat::eS16_TM;
terrainDesc.nbColumns = ts;
terrainDesc.nbRows = ts;
terrainDesc.samples.data = hfSamples;
terrainDesc.samples.stride = sizeof(PxU32); // 2x 8-bit material indices + 16-bit height
terrainDesc.flags = PxHeightFieldFlags();
PxHeightFieldGeometry hfGeom;
hfGeom.columnScale = terrainWidth / (ts-1); // compute column and row scale from input terrain
// height grid
hfGeom.rowScale = terrainWidth / (ts-1);
hfGeom.heightScale = deltaHeight!=0.0f ? heightScale : 1.0f;
hfGeom.heightField = cooking.createHeightField(terrainDesc, physics.getPhysicsInsertionCallback());
delete [] hfSamples;
PxTransform localPose;
localPose.p = PxVec3(-(terrainWidth * 0.5f), // make it so that the center of the
minHeight, -(terrainWidth * 0.5f)); // heightfield is at world (0,minHeight,0)
localPose.q = PxQuat(PxIdentity);
PxShape* shape = PxRigidActorExt::createExclusiveShape(*actor, hfGeom, material, nbMaterials);
shape->setLocalPose(localPose);
=========================
Reducing memory usage
=========================
The following strategies can be used to reduce PhysX's memory usage.
Consider using tight bounds for convex meshes
=============================================
See the above chapter about Performance Issues for details. Using tight bounds for convex meshes is mainly useful for performance, but it can also reduce the amount of pairs coming out of the broad-phase, which decreases the amount of memory needed to manage these pairs.
Use scratch buffers
===================
See the above chapter about Performance Issues for details. Scratch buffers can be shared between multiple sub-systems (e.g. physics and rendering), which can globally improve memory usage. PhysX will not use less memory per-se, but it will allocate less of it.
Flush simulation buffers
========================
Call the PxScene::flushSimulation function to free internal buffers used for temporary computations. But be aware that these buffers are usually allocated once and reused in subsequent frames, so releasing the memory might trigger new re-allocations during the next simulate call, which can decrease performance. Please refer to the :ref:`simulation_memory` chapter for details.
Use preallocation
=================
Use PxSceneDesc::limits to preallocate various internal arrays. Preallocating the exact necessary size for internal buffers may use less memory overall than the usual array resizing strategy of dynamic arrays. Please refer to the :ref:`simulation_memory` chapter for details.
Tweak cooking parameters
========================
Some cooking parameters have a direct impact on memory usage. In particular, PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE, PxCookingParams::suppressTriangleMeshRemapTable, PxBVH33MidphaseDesc::meshCookingHint, PxBVH33MidphaseDesc::meshSizePerformanceTradeOff, PxBVH34MidphaseDesc::numTrisPerLeaf, PxCookingParams::midphaseDesc, PxCookingParams::gaussMapLimit and PxCookingParams::buildTriangleAdjacencies can be modified to choose between runtime performance, cooking performance or memory usage.
Share shape and mesh data
=========================
Share the same PxConvexMesh and PxTriangleMesh objects between multiple shape instances if possible. Use shared shapes if possible. Please refer to the :ref:`RigidBodyCollision` chapter for details about shape sharing.
Use the scene-query and simulation flags
========================================
If a shape is only used for scene-queries (raycasts, etc), disable its simulation flag. If a shape is only used for simulation (e.g. it will never be raycasted against), disable its scene-query flag. This is good for both memory usage and performance. Please refer to the :ref:`RigidBodyCollision` chapter for details.
Kill kinematic pairs early
==========================
If you do not need them, use PxPairFilteringMode::eKILL to disable kinematic pairs earlier in the pipeline.
=========================
Behavior issues
=========================
Objects do not spin realistically
=================================
For historical reasons the default maximum angular velocity is set to a low value (7.0). This can artificially prevent the objects from spinning quickly, which may look unrealistic and wrong in some cases. Please use PxRigidDynamic::setMaxAngularVelocity to increase the maximum allowed angular velocity. Note that this default value has been increased to 100.0 in PhysX 4.0.
Overlapping objects explode
===========================
Rigid bodies created in an initially overlapping state may explode, because the SDK tries to resolve the penetrations in a single time-step, which can lead to large velocities. Please use PxRigidBody::setMaxDepenetrationVelocity to limit the de-penetration velocity to a reasonable value (e.g. 3.0).
Rigid bodies are jittering on the ground
========================================
Visualize the contacts with the visual debugger. If the jittering is caused by contacts that appear and disappear from one frame to another, try to increase the contact offset (PxShape::setContactOffset).
Piles or stacks of objects are not going to sleep
=================================================
PxSceneFlag::eENABLE_STABILIZATION might help here. This is not recommended for jointed objects though, so use PxRigidDynamic::setStabilizationThreshold to enable/disable this feature on a per-object basis. It should be safe to enable for objects like debris.
Jointed objects are unstable
============================
There are multiple things to try here:
- Enable the new TGS solver in PhysX 4.0.
- Increase the solver iteration counts, in particular the number of position iterations. Please refer to the :ref:`RigidBodyDynamics` chapter for details.
- Consider creating the same constraints multiple times. This is similar to increasing the number of solver iterations, but the performance impact is localized to the jointed object rather than the simulation island it is a part of. So it can be a better option overall. Note that the order in which constraints are created is important. Say you have 4 constraints named A, B, C, D, and you want to create them 4 times each. Creating them in the AAAABBBBCCCCDDDD order will not improve the behavior, but creating them in the ABCDABCDABCDABCD order will.
- Consider using joint projection. This might help for simple cases where only a few objects are connected. Please refer to the :ref:`Joints` chapter for details.
- Use smaller time steps. This can be an effective way to improve joints' behavior, although it can be an expensive solution. Instead of running 1 simulation call with a time-step dt and N solver iterations, consider trying N simulation calls with a time-step dt/N and 1 solver iteration.
- Consider tweaking inertia tensors. In particular, for ropes or chains of jointed objects, the PxJoint::setInvMassScale and PxJoint::setInvInertiaScale functions can be quite effective. An alternative is to compute the inertia tensor (e.g. using PxRigidBodyExt::setMassAndUpdateInertia) with an artificially increased mass, and then set the proper mass directly afterwards (using PxRigidBody::setMass).
- Consider adding extra distance constraints. For example in a rope, it can be effective to create an extra distance constraint between the two ends of the rope, to limit its stretching. Alternatively, one can create distance constraints between elements N and N+2 in the chain.
- Use spheres instead of capsules. A rope made of spheres will be more stable than a rope made of capsules. The positions of pivots can also affect stability. Placing the pivots at the spheres' centers is more stable than placing them on the spheres' surfaces.
- Use articulations. Perhaps not surprisingly, articulations are much better at simulating articulated objects. They can be used to model better ropes, bridges, vehicles, or ragdolls out-of-the-box, without the need for the above workarounds. Please refer to the :ref:`Articulations` chapter for details. They are more expensive than regular joints though.
==========================================================================================================
GPU Rigid Bodies
==========================================================================================================
Collision detection with PxSceneFlag::eENABLE_GPU_DYNAMICS will be executed on GPU for all convex-convex, convex-box, box-box, convex-mesh, box-mesh, convex-HF anb box-HF pairs. However, such pairs will not be processed if either the vertex count of the convex hull exceeds 64 vertices (convex desc flag PxConvexFlag::eGPU_COMPATIBLE can be used to create compatible hulls), the pair requests contact modification, the triangle mesh was not cooked with GPU data requested (PxCookingParams::buildGrbData) or if the triangle mesh makes use of per-triangle materials.
Aggregates are used to lighten the load on broad phases. When running broad phase on the CPU, aggregates frequently improve performance by reducing the load on the core broad phase algorithm. However, there is some cost when aggregates overlap because these overlaps must be processed by a separate module. When using GPU broad phase, the use of aggregates generally result in performance regressions because the processing of aggregate overlaps occurs on the CPU and, while using aggregates can reduce the load on the GPU broad phase, the amount by which they improve GPU broad phase performance is frequently smaller than the cost of processing the aggregate overlaps.
====================================================================
Determinism
====================================================================
The PhysX SDK can be described as offering limited determinism. Results can vary between platforms due to differences in hardware maths precision and differences in how the compiler reoders instructions during optimization. This means that behavior can be different between different platforms, different compilers operating on the same platform or between optimized and unoptimized builds using the same compiler on the same platform. However, on a given platform, given the exact same sequence of events operating on the exact scene using a consistent time-stepping scheme, PhysX is expected to produce deterministic results. In order to achieve this determinism, the application must recreate the scene in the exact same order each time and insert the actors into a newly-created PxScene. There are several other factors that can affect determinism so if an inconsistent (e.g. variable) time-stepping scheme is used or if the application does not perform the same sequence of API calls on the same frames, the PhysX simulation can diverge.
In addition, the PhysX simulation can produce divergent behavior if any conditions in the simulation has varied. Even the addition of a single actor that is not interacting with the existing set of actors in the scene can produce divergent results.
PhysX provides a mechanism to overcome the issue of divergent behavior in existing configurations as a result of additional actors being added or actors being removed from the scene that do not interact with the other actors in the scene. This mechanism can be enabled by raising PxSceneFlag::eENABLE_ENHANCED_DETERMINISM on PxSceneDesc::flags prior to creating the scene. Enabling this mode makes some performance concessions to be able to offer an improved level of determinism. The application must still follow all the requirements to achieve deterministic behavior described previously in order for this mechanism to produce consistent results.

View File

@ -0,0 +1,87 @@
.. _Building with PhysX:
--------------------
Building with PhysX
--------------------
===============================
Generating projects with CMake
===============================
PhysX uses CMake to generate build configuration files (e.g. Microsoft Visual Studio Solutions) for all supported platforms. The PhysX distribution comes with default CMake parameter sets called **presets**.
The build configuration files can be generated with the script **generate_projects** in the distributions "physx/" directory. This script either prompts for a preset or accepts a preset as a command line parameter. The resulting build configuration files are generated under "physx/compiler/".
PhysX binaries (including static libraries) are placed into "bin/<platform>.<target>.<compiler>.<runtime>/<configuration>/" directories, for example the vc14win64 checked build puts binaries into "bin/win.x86_32.vc140.mt/checked/".
===============================
Build Configurations
===============================
The SDK has four build configurations available, designed for different stages of development and deployment.
* the *debug* build can be useful for error analysis, but contains asserts used for SDK development which some customers may find too intrusive for daily use. Optimizations are turned off for this configuration.
* the *checked* build contains code to detect invalid parameters, API race conditions, and other incorrect uses of the API which might otherwise cause mysterious crashes or failures in simulation.
* the *profile* build omits the checks, but still has PVD and memory instrumentation.
* the *release* build is built for minimal footprint and maximum speed. It omits most checks and instrumentation.
Simulation works the same way in all of them, and all are compiled with high optimization levels (except debug configuration).
.. note:: We strongly recommend that you use the checked build as the primary configuration for day-to-day development and QA.
.. note:: PhysX libraries of different build configurations (e.g. the DEBUG version of PhysXVehicle and the CHECKED version of PhysXVisualDebuggerSDK) should never be mixed in an application because this will result a CRT conflict.
.. _Header files:
=================
Header Files
=================
To build your own PhysX app, you will need to add some include paths and libraries to your project makefile or IDE.
Users should specify the root "include/" and corresponding "bin/" folders in the additional include, and library directories of their build configuration respectively. There is a combined include header available as::
#include "PxPhysicsAPI.h"
This will include the entire PhysX API including core, extensions, vehicles, etc. It is also possible to include subsets of the SDK if preferred, for example::
#include "vehicle/PxVehicleSDK.h"
.. _Redistribution:
=================
Redistribution
=================
On the Windows platform, you need to redistribute some of our DLLs to end users as part of your application:
* PhysXCommon_*.dll - will always be needed.
* PhysX_*.dll - will always be needed.
* PhysXFoundation_*.dll - will always be needed.
* PhysXCooking_*.dll - you only need to bundle if your application cooks geometry data on the fly.
* PhysXGPU_*.dll - is only needed if your application runs some simulation on the GPU.
Where * is a platform specific suffix, e.g. 32 or 64. You will need one or the other depending on whether your application is built in 64 bit mode.
===============================
Customize CMake presets
===============================
It is possible to customize the CMake presets, which are stored in "physx/buildtools/presets/public/". Each XML file represents one preset, depending on the platform some CMake switches can be toggled.
General switches:
* PX_BUILDSNIPPETS - specify whether the PhysX Snippets should be added to the build configuration - default True
Windows switches:
* PX_GENERATE_STATIC_LIBRARIES - it is possible to switch the PhysX build to output static libs instead of DLLs - default False
* NV_USE_STATIC_WINCRT - sets static or dynamic runtime usage - default True
* NV_USE_DEBUG_WINCRT - specify whether debug CRT should be used - default True
* PX_FLOAT_POINT_PRECISE_MATH - it is possible to switch to precise math rather than fast math, this is beneficial especially for robotics projects, which do require higher precision - default False
Some switches like PX_GENERATE_STATIC_LIBRARIES may result in a need for additional defines to be set within your application. In this case you can include in your application PxConfig.h header, which is generated during generate projects.

View File

@ -0,0 +1,449 @@
.. _character:
---------------------
Character Controllers
---------------------
===============================
Introduction
===============================
The character controller (CCT) SDK is an external component built on top of the PhysX SDK, in a manner similar to PhysXExtensions.
CCTs can be implemented in a number of ways: the PhysX implementation in the CCT module is only one of them.
By nature, CCTs are often very game-specific, and they can have a number of unique features in each game. For example the character's bounding volume may be a capsule in one game, and an inverted pyramid in another. The CCT SDK does not attempt to provide a one-size-fits-all solution that would work out-of-the-box for all possible games. But it provides the basic features common to all CCTs: character control and character interactions. It is a default starting point for users, a strong base that one can build on, and later modify or customize if needed.
===============================
Kinematic Character Controller
===============================
The PhysX CCT is a kinematic controller. Traditionally, character controllers can be either kinematic or dynamic. A kinematic controller directly works with input displacement vectors (1st order control). A dynamic controller works with input velocities (2nd order control) or forces (3rd order control).
In the past, games did not use a 'real' physics engine like the PhysX SDK. But they still used a character controller to move a player in a level. These games, such as Quake or even Doom, had a dedicated, customized piece of code to implement collision detection and response, which was often the only piece of physics in the whole game. It actually had little physics, but a lot of carefully tweaked values to provide a good feeling while controlling the player. The particular behavior it implemented is often called the 'collide and slide' algorithm, and it has been 'tweaked for more than a decade'. The PhysX CCT module is an implementation of such an algorithm, providing a robust and well-known behavior for character control.
The main advantage of kinematic controllers is that they do not suffer from the following issues, which are typical for dynamic controllers:
* (lack of) continuous collision detection: typical physics engines use discrete collision checks, leading to the notorious 'tunneling effect' that has plagued various commercial & non-commercial physics packages for years. This leads to three main problems:
* the tunneling effect itself : if the character goes too fast it might tunnel through a wall
* as a consequence, the character's maximum velocity be limited (thus also limiting the game play possibilities)
* even if it does not tunnel, the character might jitter when pushed forward in a corner for example, because the physics engine keeps moving it back and forth to slightly different positions.
* No direct control: a rigid body is typically controlled with impulses or forces. It is usually not possible to move it directly to its final position: instead one must convert the delta position vector to impulses/forces, apply them, and hope that the character will end up at the desired position. This does not always work well, in particular when the physics engine uses an imperfect linear solver.
* Trouble with friction: when the character is standing on a ramp, it should not slide. So infinite friction is needed here. When the character is moving forward on that same ramp, it should not slow down. One does not need any friction here. Similarly, when the character is sliding against a wall, it should not slow down either. Thus, for a CCT, friction is usually either 0 or infinite. Unfortunately the friction model in a physics engine might not be perfect, and it is easy to end up with either a small amount of friction (the character slows down a tiny bit) or a very-large-but-not-infinite friction (the character slides very slowly on that ramp no matter how artificially big the friction parameters are). The conflicting requirements for ramps also mean that usually there is simply no way to perfectly model desired behavior.
* Trouble with restitution: typically, restitution should be avoided for CCTs. When the character moves fast and collides with a wall, it should not bounce away from it. When the character falls from a height and lands on the ground, flexing his legs, any bounce should be prevented. But once again, even when the restitution is exactly zero, a physics engine can nonetheless make the CCTs bounce a bit. This is not only related to the imperfect nature of the linear solver, it also has to do with how typical penetration-depth-based engines recover from overlap situations, sometimes applying excessive forces that separate the objects too much.
* Undesired jumps: characters must often stick to the ground, no matter what the physical behavior should be. For example characters in action games tend to move fast, at unrealistic speeds. When they reach the top of a ramp, the physics engine often makes them jump a bit, in the same way a fast car would jump in the streets of San Francisco. But that is often not the desired behavior: instead the character should often stick to the ground regardless of its current velocity. This is sometimes implemented using fixed joints, but this is an unnecessarily complex solution to a problem that is easily prevented with kinematic controllers.
* Undesired rotations: a typical character is always standing up and never rotating. However physics engines often have poor support for that sort of constraints, and a great deal of effort is often put into preventing a capsule around the character from falling (it should always stands up on its tip). This is often implemented using artificial joints, and the resulting system is neither very robust nor very fast.
To summarize, a lot of effort can be spent on tweaking and disabling the physics engine's features simply to emulate what is otherwise a much less complex piece of custom code. It is natural to instead keep using that simple piece of custom code.
===============================
Creating a character controller
===============================
First, create a controller manager somewhere in your application. This object keeps track of all created controllers and allows characters from the same manager to interact with each other. Create the manager using the *PxCreateControllerManager* function::
PxScene* scene; // Previously created scene
PxControllerManager* manager = PxCreateControllerManager(*scene);
Then, create one controller for each character in the game. At the time of writing only boxes (*PxBoxController*) and capsules (*PxCapsuleController*) are supported. A capsule controller for example, is created this way::
PxCapsuleControllerDesc desc;
...
<fill the descriptor here>
...
PxController* c = manager->createController(desc);
The manager class will keep track of all created controllers. They can be retrieved at any time using the following functions::
PxU32 PxControllerManager::getNbControllers() const = 0;
PxController* PxControllerManager::getController(PxU32 index) = 0;
To release a character controller, simply call its release function::
void PxController::release() = 0;
To release all created character controllers at once, either release the manager object itself, or use the following function if you intend to keep using the manager::
void PxControllerManager::purgeControllers() = 0;
The creation of a controller manager and its subsequent controllers is illustrated in SampleBridges.
===============================
Overlap Recovery Module
===============================
Ideally, character should not be created in an initial overlap state, i.e. they should be created in a position where they do not overlap the surrounding geometry. The various PxScene overlap functions can be used to check the desired volume of space is empty, prior to creating the character. By default the CCT module does not check for overlaps itself, and creating a character that initially overlaps the world's static geometry can have undesired and undefined behavior - like the character going through the ground for example.
However, the overlap recovery module can be used to automatically correct the character's initial position. As long as the amount of overlap is reasonable, the recovery module should be able to relocate the character to a proper, collision-free position.
The overlap recovery module can be useful in several other situations. There are three main cases:
* when the CCT is directly spawned or teleported in another object
* when the CCT algorithm fails due to limited FPU accuracy
* when the "up vector" is modified, making the rotated CCT shape overlap surrounding objects
When activated, the CCT module will automatically try to resolve the penetration, and move the CCT to a safe place where it does not overlap other objects anymore. This only concerns static objects, dynamic objects are ignored by this module.
Enable or disable the overlap recovery module with this function::
void PxControllerManager::setOverlapRecoveryModule(bool flag);
By default the character controllers use precise sweep tests, whose accuracy is usually enough to avoid all penetration - provided the contact offset is not too small. Thus, in most cases the overlap recovery module is not needed. When it is used though, the sweep tests can be switched to less accurate but potentially faster versions, using the following function::
void PxControllerManager::setPreciseSweeps(bool flag);
===============================
Character Volume
===============================
The character uses a bounding volume that is independent from already existing shapes in the SDK. We currently support two different shapes around the character:
* An AABB, defined by a center position and an extents vector. The AABB does not rotate. It always has a fixed rotation even when the player is (visually) rotating. This avoids getting stuck in places too tight to let the AABB rotate.
* A capsule, defined by a center position, a vertical height and a radius. The height is the distance between the two sphere centers at the end of the capsule. The capsule has a better behavior when climbing stairs, for example. It is the recommended default choice.
.. image:: ../images/cctCapsule.png
Note: versions prior to 2.3 also supported a sphere. This has been removed since the PxCapsuleController is more robust and provides the same functionality (zero length capsule).
A small skin is maintained around the character's volume, to avoid numerical issues that would otherwise happen when the character touches other shapes. The size of this skin is user-defined. When rendering the character's volume for debug purpose, remember to expand the volume by the size of this skin to get accurate debug visualization. This skin is defined in *PxControllerDesc::contactOffset* and later available through the *PxController::getContactOffset()* function.
===============================
Volume Update
===============================
Sometimes it is useful to change the size of the character's volume at runtime. For example if the character can crouch, it might be required to reduce the height of its bounding volume so that it can then move to places he could not reach otherwise.
For the box controller, the related functions are::
bool PxBoxController::setHalfHeight(PxF32 halfHeight) = 0;
bool PxBoxController::setHalfSideExtent(PxF32 halfSideExtent) = 0;
bool PxBoxController::setHalfForwardExtent(PxF32 halfForwardExtent) = 0;
And for the capsule controller::
bool PxCapsuleController::setRadius(PxF32 radius) = 0;
bool PxCapsuleController::setHeight(PxF32 height) = 0;
Changing the size of a controller using the above functions does not actually change its position. So if the character is standing on the ground (touching it), and its height is suddenly reduced without updating its position, the character will end up levitating above the ground for a few frames until gravity makes it fall and touch the ground again. This happens because the controllers positions are located at the center of the shapes, rather than the bottom. Thus, to modify a controller's height and preserve its bottom position, one must change both the height and position of a controller. The following helper function does that automatically::
void PxController::resize(PxF32 height) = 0;
.. image:: ../images/cctResize.png
It is important to note that volumes are directly modified without any extra tests, and thus it might happen that the resulting volume overlaps some geometry nearby. For example when resizing the character to leave a crouch pose, i.e. when the size of the character is *increased*, it is important to first check that the character can indeed 'stand up': the volume of space above the character must be empty (collision free). It is recommended to use the various PxScene overlap queries for this purpose::
bool PxScene::overlap(...) = 0;
Updating the character's volume at runtime to implement a 'crouch' motion is illustrated in SampleNorthPole. Using overlap queries to leave the crouch pose is done in the *SampleNorthPole::tryStandup()* function.
===============================
Moving a Character Controller
===============================
The heart of the CCT algorithm is the function that actually moves characters around::
PxControllerCollisionFlags collisionFlags =
PxController::move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime,
const PxControllerFilters& filters, const PxObstacleContext* obstacles=NULL);
*disp* is the displacement vector for current frame. It is typically a combination of vertical motion due to gravity and lateral motion when your character is moving. Note that users are responsible for applying gravity to characters here.
*minDist* is a minimal length used to stop the recursive displacement algorithm early when remaining distance to travel goes below this limit.
*elapsedTime* is the amount of time that passed since the last call to the move function.
*filters* are filtering parameters similar to the ones used in the SDK. Use these to control what the character should collide with.
*obstacles* are optional additional obstacle objects with which the character should collide. Those objects are fully controlled by users and do not need to have counterpart SDK objects. Note that touched obstacles are cached, meaning that the cache needs to be invalidated if the collection of obstacles changes.
*collisionFlags* is a bit mask returned to users to define collision events that happened during the move. This is a combination of PxControllerCollisionFlag flags. It can be used to trigger various character animations. For example your character might be falling while playing a falling idle animation, and you might start the land animation as soon as PxControllerCollisionFlag::eCOLLISION_DOWN is returned.
It is important to understand the difference between *PxController::move* and *PxController::setPosition*. The *PxController::move* function is the core of the CCT module. This is where the aforementioned 'collide-and-slide' algorithm takes place. So the function will start from the CCT's current position, and use sweep tests to attempt to move in the required direction. If obstacles are found, it may make the CCT slide smoothly against them. Or the CCT can get blocked against a wall: the result of the move call depends on the surrounding geometry. On the contrary, *PxController::setPosition* is a simple 'teleport' function that will move the CCT to desired position no matter what, regardless of where the CCT starts from, regardless of surrounding geometry, and even if the required position is in the middle of another object.
Both *PxController::move* and *PxController::setPosition* are demonstrated in SampleBridges.
===============================
Graphics Update
===============================
Each frame, after *PxController::move* calls, graphics object must be kept in sync with the new CCT positions. Controllers' positions can be accessed using::
const PxExtendedVec3& PxController::getPosition() const;
This function returns the position from the center of the collision shape, since this is what is used internally both within the PhysX SDK and by usual graphics APIs. Retrieving this position and passing it to the renderer is illustrated in SampleBridges. Note that the position uses double-precision, to make the CCT module work well with large worlds. Also note that a controller never rotates so you can only access its position.
Alternative helper functions are provided to work using the character's bottom position, a.k.a. the foot position::
const PxExtendedVec3& PxController::getFootPosition() const;
bool PxController::setFootPosition(const PxExtendedVec3& position);
Note that the foot position takes the contact offset into account.
.. image:: ../images/cctContactOffset.png
===============================
Auto Stepping
===============================
Without auto-stepping it is easy for a box-controlled character to get stuck against slight elevations of the ground mesh. In the following picture the small step would stop the character completely. It feels unnatural because in the real world a character would just cross this small obstacle without thinking about it.
.. image:: ../images/cctStepOffset0.png
This is what auto-stepping enables us to do. Without any intervention from the player (i.e. without them thinking about it) the box correctly steps above the minor obstacle.
.. image:: ../images/cctStepOffset1.png
However, if the obstacle is too big, i.e. its height is greater than the *stepOffset* parameter, the controller cannot climb automatically, and the character gets stuck (correctly this time):
.. image:: ../images/cctStepOffset2.png
'Climbing' (over this bigger obstacle, for example) may also be implemented in the future, as an extension of auto-stepping. The step offset is defined in *PxControllerDesc::stepOffset* and later available through the *PxController::getStepOffset()* function.
Generally speaking, the step offset should be kept as small as possible.
===============================
Climbing Mode
===============================
The auto-stepping feature was originally intended for box controllers, which are easily blocked by small obstacles on the ground. Capsule controllers, thanks to their rounded nature, do not necessarily need the feature.
Even with a step offset of 0.0, capsules are able to go over small obstacles since their rounded bottom produces an upward motion after colliding with a small obstacle.
Capsules with a non-zero step-offset can go over obstacles higher than the step offset, because of the combined effect of the auto-stepping feature and their rounded shape. In this case the largest altitude a capsule can climb over is difficult to predict, as it depends on the auto-step value, the capsule's radius, and even the magnitude of the displacement vector.
This is why there are two different climbing modes for capsules:
* *PxCapsuleClimbingMode::eEASY*: in this mode, capsules are not constrained by the step offset value. They can potentially climb over obstacles higher than this value.
* *PxCapsuleClimbingMode::eCONSTRAINED*: in this mode, an attempt is made to make sure the capsule can not climb over obstacles higher than the step offset.
===============================
Up Vector
===============================
In order to implement the auto-stepping feature, the SDK needs to know about the 'up' vector. The up vector is defined in *PxControllerDesc::upDirection* and later available through the *PxController::getUpDirection()* function.
The up vector does not need to be axis-aligned. It can be arbitrary, modified each frame using the *PxController::setUpDirection()* function, allowing the character to navigate on spherical worlds. This is demonstrated in SampleCustomGravity.
Modifying the up vector changes the way the CCT library sees character volumes. For example a capsule is defined by a *PxCapsuleControllerDesc::height*, which is the 'vertical height' along the up vector. Thus, changing the up vector effectively rotates the capsule from the point of view of the library.
The modification happens immediately, without tests to validate that the character does not overlap nearby geometry. It is then possible for the character to be penetrating some geometry right after the call. Using the overlap recovery module is recommended to solve these issues.
.. image:: ../images/cctUpVector.png
In the above picture the capsule on the left uses a vertical up vector and does not collide with the surrounding geometry. On the right the up vector has been set to 45 degrees, and the capsule now penetrates the wall nearby. For most applications the up vector will be constant, and the same for all characters. These issues will only appear for characters navigating in spherical worlds (e.g. planetoids, etc).
================================
Walkable Parts & Invisible Walls
================================
By default the characters can move everywhere. This may not always be a good thing. In particular, it is often desired to prevent walking on polygons whose slope is steep. The SDK can do this automatically thanks to a user-defined slope limit. All polygons whose slope is higher than the limit slope will be marked as non walk-able, and the SDK will not let characters go there.
Two modes are available to define what happens when touching a non walk-able part. The desired mode is selected with the *PxControllerDesc::nonWalkableMode* enum:
* *PxControllerNonWalkableMode::ePREVENT_CLIMBING* prevents the character from moving up a slope, but does not move the character otherwise. The character will still be able to walk laterally on these polygons, and to move down their slope.
* *PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING* not only prevents the character from moving up non walk-able slopes but also forces it to slide down those slopes.
The slope limit is defined in *PxControllerDesc::slopeLimit* and later available through the *PxController::getSlopeLimit()* function. The limit is expressed as the cosine of desired limit angle. For example this uses a slope limit of 45 degrees::
slopeLimit = cosf(PxMath::degToRad(45.0f));
Using slopeLimit = 0.0f automatically disables the feature (i.e. characters can go everywhere).
This feature is not always needed. A common strategy is to disable it and place invisible walls in the level, to restrict player's movements. The character module can also create those walls for you, if *PxControllerDesc::invisibleWallHeight* is non-zero. In this case the library creates those extra triangles on the fly, and that parameter controls their height (extruded in the user-defined up direction). A common problem is that those invisible walls are only created when non-walkable triangles are found. It is possible for a jumping character to go over them, if its bounding volume is too small and does not collide with the non-walkable triangles below him. The *PxControllerDesc::maxJumpHeight* parameter addresses this issue, by extending the size of the bounding volume downward. That way all potentially non-walkable triangles are properly returned by the collision queries, and invisible walls are properly created - preventing the character from jumping on them.
A known limitation is that the slope limit mechanism is currently only enabled against static objects. It is not enabled against dynamic objects, and in particular against kinematic objects. It is also not supported for static spheres or static capsules.
===============================
Obstacle Objects
===============================
Sometimes it is convenient to create additional obstacles for the CCT to collide with, without creating an actual SDK object. This is useful in a number of situations. For example:
* the obstacles might only exist for a couple of frames, in which case creating and deleting SDK objects is not always efficient.
* the obstacles might only exist for stopping the characters, not the SDK's dynamic objects. This would be for example invisible walls around geometry, that only the characters should collide with. In this case it may not be very efficient to create the invisible walls as SDK objects, since their interactions would then have to be filtered out for everything except the characters. It is probably more efficient to create those additional invisible walls as external obstacles, that only characters can interact with.
* the obstacles might be dynamic and updated with a variable timestep, while the SDK uses a fixed timestep. This could be for example a moving platform on which the characters can stand.
At the time of writing the character controller supports box and capsule *PxObstacle* objects, namely *PxBoxObstacle* and *PxCapsuleObstacle*. To create those, first create a *PxObstacleContext* object using the following function::
PxObstacleContext* PxControllerManager::createObstacleContext() = 0;
Then manage obstacles with::
ObstacleHandle PxObstacleContext::addObstacle(const PxObstacle& obstacle) = 0;
bool PxObstacleContext::removeObstacle(ObstacleHandle handle) = 0;
bool PxObstacleContext::updateObstacle(ObstacleHandle handle, const PxObstacle& obstacle) = 0;
Typically *updateObstacle* is called right before the controllers' *move* calls.
Using obstacles for moving platforms is illustrated in SampleBridges, when PLATFORMS_AS_OBSTACLES is defined in SampleBridgesSettings.h.
===============================
Hit Callback
===============================
The *PxUserControllerHitReport* object is used to retrieve some information about controller's evolution. In particular, it is called when a character hits a shape, another character, or a user-defined obstacle object.
When the character hits a shape, the *PxUserControllerHitReport::onShapeHit* callback is invoked - for both static and dynamic shapes. Various impact parameters are sent to the callback, and they can then be used to do various things like playing sounds, rendering trails, applying forces, and so on. The use of *PxUserControllerHitReport::onShapeHit* is illustrated in SampleBridges. Note that this callback will only be called in response to a character moving against a shape. It will *not* be called if a (dynamic) shape collides against an otherwise non-moving character. In other words, this will only be called during a *PxController::move* call.
When the character hits another character, i.e. another object controlled by a character controller, the *PxUserControllerHitReport::onControllerHit* callback is invoked. This happens when the player collides with an NPC, for example.
Finally, when the character hits a user-defined obstacle the *PxUserControllerHitReport::onObstacleHit* callback is invoked.
===============================
Behavior Callback
===============================
The *PxControllerBehaviorCallback* object is used to customize the character's behavior after touching a *PxShape*, a *PxController*, or a *PxObstacle*. This is done using the following functions::
PxControllerBehaviorFlags PxControllerBehaviorCallback::getBehaviorFlags
(const PxShape& shape, const PxActor& actor) = 0;
PxControllerBehaviorFlags PxControllerBehaviorCallback::getBehaviorFlags
(const PxController& controller) = 0;
PxControllerBehaviorFlags PxControllerBehaviorCallback::getBehaviorFlags
(const PxObstacle& obstacle) = 0;
At the time of writing the following returned flags are supported:
*PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT* defines if the character can effectively travel with the object it is standing on. For example a character standing on a dynamic bridge should follow the motion of the PxShape it is standing on (e.g. in SampleBridges). But it should not be the case if the character stands on, say a PxShape bottle rolling on the ground (e.g. the snowballs in SampleNorthPole). Note that this flag only controls the horizontal displacement communicated from an object to the controller. The vertical motion is something slightly different, as many factors contribute to this displacement: the *step offset* used to automatically walk over small bumps, the vertical motion of underlying dynamic actors like e.g. the bridges in SampleBridges, which should probably always been taken into account, etc.
*PxControllerBehaviorFlag::eCCT_SLIDE* defines if the character should slide or not when standing on the object. This can be used as an alternative to the previously discussed slope limit feature, to define non walk-able objects rather than non-walkable parts. It can also be used to make a capsule character fall off a platform's edge automatically, when the center of the capsule crosses the platform's edge.
*PxControllerBehaviorFlag::eCCT_USER_DEFINED_RIDE* simply disables all built-in code related to controllers riding on objects. This can be useful to get the legacy behavior back, which can sometimes be necessary when porting to PhysX 3.x a piece of code built around the PhysX 2.x character controller. The flag simply skips the new codepath, and lets users deal with this particular problem in their own application, outside of the CCT library.
The behavior callback is demonstrated in SampleBridges.
=============================================
Character Interactions: CCT-vs-dynamic actors
=============================================
It is tempting to let the physics engine push dynamic objects by applying forces at contact points. However it is often not a very convincing solution.
The bounding volumes around characters are artificial (boxes, capsules, etc) and invisible, so the forces computed by the physics engine between a bounding volume and its surrounding objects will not be realistic anyway. They will not properly model the interaction between an actual character and these objects. If the bounding volume is large compared to the visible character, maybe to make sure that its limbs never penetrate the static geometry around, the dynamic objects will start moving (pushed by a bounding volume) before the actual character touches them - making it look like the character is surrounded by some kind of force field.
Additionally, the pushing effect should not change when switching from a box controller to a capsule controller. It should ideally be independent from the bounding volume.
Pushing effects are usually dictated by gameplay, and sometimes require extra code like inverse kinematic solvers, which are outside of the scope of the CCT module. Even for simple use cases, it is for example difficult to push a dynamic box forward with a capsule controller: since the capsule never hits the box exactly in the middle, applied force tends to rotate the box - even if gameplay dictates that it should move in a straight line.
Thus, this is an area where the CCT module should best be coupled to specific game code, to implement a specific solution for a specific game. This coupling can be done in many different ways. For simple use cases it is enough to use the *PxUserControllerHitReport::onShapeHit* callback to apply artificial forces to surrounding dynamic objects. Such an approach is illustrated in SampleBridges.
Note that the character controller does use overlap queries to determine which shapes are nearby. Thus, SDK shapes that should interact with the characters (e.g. the objects that the character should push) must have the PxShapeFlag::eSCENE_QUERY_SHAPE flag set to true, otherwise the CCT will not detect them and characters will move right through these shapes.
==================================
Character Interactions: CCT-vs-CCT
==================================
The interactions between CCTs (i.e. between two PxController objects) are limited, since in this case both objects are effectively kinematic objects. In other words their motion should be fully controlled by users, and neither the PhysX SDK nor the CCT module should be allowed to move them.
The *PxControllerFilterCallback* object is used to define basic interactions between characters. Its *PxControllerFilterCallback::filter* function can be used to determine if two PxController objects should collide at all with each other::
bool PxControllerFilterCallback::filter(const PxController& a, const PxController& b) = 0;
To make CCTs always collide-and-slide against each other, simply return true.
To make CCTs always move freely through each other, simply return false.
Otherwise, customized and maybe gameplay-driven filtering rules can be implemented in this callback. Sometimes the filtering changes at runtime, and two characters might be allowed to go through each other only for a limited amount of time. When that limited time expires, the characters may be left in an overlapping state until they separate and move again towards each other. To automatically separate overlapping characters, the following function can be used::
void PxControllerManager::computeInteractions(PxF32 elapsedTime,
PxControllerFilterCallback* cctFilterCb=NULL) = 0;
This function is an optional helper to properly resolve overlaps between characters. It should be called once per frame, before the *PxController::move* calls. The function will not move the characters directly, but it will compute overlap information for each character that will be used in the next *PxController::move* call.
===============================
Hidden Kinematic Actors
===============================
The CCT library creates a kinematic actor under the hood, for each controlled character. When invoking the *PxController::move* function, the underlying hidden kinematic *PxActor* is also updated to reflect the CCT position in the physics scene.
Users should be aware of these hidden entities, since the total number of actors in the scene will be higher than the number they created themselves. Additionally they might get back these potentially confusing unknown actors from scene-level collision queries.
One possible strategy is to retrieve the controllers' kinematic actors using the following function::
PxRigidDynamic* PxController::getActor() const;
Then mark these actors with a special tag, using the *PxRigidDynamic::userData* field. That way the CCT actors can easily be identified (and possibly ignored) in collision queries or contact reports.
===============================
Time Stepping
===============================
Actors used internally by the CCT library follow the same rules as any other PhysX objects. In particular, they are updated using fixed or variable timesteps. This can be troublesome because the PxController objects are otherwise often updated using variable time steps (typically using the elapsed time between two rendering frames).
Thus the PxController objects (using variable time steps) may not always be perfectly in sync with their kinematic actors (using fixed time steps). This phenomenon is shown in SampleBridges.
=====================================
Invalidating Internal Geometry Caches
=====================================
The CCT library caches the geometry around each character, in order to speed up collision queries. The temporal bounding box for a character is an AABB around the character's motion (it contains the character's volume at both its start and end position). The cached volume of space is determined by the size of the character's temporal bounding box, multiplied by a constant factor. This constant factor is defined for each character by *PxControllerDesc::volumeGrowth*. Each time a character moves, its temporal bounding box is tested against the cached volume of space. If the motion is fully contained within that volume of space, the contents of the cache are reused instead of regenerated through PxScene-level queries.
.. image:: ../images/cctCacheBounds.png
In PhysX 3.3 and above, those caches should be automatically invalidated when a cached object gets updated or removed. However it is also possible to manually flush those caches using the following function::
void PxController::invalidateCache();
Prior to deciding if a character will travel with the motion of an object that is touching the character, a number of tests are automatically performed to decide if the cached touched object remains valid. These automatic validity tests mean that in the following cases it is not strictly necessary to invalidate the cache:
* If the shapes actor is released
* If the shape is released
* If the shape is removed from an actor
* If an actor is removed from scene or moved to another one
* If the shapes scene query flag changed
* If the filtering parameters of the shape or the scene have changed.
If a cached touched object is no longer actually touching the character and it is desired that the character no longer travels with the motion of that cached object then it is necessary to invalidate the cache. This holds true if the pair have separated as a consequence of an updated global pose or modified geometry.
===============================
Runtime Tessellation
===============================
The CCT library is quite robust, but sometimes suffers from FPU accuracy issues when a character collides against large triangles. This can lead to characters not smoothly sliding against those triangles, or even penetrating them. One way to effectively solve these problems is to tessellate the large triangles at runtime, replacing them on-the-fly with a collection of smaller triangles. The library supports a built-in tessellation feature, enabled with this function::
void PxControllerManager::setTessellation(bool flag, float maxEdgeLength);
The first parameter enables or disables the feature. The second parameter defines the maximum allowed edge length for a triangle, before it gets tessellated. Obviously, a smaller edge length leads to more triangles being created at runtime, and the more triangles get generated, the slower it is to collide against them.
It is thus recommended to disable the feature at first, and only enable it if experiencing collision problems. When enabling the feature, it is recommended to use the largest possible *maxEdgeLength* that does fix encountered problems.
.. image:: ../images/cctTessellation.png
In the screenshot, the large magenta triangle on which the character is standing is replaced with the smaller green triangles by the tessellation module. The internal geometry cache is represented by the blue bounding box. Note that only the green triangles touching this volume of space are kept. Thus, the exact number of triangles produced by the tessellation code depends on both the *maxEdgeLength* parameter and the *PxControllerDesc::volumeGrowth* parameter.
===============================
Troubleshooting
===============================
This section introduces common solutions to common problems with the CCT library.
Character goes through walls in rare cases
------------------------------------------
1. Try increasing *PxControllerDesc::contactOffset*.
2. Try enabling runtime tessellation with *PxControllerManager::setTessellation*. Start with a small *maxEdgeLength* first, to see if it solves the problem. Then increase that value as much as possible.
3. Try enabling overlap recovery module with *PxControllerManager::setOverlapRecoveryModule*.
Tessellation performance issue
------------------------------
1. Try fine-tuning the *maxEdgeLength* parameter. Use the largest possible value that still prevents tunneling issues.
2. Try reducing *PxControllerDesc::volumeGrowth*.
The capsule controller manages to climb over obstacles higher than the step offset value
----------------------------------------------------------------------------------------
1. Try using *PxCapsuleClimbingMode::eCONSTRAINED*.

View File

@ -0,0 +1,41 @@
.. _debugvisualization:
-------------------
Debug Visualization
-------------------
============
Introduction
============
With the PhysX Visual Debugger (see :ref:`PhysXVisualDebugger`), NVIDIA offers a tool to record information about simulated PhysX scenes and visualize that information in a remote viewer application. However, sometimes it is preferable to integrate visual debug information directly into the application's view. For that purpose, PhysX provides an interface to extract visual debug information as a set of basic rendering primitives, that is, points, lines, triangles and text. These primitives can then be rendered and overlayed with the application render objects.
=====
Usage
=====
To enable debug visualization, the global visualization scale has to be set to a positive value first::
PxScene* scene = ...
scene->setVisualizationParameter(PxVisualizationParameter::eSCALE, 1.0f);
Then the individual properties that should be visualized can be enabled using, again, a positive value::
scene->setVisualizationParameter(PxVisualizationParameter::eACTOR_AXES, 2.0f);
In the example, the actor world axes will be visualized. The scale used for visualization will be the product of the global scale (1.0 in this example) and the property scale (2.0 in this example). Please note that for some properties the scale factor does not apply: shape geometry, for example, will not be scaled since the size is defined by the user application. Furthermore, for some objects, visualization has to be enabled explicitly on the corresponding object instances too (see *PxActorFlag::eVISUALIZATION*, *PxShapeFlag::eVISUALIZATION*, ...).
After a simulation step, the visualization primitives can then be extracted as follows::
const PxRenderBuffer& rb = scene->getRenderBuffer();
for(PxU32 i=0; i < rb.getNbLines(); i++)
{
const PxDebugLine& line = rb.getLines()[i];
// render the line
}
.. note:: Do not extract render primitives while the simulation is running.
The amount of debug visualization data might be too vast to create efficiently for large scenes. In cases where only a localized area is of interest, there is the option to use a culling box for debug visualization via *PxScene::setVisualizationCullingBox()*.
Note that simply enabling debug visualization (PxVisualizationParameter::eSCALE) can have a significant performance impact, even when all the other individual visualization flags are disabled. Thus, make sure debug visualization is disabled in your final/release builds.

View File

@ -0,0 +1,340 @@
.. _extendedSerialization:
-------------------------
Extending Serialization
-------------------------
====================================================
Introduction
====================================================
The PhysX serialization system (:ref:`Serialization`) is extendable to custom types. If an application were to require a new joint type, for example, the serialization system could be extended to add support for serialization of that new joint type.
The following document contains some recipes and example code that show how PhysX serialization may be extended to custom types. It doesn't cover all aspects of the extension mechanisms. It is therefore advisable to look into the following implementation example for more details:
* PhysXVehicle library (PhysXVehicle/src)
====================================================
Overview
====================================================
Both binary and RepX serialization can be extended for custom types. To prepare the custom type for serialization it must first inherit from *PxBase*. This allows instances of the custom type to be added to a *PxCollection*, which is a pre-requisite for serialization. The core serialization functionality needs to be provided by implementing the *PxSerializer* interface. The template *PxSerializerDefaultAdapter* provides a default implementation and can be specialized for the custom type as required. In order to support RepX serialization an additional *PxRepXSerializer* interface needs to be implemented. RepX serialization relies on automatic code generation using clang. Scripts to run the code generation for the examples can be found in (Tools/PhysXMetaDataGenerator).
.. _BinarySerializationOfCustomClasses:
====================================================
Binary Serialization of Custom Classes
====================================================
Serialization and deserialization of a custom class can be achieved with the following steps:
1. Define a *PxConcreteType* and type info for the custom class. Make sure its type value is unique.
2. The custom class needs to inherit from *PxBase* and implement its interface.
3. Instance PxSerializerDefaultAdapter<T> and implement specialized methods where necessary.
4. If retargeting to other platforms is needed, implement *getBinaryMetaData()*.
5. Register the adapter and metadata, see *PX_NEW_SERIALIZER_ADAPTER* , *PxSerializationRegistry::registerSerializer* and *PxSerializationRegistry::registerBinaryMetaDataCallback*. Note that serializers also need to be unregistered before *PxSerializationRegistry::release* is called. The application is responsible for custom type serializer allocation and deallocation.
For pointer members the following needs to be done (Note that reference members are currently not supported):
6. Implement *PxSerializer::requires*. It should enumerate *PxBase* objects on which the object depends for deserialization. See :ref:`Requires`.
7. For a member pointer to another *PxBase* object, register the reference in the implementation of *PxSerializer::registerReferences*. The implementation of *PxSerializer::requires* may be used to help with this.
8. Resolve references in the implementation of *PxSerializer::createObject* using *PxDeserializationContext::resolveReference, translatePxBase*.
9. Make sure that *PxSerializer::isSubordinate* returns whether the object can only be serialized along with an owner object. See :ref:`Subordinate`.
10. Export non *PxBase* data by implementing *PxSerializer::exportExtraData* using *PxSerializationContext::writeData, alignData*.
11. Import non *PxBase* data in the implementation of *PxSerializer::createObject* using *PxDeserializationContext::readExtraData, alignExtraData*.
.. note:: In checked builds (PX_CHECKED defined as 1) metadata definitions are verified against serialized data. If metadata definitions are missing warnings are output on the error stream during re-targeting (*PxBinaryConverter::convert*). To avoid false warnings, all unused memory in custom serialized class instances should be marked with a 0xcd pattern. This can be done with *Cm::markSerializedMem* from CmUtils.h.
.. note:: The memory of a deserialized class instance should not be deallocated. The memory is embedded in the memory buffer containing the serialized data. The flag PxBaseFlag::eOWNS_MEMORY can used to decide whether the object memory needs be deallocated or not.
Example for a custom class::
#include "extensions/PxSerialization.h"
#include "common/PxTypeInfo.h"
#include "common/PxMetaData.h"
#include "common/PxSerializer.h"
#include "common/PxSerialFramework.h
using namespace physx;
const PxType customClassType = PxConcreteType::eFIRST_USER_EXTENSION;
PX_DEFINE_TYPEINFO(CustomClass, customClassType);
class CustomClass : public PxBase
{
friend class PxSerializerDefaultAdapter<CustomClass>;
public:
// constructor setting up PxBase object
CustomClass()
: PxBase(customClassType, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE)
{}
// constructor called on deserialization
CustomClass(PxBaseFlags baseFlags) : PxBase(baseFlags) {}
virtual ~CustomClass() {}
//PxBase
virtual const char* getConcreteTypeName() const { return "CustomClass"; }
virtual bool isKindOf(const char* name) const
{
return !strcmp("CustomClass", name) || PxBase::isKindOf(name);
}
//~PxBase
//PxSerializationRegistry::registerBinaryMetaDataCallback
static void getBinaryMetaData(PxOutputStream& stream)
{
PX_DEF_BIN_METADATA_VCLASS(stream, CustomClass)
PX_DEF_BIN_METADATA_BASE_CLASS(stream, CustomClass, PxBase)
PX_DEF_BIN_METADATA_ITEM(stream, CustomClass, PxRigidDynamic, mActor,
PxMetaDataFlag::ePTR)
PX_DEF_BIN_METADATA_ITEM(stream, CustomClass, char, mBuf, PxMetaDataFlag::ePTR)
PX_DEF_BIN_METADATA_ITEM(stream, CustomClass, PxU32, mSize, 0)
PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, CustomClass, char, mBuf, mSize, 0, 0)
}
//~PxSerializationRegistry::registerBinaryMetaDataCallback
private:
PxRigidDynamic* mActor; //add in requires
char* mBuf; //extra data
PxU32 mSize; //size of mBuf
};
//PxSerializerDefaultAdapter
template<>
void PxSerializerDefaultAdapter<CustomClass>::requires(PxBase& obj,
PxProcessPxBaseCallback& c) const
{
CustomClass* custom = obj.is<CustomClass>();
PX_ASSERT(custom);
c.process(*custom->mActor);
}
template<>
void PxSerializerDefaultAdapter<CustomClass>::registerReferences(PxBase& obj,
PxSerializationContext& s) const
{
CustomClass* custom = obj.is<CustomClass>();
PX_ASSERT(custom);
s.registerReference(obj, PX_SERIAL_REF_KIND_PXBASE, size_t(&obj));
s.registerReference(*custom->mActor, PX_SERIAL_REF_KIND_PXBASE, size_t(custom->mActor));
}
template<>
void PxSerializerDefaultAdapter<CustomClass>::exportExtraData(PxBase& obj,
PxSerializationContext& s) const
{
CustomClass* custom = obj.is<CustomClass>();
PX_ASSERT(custom);
s.alignData(PX_SERIAL_ALIGN);
s.writeData(custom->mBuf, custom->mSize);
}
template<>
PxBase* PxSerializerDefaultAdapter<CustomClass>::createObject(PxU8*& address,
PxDeserializationContext& context)
const
{
CustomClass* custom = new (address) CustomClass(PxBaseFlag::eIS_RELEASABLE);
address += sizeof(CustomClass);
// resolve references
context.translatePtr(custom->mActor);
// import extra data
custom->mBuf = context.readExtraData<char*, PX_SERIAL_ALIGN>();
// return deserialized object
return custom;
}
//~PxSerializerDefaultAdapter
void registerCustomClassBinarySerializer(PxSerializationRegistry& registry)
{
registry.registerSerializer(customClassType, PX_NEW_SERIALIZER_ADAPTER(CustomClass));
registry.registerBinaryMetaDataCallback(CustomClass::getBinaryMetaData);
}
void unregisterCustomClassBinarySerializer(PxSerializationRegistry& registry)
{
PX_DELETE_SERIALIZER_ADAPTER(registry.unregisterSerializer(customClassType));
}
====================================================
RepX Serialization of Custom Classes
====================================================
Serialization and deserialization of a custom class can be achieved with the following steps:
1. Perform the first three steps from :ref:`BinarySerializationOfCustomClasses`. Methods in *PxSerializer* and *PxSerializerDefaultAdapter<T>* required exclusively for binary serialization may be left empty.
2. Create a custom RepX serializer that implements the *PxRepXSerializer* interface. *PxRepXSerializer* is used to create an object from the xml file and write an object to the xml file. The class *RepXSerializerImpl* can be used to inherit default implementations of some methods.
3. Register the general serializer adapter and the RepX serializer. Note that custom type serializers also need to be unregistered and deallocated.
4. RepX supports automatic reading and writing of class properties. To achieve this, clang has to be used to generate corresponding metadata: :ref:`physxmetadata`.
Example for a custom class::
#include "SnRepXSerializerImpl.h"
const PxType customClassType = PxConcreteType::eFIRST_USER_EXTENSION;
PX_DEFINE_TYPEINFO(CustomClass, customClassType);
struct CustomClassRepXSerializer : public RepXSerializerImpl<CustomClass>
{
CustomClassRepXSerializer(PxAllocatorCallback& inCallback)
: RepXSerializerImpl<CustomClass>(inCallback)
{}
virtual PxRepXObject fileToObject(XmlReader& inReader, XmlMemoryAllocator& inAllocator,
PxRepXInstantiationArgs& inArgs, PxCollection* inCollection)
{
// factory for CustomClass instance provided by application
CustomClass* object = createCustomClass();
// when using the PhysX API metadata system readAllProperties(...) can be used to read
// all properties automatically
readAllProperties(inArgs, inReader, object, inAllocator, *inCollection);
return PxCreateRepXObject(object);
}
virtual void objectToFileImpl(const CustomClass* obj, PxCollection* inCollection,
XmlWriter& inWriter, MemoryBuffer& inTempBuffer,
PxRepXInstantiationArgs&)
{
// when using the PhysX API metadata system writeAllProperties(...) can be used to save
// all properties automatically
writeAllProperties(obj, inWriter, inTempBuffer, *inCollection);
}
// this can return NULL if fileToObject(...) is overwritten with a custom implementation.
virtual CustomClass* allocateObject(PxRepXInstantiationArgs&) { return NULL; }
};
void registerCustomClassRepXSerializer(PxSerializationRegistry& registry)
{
registry.registerSerializer(customClassType,
PX_NEW_SERIALIZER_ADAPTER(CustomClass));
registry.registerRepXSerializer(customClassType,
PX_NEW_REPX_SERIALIZER<CustomClassRepXSerializer>));
}
void unregisterCustomClassRepXSerializer(PxSerializationRegistry& registry)
{
PX_DELETE_SERIALIZER_ADAPTER(registry.unregisterSerializer(customClassType));
PX_DELETE_REPX_SERIALIZER(registry.unregisterRepXSerializer(customClassType));
}
.. note:: Implementing a PxRepXSerializer is currently not practical without including the internal PhysXExtension header "SnRepXSerializerImpl.h".
.. _physxmetadata:
..............................................................................
PhysX API Metadata System
..............................................................................
This system produces a set of objects that are analogues of the interfaces and of descriptors in the PhysX system, all based on the public interface. The generator heuristically finds functions that start with get/set and, through a series of cascading rules, combines those into several types of properties.
Currently the generator supports the following property types:
* Basic property
* {ptype} get{pname}() const;
* void set{pname}( const ptype& prop ); //plus variations
* read-only, write-only variants of above.
* Range property
* void get{pname}( {ptype}& lowEnd, {ptype}& highEnd );
* void set{pname}( {ptype} lowEnd, {ptype} highEnd );
* Indexed property
* {ptype} get{pname}( enumType idx );
* void set{pname}( enumType idx, const {ptype}& prop );
* Dual indexed property (like above, but with two enumeration indexes).
* Collection
* PxU32 getNb() const;
* PxU32 get( {ptype}* buffer, PxU32 count );
* void set({ptype}* buffer, PxU32 count);
In order to make use of the generator the following files need to be created with the following recipe:
* CustomTypeExtensionAPI.h
* Add all the types that should be exported to gUserPhysXTypes to this file.
* Add the unnecessary types to gAvoidedPhysXTypes. It will not generate metadata information for these types.
* Be sure to append the included files for these types.
* tools/physxmetadatagenerator/generateMetaData.[bat|sh] (see tools/physxmetadatagenerator/readme.txt)
* Set definition folder for these autogenerated files and set the source file in here.
* Specify the filename of autogenerated files. Then it will generate the following files::
include/CustomTypeAutoGeneratedMetaDataObjectNames.h
include/CustomTypeAutoGeneratedMetaDataObjects.h
src/CustomTypeAutoGeneratedMetaDataObjects.cpp
* CustomTypeMetaDataObjects.h
* CustomTypePropertyInfoName has to be defined and CustomTypeAutoGeneratedMetaDataObjects.h has to be included in this file. The file will then export the properties of the custom class and can be included for implementing the custom RepX serializer.
* CustomTypeMetaDataObjects.cpp
* This file is optional. It is only required when custom properties are needed.
PxVehicle serialization is a useful example. With Source/PhysXVehicle as the root folder the structure of the files is as follows::
src/PhysXMetaData/include/PxVehicleMetaDataObjects.h
src/PhysXMetaData/src/PxVehicleMetaDataObjects.cpp
../../Tools/PhysXMetaDataGenerator/PxVehicleExtensionAPI.h
../../Tools/PhysXMetaDataGenerator/generateMetaData.py
Running the script will auto-generate the following files::
src/PhysXMetaData/include/PxVehicleAutoGeneratedMetaDataObjectNames.h
src/PhysXMetaData/include/PxVehicleAutoGeneratedMetaDataObjects.h
src/PhysXMetaData/src/PxVehicleAutoGeneratedMetaDataObjects.cpp
1. PxVehicleExtensionAPI.h: The type DisabledPropertyEntry is used to mark properties which do not require export. CustomProperty is for properties that need to be customized and gUserPhysXTypes is for general properties that need to be exported.
2. tools/physxmetadatagenerator/generateMetaData.[bat|sh]: The target directory is set to src/PhysXMetaData, and the target name is PxVehicle.
3. PxVehicleMetaDataObjects.h: It defines the custom properties and includes PxVehicleAutoGeneratedMetaDataObjects.h
4. PxVehicleMetaDataObjects.cpp: It implements the custom properties.
.. note:: The properties defined in PxVehicleAutoGeneratedMetaDataObjects.h are written to the RepX file automatically if PxVehicleMetaDataObjects.h is included for the custom RepX serializer.

View File

@ -0,0 +1,141 @@
.. _GPURigidBodies:
--------------------------------
GPU Rigid Bodies
--------------------------------
=================================
Introduction
=================================
PhysX rigid body simulation can be configured to take advantage of CUDA capable GPUs under Linux or Windows. This provides a performance benefit proportional to the arithmetic complexity of a scene. GPU acceleration extensions are provided as an optional binary DLL. It supports the entire rigid body pipeline feature-set but currently does not support articulations. The state of GPU-accelerated rigid bodies can be modified and queried using the exact same API as used to modify and query CPU rigid bodies. GPU rigid bodies can easily be used in conjunction with character controllers (CCTs) and vehicles.
=================================
Using GPU Rigid Bodies
=================================
GPU rigid bodies are no more difficult to use than CPU rigid bodies. GPU rigid bodies use the exact same API and same classes as CPU rigid bodies. GPU rigid body acceleration is enabled on a per-scene basis. If enabled, all rigid bodies occupying the scene will be processed by the GPU. This feature is implemented in CUDA and requires SM3.0 (Kepler) or later compatible GPU. If no compatible device is found, simulation will fall back onto the CPU and corresponding error messages will be provided.
This feature is split into two components: rigid body dynamics and broad phase. These are enabled using PxSceneFlag::eENABLE_GPU_DYNAMICS and by setting PxSceneDesc::broadphaseType to PxBroadPhaseType::eGPU respectively. These properties are immutable properties of the scene. In addition, you must initialize the CUDA context manager and set it on the PxSceneDesc. A snippet demonstrating how to enable GPU rigid body simulation is provided in SnippetHelloGRB. The code example below serves as a brief reference::
PxCudaContextManagerDesc cudaContextManagerDesc;
gCudaContextManager = PxCreateCudaContextManager(*gFoundation, cudaContextManagerDesc, PxGetProfilerCallback());
PxSceneDesc sceneDesc(gPhysics->getTolerancesScale());
sceneDesc.gravity = PxVec3(0.0f, -9.81f, 0.0f);
gDispatcher = PxDefaultCpuDispatcherCreate(4);
sceneDesc.cpuDispatcher = gDispatcher;
sceneDesc.filterShader = PxDefaultSimulationFilterShader;
sceneDesc.cudaContextManager = gCudaContextManager;
sceneDesc.flags |= PxSceneFlag::eENABLE_GPU_DYNAMICS;
sceneDesc.broadPhaseType = PxBroadPhaseType::eGPU;
gScene = gPhysics->createScene(sceneDesc);
Enabling GPU rigid body dynamics turns on GPU-accelerated contact generation, shape/body management and the GPU-accelerated constraint solver. This accelerates the majority of the discrete rigid body pipeline.
Turning on GPU broad phase replaces the CPU broad phase with a GPU-accelerated broad phase.
Each can be enabled independently so, for example, you may enable GPU broad phase with CPU rigid body dynamics , CPU broad phase (SAP, MBP or ABP) with GPU rigid body dynamics or combine GPU broad phase with GPU rigid body dynamics.
=================================
What is GPU accelerated?
=================================
The GPU rigid body feature provides GPU-accelerated implementations of:
* Broad Phase
* Contact generation
* Shape and body management
* Constraint solver
All other features are performed on the CPU.
There are several caveats to GPU contact generation. These are as follows:
* GPU contact generation supports only boxes, convex hulls, triangle meshes and heightfields. Any spheres, capsules or planes will have contact generation involving those shapes processed on the CPU, rather than GPU.
* Convex hulls require PxCookingParam::buildGRBData to be set to true to build data required to perform contact generation on the GPU. If a hull with more than 64 vertices or more than 32 vertices per-face is used, it will be processed on the CPU. If the PxConvexFlag::eGPU_COMPATIBLE flag is used when the convex hull is created the limits are applied to ensure the resulting hull can be used on GPU.
* Triangle meshes require PxCookingParam::buildGRBData to be set to true to build data required to process the mesh on the GPU. If this flag is not set during cooking, the GPU data for the mesh will be absent and any contact generation involving this mesh will be processed on CPU.
* Any pairs requesting contact modification will be processed on the CPU.
* PxSceneFlag::eENABLE_PCM must be enabled for GPU contact generation to be performed. This is the only form of contact generation implemented on the GPU. If eENABLE_PCM is not raised, contact generation will be processed on CPU for all pairs using the non distance-based legacy contact generation.
Irrespective of whether contact generation for a given pair is processed on CPU or GPU, the GPU solver will process all pairs with contacts that request collision response in their filter shader.
As mentioned above, GPU rigid bodies currently do not support articulations. If eENABLE_GPU_DYNAMICS is enabled on the scene, any attempts to add an articulation to the scene will result in an error message being displayed and the articulation will not be added to the scene.
The GPU rigid body solver provides full support for joints and contacts. However, best performance is achieved using D6 joints because D6 joints are natively supported on the GPU, i.e. the full solver pipeline from prep to solve is implemented on the GPU. Other joint types are supported by the GPU solver but their joint shaders are run on the CPU. This will incur some additional host-side performance overhead compared to D6 joints.
============================
Tuning
============================
Unlike CPU PhysX, the GPU rigid bodies feature is not able to dynamically grow all buffers. Therefore, it is necessary to provide some fixed buffer sizes for the GPU rigid body feature. If insufficient memory is available, the system will issue warnings and discard contacts/constraints/pairs, which means that behavior may be adversely affected. The following buffers are adjustable in PxSceneDesc::gpuDynamicsConfig::
struct PxgDynamicsMemoryConfig
{
PxU32 constraintBufferCapacity; //!< Capacity of constraint buffer allocated in GPU global memory
PxU32 contactBufferCapacity; //!< Capacity of contact buffer allocated in GPU global memory
PxU32 tempBufferCapacity; //!< Capacity of temp buffer allocated in pinned host memory.
PxU32 contactStreamCapacity; //!< Capacity of contact stream buffer allocated in pinned host memory. This is double-buffered so total allocation size = 2* contactStreamCapacity.
PxU32 patchStreamCapacity; //!< Capacity of the contact patch stream buffer allocated in pinned host memory. This is double-buffered so total allocation size = 2 * patchStreamCapacity.
PxU32 forceStreamCapacity; //!< Capacity of force buffer allocated in pinned host memory.
PxU32 heapCapacity; //!< Initial capacity of the GPU and pinned host memory heaps. Additional memory will be allocated if more memory is required.
PxU32 foundLostPairsCapacity; //!< Capacity of found and lost buffers allocated in GPU global memory. This is used for the found/lost pair reports in the BP.
PxgDynamicsMemoryConfig() :
constraintBufferCapacity(32 * 1024 * 1024),
contactBufferCapacity(24 * 1024 * 1024),
tempBufferCapacity(16 * 1024 * 1024),
contactStreamCapacity(6 * 1024 * 1024),
patchStreamCapacity(5 * 1024 * 1024),
forceStreamCapacity(1 * 1024 * 1024),
heapCapacity(64 * 1024 * 1024),
foundLostPairsCapacity(256 * 1024)
{
}
};
The default values are generally sufficient for scenes simulating approximately 10,000 rigid bodies.
* constraintBufferCapacity defines the total amount of memory that can be occupied by constraints in the solver. If more memory is required, a warning is issued and no further constraints will be created.
* contactBufferCapacity defines the size of a temporary contact buffer used in the constraint solver. If more memory is required, a warning is issued and contacts will be dropped.
* tempBufferCapacity defines the size of a buffer used for miscellaneous transient memory allocations used in the constraint solver.
* contactStreamCapacity defines the size of a buffer used to store contacts in the contact stream. This data is allocated in pinned host memory and is double-buffered. If insufficient memory is allocated, a warning will be issued and contacts will be dropped.
* patchStreamCapacity defines the size of a buffer used to store contact patches in the contact stream. This data is allocated in pinned host memory and is double-buffered. If insufficient memory is allocated, a warning will be issued and contacts will be dropped.
* forceStreamCapacity defines the size of a buffer used to report applied contact forces to the user. This data is allocated in pinned host memory. If insufficient memory is allocated, a warning will be issued and contacts will be dropped.
* heapCapacity defines the initial size of the GPU and pinned host memory heaps. Additional memory will be allocated if more memory is required. The cost of physically allocating memory can be relatively high so a custom heap allocator is used to reduce these costs.
* foundLostPairsCapacity defines the maximum number of found or lost pairs that the GPU broad phase can produce in a single frame. This does not limit the total number of pairs but only limits the number of new or lost pairs that can be detected in a single frame. If more pairs are detected or lost in a frame, an error is emitted and pairs will be dropped by the broad phase.
==============================
Performance Considerations
==============================
GPU rigid bodies can provide extremely large performance advantages over CPU rigid bodies in scenes with several thousand active rigid bodies. However, there are some performance considerations to be taken into account.
GPU rigid bodies currently only accelerate contact generation involving convex hulls and boxes (against convex hulls, boxes, triangle meshes and heighfields). If you make heavy use of other shapes, e.g. capsules or spheres, contact generation involving these shapes will only be processed on CPU.
D6 joints will provide best performance when used with GPU rigid bodies. Other joint types will be partially GPU-accelerated but the performance advantages will be less than the performance advantage exhibited by D6 joints.
Convex hulls with more than 64 vertices or with more than 32 vertices per-face will have their contacts processed by the CPU rather than the GPU, so, if possible, keep vertex counts within these limits. Vertex limits can be defined in cooking to ensure that cooked convex hulls do not exceed these limits.
If your application makes heavy use of contact modification, this may limit the number of pairs that have contact generation performed on the GPU.
Modifying the state of actors forces data to be re-synced to the GPU, e.g. transforms for actors must be updated if the application adjusts global pose, velocities must be updated if the application modifies the bodies' velocities etc.. The associated cost of re-syncing data to the GPU is relatively low but it should be taken into consideration.
Features such as joint projection, CCD and triggers are not GPU accelerated and are still processed on the CPU.

View File

@ -0,0 +1,721 @@
.. _Geometry:
--------
Geometry
--------
============
Introduction
============
This section discusses the PhysX geometry classes. Geometries are used to build shapes for rigid bodies, as collision triggers, and as volumes in PhysX' scene query system. PhysX also provides standalone functions for testing intersection between geometries, raycasting against them, and sweeping one geometry against another.
Geometries are value types, and inherit from a common base class, PxGeometry. Each geometry class defines a volume or surface with a fixed position and orientation. A transform specifies the frame in which the geometry is interpreted. For plane and capsule geometry types PhysX provides helper functions to construct these transforms from common alternative representations.
Geometries fall into two classes:
* primitives (PxBoxGeometry, PxSphereGeometry, PxCapsuleGeometry, PxPlaneGeometry) where the geometry object contains all of the data
* meshes or height fields (PxConvexMeshGeometry, PxTriangleMeshGeometry, PxHeightFieldGeometry), where the geometry object contains a pointer to a much larger object (PxConvexMesh, PxTriangleMesh, PxHeightField respectively) You can use these objects with different scales in each PxGeometry type which references them. The larger objects must be created using a *cooking* process, described for each type below.
When passed into and out of the SDK for use as simulation geometry, the geometry is copied into and out of a PxShape class. It can be awkward in this case to retrieve the geometry without knowing its type, so PhysX provides a union-like wrapper class (PxGeometryHolder) that can be used to pass any geometry type by value. Each mesh (or height field) has a reference count that tracks the number of PxShapes whose geometries reference the mesh.
==============
Geometry Types
==============
+++++++++++++++++++++++++++++++
Spheres
+++++++++++++++++++++++++++++++
.. image:: ../images/GeomTypeSphere.png
A PxSphereGeometry is specified by one attribute, its radius, and is centered at the origin.
+++++++++++++++++++++++++++++++
Capsules
+++++++++++++++++++++++++++++++
.. image:: ../images/GeomTypeCapsule.png
A PxCapsuleGeometry is centered at the origin. It is specified by a radius and a half-height value by which its axis extends along the positive and negative X-axis.
To create a dynamic actor whose geometry is a capsule standing upright, the shape needs a relative transform that rotates it around the Z-axis by a quarter-circle. By doing this, the capsule will extend along the Y-axis of the actor instead of the X-axis. Setting up the shape and actor is otherwise the same as for the sphere::
PxRigidDynamic* aCapsuleActor = thePhysics->createRigidDynamic(PxTransform(position));
PxTransform relativePose(PxQuat(PxHalfPi, PxVec(0,0,1)));
PxShape* aCapsuleShape = PxRigidActorExt::createExclusiveShape(*aCapsuleActor,
PxCapsuleGeometry(radius, halfHeight), aMaterial);
aCapsuleShape->setLocalPose(relativePose);
PxRigidBodyExt::updateMassAndInertia(*aCapsuleActor, capsuleDensity);
aScene->addActor(aCapsuleActor);
The function PxTransformFromSegment() converts from a line segment defining the capsule axis to a transform and halfheight.
+++++++++++++++++++++++++++++++
Boxes
+++++++++++++++++++++++++++++++
.. image:: ../images/GeomTypeBox.png
A PxBoxGeometry has three attributes, the three extents halved::
PxShape* aBoxShape = PxRigidActorExt::createExclusiveShape(*aBoxActor,
PxBoxGeometry(a/2, b/2, c/2), aMaterial);
Where a, b and c are the side lengths of the resulting box.
+++++++++++++++++++++++++++++++
Planes
+++++++++++++++++++++++++++++++
.. image:: ../images/GeomTypePlane.png
Planes divide space into "above" and "below" them. Everything "below" the plane will collide with it.
The Plane lies on the YZ plane with "above" pointing towards positive X. To convert from a plane equation to an equivalent transform, use the function PxTransformFromPlaneEquation(). PxPlaneEquationFromTransform() performs the reverse conversion.
A PxPlaneGeometry has no attributes, since the shape's pose entirely defines the plane's collision volume.
Shapes with a PxPlaneGeometry may only be created for static actors.
+++++++++++++++++++++++++++++++
Convex Meshes
+++++++++++++++++++++++++++++++
.. image:: ../images/GeomTypeConvex.png
A shape is convex if, given any two points within the shape, the shape contains the line between them. A PxConvexMesh is a convex polyhedron represented as a set of vertices and polygonal faces. The number of vertices and faces of a convex mesh in PhysX is limited to 255.
Creating a PxConvexMesh requires cooking. It is assumed here that the cooking library has already been initialized (see :ref:`startup`.) The following steps explain how to create a simple square pyramid.
First, define the vertices of the convex object::
static const PxVec3 convexVerts[] = {PxVec3(0,1,0),PxVec3(1,0,0),PxVec3(-1,0,0),PxVec3(0,0,1),
PxVec3(0,0,-1)};
Then construct a description of the convex data layout::
PxConvexMeshDesc convexDesc;
convexDesc.points.count = 5;
convexDesc.points.stride = sizeof(PxVec3);
convexDesc.points.data = convexVerts;
convexDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX;
Now use the cooking library to construct a PxConvexMesh::
PxDefaultMemoryOutputStream buf;
PxConvexMeshCookingResult::Enum result;
if(!cooking.cookConvexMesh(convexDesc, buf, &result))
return NULL;
PxDefaultMemoryInputData input(buf.getData(), buf.getSize());
PxConvexMesh* convexMesh = physics->createConvexMesh(input);
Finally, create a shape using a PxConvexMeshGeometry which instances the mesh::
PxShape* aConvexShape = PxRigidActorExt::createExclusiveShape(*aConvexActor,
PxConvexMeshGeometry(convexMesh), aMaterial);
Alternatively the PxConvexMesh can be cooked and directly inserted into PxPhysics without stream serialization. This is useful if real-time cooking is required. It is strongly recommended to use offline cooking and streams. Here is an example showing how to improve cooking speed if needed::
PxConvexMeshDesc convexDesc;
convexDesc.points.count = 5;
convexDesc.points.stride = sizeof(PxVec3);
convexDesc.points.data = convexVerts;
convexDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX | PxConvexFlag::eDISABLE_MESH_VALIDATION | PxConvexFlag::eFAST_INERTIA_COMPUTATION;
#ifdef _DEBUG
// mesh should be validated before cooking without the mesh cleaning
bool res = theCooking->validateConvexMesh(convexDesc);
PX_ASSERT(res);
#endif
PxConvexMesh* aConvexMesh = theCooking->createConvexMesh(convexDesc,
thePhysics->getPhysicsInsertionCallback());
Please note that mesh validation is required for debug and checked builds, as creating meshes from unvalidated input descriptors may result in undefined behavior. Providing PxConvexFlag::eFAST_INERTIA_COMPUTATION flag the volume integration will use SIMD code path which does faster computation but with lesser precision.
The user can optionally provide a per-instance PxMeshScale in the PxConvexMeshGeometry. The scale defaults to identity. Negative scale is not supported for convex meshes.
PxConvexMeshGeometry also contains flags to tweak some aspects of the convex object. By default the system computes approximate (loose) bounds around convex objects. Using PxConvexMeshGeometryFlag::eTIGHT_BOUNDS enables smaller/tighter bounds, which are more expensive to compute but could result in improved simulation performance when a lot of convex objects are interacting with each other.
+++++++++++++++++++++++++++++++
Convex Mesh cooking
+++++++++++++++++++++++++++++++
Convex Mesh cooking transforms the mesh data into a form which allows the SDK to perform efficient collision detection. The input to cooking is defined using the input PxConvexMeshDesc.
There are different ways to fill in this structure, depending on whether you want to produce a convex mesh starting from just a cloud of vertices, or whether you have the vertices and faces of a polyhedron already.
If Only Vertex Points are Provided
-------------------------------------------
When providing only vertices, set the PxConvexFlag::eCOMPUTE_CONVEX flag to compute the mesh::
PxConvexMeshDesc convexDesc;
convexDesc.points.count = 20;
convexDesc.points.stride = sizeof(PxVec3);
convexDesc.points.data = convexVerts;
convexDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX;
convexDesc.maxVerts = 10;
PxDefaultMemoryOutputStream buf;
if(!cooking.cookConvexMesh(convexDesc, buf))
return NULL;
The algorithm tries to create a convex mesh from the source vertices. The field convexDesc.vertexLimit specifies the limit for the maximum number of vertices in the resulting hull.
This routine can sometimes fail when the source data is geometrically challenging, for example if it contains a lot of vertices close to each-other. If cooking fails, an error is reported to the error stream and the routine returns false.
If PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES is used, the algorithm does not include triangles with an area less than PxCookingParams::areaTestEpsilon. If the algorithm cannot find 4 initial vertices without a small triangle, PxConvexMeshCookingResult::eZERO_AREA_TEST_FAILED is returned. This means that the provided vertices were in a very small area and the cooker could not produce a valid hull.
The toolkit helper function PxToolkit::createConvexMeshSafe illustrates the most robust strategy for convex mesh cooking. First it tries to create the regular convex hull. If that fails it will use an AABB or OBB.
It is recommended to provide vertices around origin and put transformation in PxShape, otherwise addional PxConvexFlag::eSHIFT_VERTICES flag for the mesh computation.
If huge amount of input vertices are provided, it might be useful to quantize the input vertices, in this case use PxConvexFlag::eQUANTIZE_INPUT and set the required PxConvexMeshDesc::quantizedCount.
Quickhull Algorithm
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This algorithm creates a convex mesh whose vertices are a subset of the original vertices, and the number of vertices is guaranteed to be no more than the specified maximum.
The Quickhull algorithm performs these steps:
* Cleans the vertices - removes duplicates etc.
* Finds a subset of vertices, no more than vertexLimit, that enclose the input set.
* If the vertexLimit is reached, expand the limited hull around the input vertices to ensure we encapsulate all the input vertices.
* Compute a vertex map table. (Requires at least 3 neighbor polygons for each vertex.)
* Checks the polygon data - verifies that all vertices are on or inside the hull, etc.
* Computes mass and inertia tensor assuming density is 1.
* Saves data to stream.
When the hull is constructed each new vertex added must be further than PxCookingParams::planeTolerance from the hull, if not that vertex is dropped.
Vertex Limit Algorithms
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If a vertex limit has been provided, there are two algorithms that handle vertex limitation.
The default algorithm computes the full hull, and an OBB around the input vertices. This OBB is then sliced with the hull planes until the vertex limit is reached. The default algorithm requires the vertex limit to be set to at least 8, and typically produces results that are much better quality than are produced by plane shifting.
When plane shifting is enabled (PxConvexFlag::ePLANE_SHIFTING), the hull computation stops when vertex limit is reached. The hull planes are then shifted to contain all input vertices, and the new plane intersection points are then used to generate the final hull with the given vertex limit. Plane shifting may produce sharp edges to vertices very far away from the input cloud, and does not guarantee that all input vertices are inside the resulting hull. However, it can be used with a vertex limit as low as 4, and so may be a better choice for cases such as small pieces of debris with very low vertex counts.
Vertex Points, Indices and Polygons are Provided
--------------------------------------------------
To create a PxConvexMesh given a set of input vertices (convexVerts) and polygons (hullPolygons)::
PxConvexMeshDesc convexDesc;
convexDesc.points.count = 12;
convexDesc.points.stride = sizeof(PxVec3);
convexDesc.points.data = convexVerts;
convexDescPolygons.polygons.count = 20;
convexDescPolygons.polygons.stride = sizeof(PxHullPolygon);
convexDescPolygons.polygons.data = hullPolygons;
convexDesc.flags = 0;
PxDefaultMemoryOutputStream buf;
if(!cooking.cookConvexMesh(convexDesc, buf))
return NULL;
When points and polygons are provided, the SDK validates the mesh and creates the PxConvexmesh directly. This is the fastest way to create a convex mesh. Note that the SDK requires at least 3 neighbor polygons for each vertex. Otherwise acceleration structure for PCM is not created and it does result in performance penalty if PCM is enabled.
(NOTE: the SDK should reject such a mesh as invalid)
Internal steps during convex cooking:
* Compute vertex map table, requires at least 3 neighbor polygons for each vertex.
* Check polygons data - check if all vertices are on or inside the hull, etc.
* Compute mass and inertia tensor assuming density 1.
* Save data to stream.
+++++++++++++++++++++++++++++++
Triangle Meshes
+++++++++++++++++++++++++++++++
.. image:: ../images/GeomTypeMesh.png
Like graphical triangle meshes, a collision triangle mesh consists of a collection of vertices and the triangle indices. Triangle mesh creation requires use of the cooking library. It is assumed here that the cooking library has already been initialized (see :ref:`startup`.)::
PxTriangleMeshDesc meshDesc;
meshDesc.points.count = nbVerts;
meshDesc.points.stride = sizeof(PxVec3);
meshDesc.points.data = verts;
meshDesc.triangles.count = triCount;
meshDesc.triangles.stride = 3*sizeof(PxU32);
meshDesc.triangles.data = indices32;
PxDefaultMemoryOutputStream writeBuffer;
PxTriangleMeshCookingResult::Enum result;
bool status = cooking.cookTriangleMesh(meshDesc, writeBuffer,result);
if(!status)
return NULL;
PxDefaultMemoryInputData readBuffer(writeBuffer.getData(), writeBuffer.getSize());
return physics.createTriangleMesh(readBuffer);
Alternatively *PxTriangleMesh* can be cooked and directly inserted into *PxPhysics* without stream serialization. This is useful if real-time cooking is required. It is strongly recommended to use offline cooking and streams. Example how to improve cooking speed if needed::
PxTolerancesScale scale;
PxCookingParams params(scale);
// disable mesh cleaning - perform mesh validation on development configurations
params.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH;
// disable edge precompute, edges are set for each triangle, slows contact generation
params.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE;
// lower hierarchy for internal mesh
params.meshCookingHint = PxMeshCookingHint::eCOOKING_PERFORMANCE;
theCooking->setParams(params);
PxTriangleMeshDesc meshDesc;
meshDesc.points.count = nbVerts;
meshDesc.points.stride = sizeof(PxVec3);
meshDesc.points.data = verts;
meshDesc.triangles.count = triCount;
meshDesc.triangles.stride = 3*sizeof(PxU32);
meshDesc.triangles.data = indices32;
#ifdef _DEBUG
// mesh should be validated before cooked without the mesh cleaning
bool res = theCooking->validateTriangleMesh(meshDesc);
PX_ASSERT(res);
#endif
PxTriangleMesh* aTriangleMesh = theCooking->createTriangleMesh(meshDesc,
thePhysics->getPhysicsInsertionCallback());
Indices can be 16 or 32 bit. The strides used here assume that vertices and indices are arrays of PxVec3s and 32bit integers respectively with no gaps in the data layout.
Returned result enum *PxTriangleMeshCookingResult::eLARGE_TRIANGLE* does warn the user if the mesh contains large triangles, which should be tessellated to ensure better simulation and CCT stability.
Like height fields, triangle meshes support per-triangle material indices. To use per-triangle materials for a mesh, provide per-triangle indices to the cooking library in the mesh descriptor. Later, when creating the PxShape, supply a table mapping the index values in the mesh to material instances.
Triangle Mesh cooking
-------------------------------------------
Triangle mesh cooking proceeds as follows:
* Check validity of input vertices.
* Weld vertices and check triangle sizes.
* create acceleration structure for queries.
* Compute edge convexity information and adjacencies.
* Save data to stream.
Note that mesh cleaning may result in the set of triangles produced by cooking being a subset different from the original input set. Mesh cleaning removes invalid triangles (containing out-of-range vertex references), duplicate triangles, and zero-area triangles. When this happens, PhysX optionally outputs a mesh remapping table that links each internal triangle to its source triangle in the user's data.
There are multiple parameters to control mesh creation.
In *PxTriangleMeshDesc*:
* *materialIndices* defines per triangle materials. When a triangle mesh collides with another object, a material is required at the collision point. If materialIndices is NULL, then the material of the PxShape instance is used.
In *PxCookingParams*:
* *scale* defines Tolerance scale is used to check if cooked triangles are not too huge. This check will help with simulation stability.
* *suppressTriangleMeshRemapTable* specifies whether the face remap table is created. If not, this saves a significant amount of memory, but the SDK will not be able to provide information about which original mesh triangle is hit in collisions, sweeps or raycasts hits.
* *buildTriangleAdjacencies* specifies if the triangle adjacency information is created. The adjacent triangles can be retrieved for a given triangle using the getTriangle.
* *meshPreprocessParams* specifies mesh pre-processing parameters.
* *PxMeshPreprocessingFlag::eWELD_VERTICES* enables vertex welding during triangle mesh cooking.
* *PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH* disables mesh clean process. Vertices duplicities are not searched, huge triangles test is not done. Vertices welding is not done. Does speed up the cooking.
* *PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE* disables vertex edge precomputation. Makes cooking faster but slow up contact generation.
* *meshWeldTolerance* - If mesh welding is enabled, this controls the distance at which vertices are welded. If mesh welding is not enabled, this value defines the acceptance distance for mesh validation. Provided no two vertices are within this distance, the mesh is considered to be clean. If not, a warning will be emitted. Having a clean mesh is required to achieve the best possible performance.
* *midphaseDesc* specifies the desired midphase acceleration structure descriptor.
* *PxBVH33MidphaseDesc - PxMeshMidPhase::eBVH33* is the default structure. It was the one used in recent PhysX versions up to PhysX 3.3. It has great performance and is supported on all platforms.
* *PxBVH34MidphaseDesc - PxMeshMidPhase::eBVH34* is a revisited implementation introduced in PhysX 3.4. It can be significantly faster both in terms of cooking performance and runtime performance, but it is currently only available on platforms supporting the SSE2 instuction set.
*PxBVH33MidphaseDesc params*:
* *meshCookingHint* specifies mesh hierarchy construction preferences. Enables better cooking performance over collision performance, for applications where cooking performance is more important than best quality mesh creation.
* *meshSizePerformanceTradeOff* specifies the trade-off between mesh size and runtime performance.
*PxBVH34MidphaseDesc params*:
* *numTrisPerLeaf* specifies the number of triangles per leaf. Less triangles per leaf produces larger meshes with general better runtime performance and worse cooking performance.
+++++++++++++++++++++++++++++++
Height Fields
+++++++++++++++++++++++++++++++
.. image:: ../images/GeomTypeHeightField.png
Local space axes for the height fields are:
* Row - X axis
* Column - Z axis
* Height - Y axis
As the name suggests, terrains can be described by just the height values on a regular, rectangular sampling grid::
PxHeightFieldSample* samples = (PxHeightFieldSample*)alloc(sizeof(PxHeightFieldSample)*
(numRows*numCols));
Each sample consists of a 16 bit integer height value, two materials (for the two triangles in the samples rectangle) and a tessellation flag.
The flag and materials refer to the cell below and to the right of the sample point, and indicate along which diagonal to split it into triangles, and the materials of those triangles. A special predefined material ``PxHeightFieldMaterial::eHOLE`` specifies a hole in the height field. See the reference documentation for *PxHeightFieldSample* for more details.
.. csv-table::
:header: Tesselation flag not set, Tesselation flag set
.. image:: ../images/heightfieldTriMat1.png, .. image:: ../images/heightfieldTriMat2.png
Examples:
+-------------------+-------------------------------------------+
| Tesselation flags | Result |
+===================+===========================================+
| | 0,0,0 | .. image:: ../images/heightfieldTess2.png |
| | 0,0,0 | |
| | 0,0,0 | |
+-------------------+-------------------------------------------+
| | 1,1,1 | .. image:: ../images/heightfieldTess1.png |
| | 1,1,1 | |
| | 1,1,1 | |
+-------------------+-------------------------------------------+
| | 1,0,1 | .. image:: ../images/heightfieldTess3.png |
| | 0,1,0 | |
| | 1,0,1 | |
+-------------------+-------------------------------------------+
To tell the system the number of sampled heights in each direction, use a descriptor to instantiate a PxHeightField object::
PxHeightFieldDesc hfDesc;
hfDesc.format = PxHeightFieldFormat::eS16_TM;
hfDesc.nbColumns = numCols;
hfDesc.nbRows = numRows;
hfDesc.samples.data = samples;
hfDesc.samples.stride = sizeof(PxHeightFieldSample);
PxHeightField* aHeightField = theCooking->createHeightField(hfDesc,
thePhysics->getPhysicsInsertionCallback());
Now create a PxHeightFieldGeometry and a shape::
PxHeightFieldGeometry hfGeom(aHeightField, PxMeshGeometryFlags(), heightScale, rowScale,
colScale);
PxShape* aHeightFieldShape = PxRigidActorExt::createExclusiveShape(*aHeightFieldActor,
hfGeom, aMaterialArray, nbMaterials);
The row and column scales tell the system how far apart the sampled points lie in the associated direction. The height scale scales the integer height values to a floating point range.
The variant of *createExclusiveShape()* used here specifies an array of materials for the height field, which will be indexed by the material indices of each cell to resolve collisions with that cell. The single-material variant may be used instead, but the height field material indices must all be a single value or the special value ``eHOLE``.
Contact generation with triangle edges at the terrain's borders can be disabled using the *PxHeightFieldFlag::eNO_BOUNDARY_EDGES* flag, allowing more efficient contact generation when there are multiple heightfield shapes arranged so that their edges touch.
Heightfield cooking
-------------------------------------------
Heightfield data can be cooked in offline and then used to createHeightField. The cooking does precompute and store the edge information. This allows much faster create of the heightfield, since the edges are already precomputed. It is very useful if you need to create heightfields in the runtime, since it does improve the speed of createHeightField significantly.
Heightfield cooking proceeds as follows:
* Load heightfield samples into internal memory.
* Precompute edge collision information.
* Save data to stream.
Heightfields contact generation
-------------------------------------------
The heightfield contact generation approach extracts triangles from the heightfield and utilizes the same low-level contact generation code that is used for triangle meshes. This approach ensures equivalent behavior and performance if triangle meshes or heightfields are used interchangeably. However, with this approach, the heightfield surface has no thickness so fast-moving objects may tunnel if CCD is not enabled.
Heightfields registration
-------------------------------------------
Heightfields are enabled by calling::
PxRegisterHeightFields(PxPhysics& physics);
This call must be made before creating any scene, otherwise warnings will be issued. This is a global setting, and it applies to all scenes.
If PxCreatePhysics(...) is called, this will automatically call PxRegisterHeightFields(...). If PxCreateBasePhysics(...) is called, heightfields are not registered by default. If heightfields are used, the application must call the heightfield registration function.
=================================
Deformable meshes
=================================
PhysX supports deformable meshes, i.e. meshes whose vertices move over time (while the topology, i.e. the triangle indices, remains fixed).
Deformable meshes are only supported with the *PxMeshMidPhase::eBVH33* data structure. Because the mesh vertices are going to be updated, the mapping between the user-defined vertices and PhysX' internal vertices must also be preserved. That is, PhysX should not reorder vertices during cooking. So all cooking operations that could reorder vertices should be disabled, and it is the user's responsability to make sure that the passed vertices are correct w.r.t. disabled operations. For example the mesh cleaning phase should be disabled::
cookingParams.midphaseDesc.setToDefault(PxMeshMidPhase::eBVH33);
cookingParams.meshPreprocessParams = PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH;
It is possible to mix eBVH33 and eBVH34 meshes in the same scene, so the default cooking parameters can still be used for non-deformable / static meshes.
To modify the vertices, use the *PxTriangleMesh::getVerticesForModification()* and *PxTriangleMesh::refitBVH()* functions before simulating the scene::
// get vertex array
PxVec3* verts = mesh->getVerticesForModification();
// update the vertices here
...
// tell PhysX to update the mesh structure
PxBounds3 newBounds = mesh->refitBVH();
Then use *PxScene::resetFiltering()* for the corresponding mesh actor, to tell the broadphase its bounds have been modified::
scene->resetFiltering(*actor);
When the mesh deforms and moves away from the objects resting on it, said meshes can bounce and jitter slightly on the mesh. Using a slightly negative rest offset for the mesh shape can help reduce this effect::
PxShape* shape;
mesh->getShapes(&shape, 1);
shape->setRestOffset(-0.5f); // something negative, value depends on your world's scale
This will let objects "sink" a bit into the dynamic mesh. That way contacts are not immediately lost and the motion remains smooth. Please refer to the deformable mesh snippet in the SDK for more details.
=================================
Mesh Scaling
=================================
A shared PxTriangleMesh or PxConvexMesh may be stretched or compressed when it is instanced by a geometry. This allows multiple instancing of the same mesh with different scale factors applied. Scaling is specified with the PxMeshScale class, which defines scale factors to be applied along 3 orthogonal axes. A factor greater than 1.0 results in stretching, while a factor less than 1.0 results in compression. The directions of the axes are governed by a quaternion, and specified in the local frame of the shape.
Negative mesh scale is supported, with negative values producing a reflection along each corresponding axis. In addition PhysX will flip the normals for mesh triangles when scale.x*scale.y*scale.z < 0.
The following code creates a shape with a PxTriangleMesh scaled by a factor of x along the x-axis, y along the y-axis, and z along the z-axis::
// created earlier
PxRigidActor* myActor;
PxTriangleMesh* myTriMesh;
PxMaterial* myMaterial;
// create a shape instancing a triangle mesh at the given scale
PxMeshScale scale(PxVec3(x,y,z), PxQuat(PxIdentity));
PxTriangleMeshGeometry geom(myTriMesh,scale);
PxShape* myTriMeshShape = PxRigidActorExt::createExclusiveShape(*myActor,geom,*myMaterial);
Convex meshes are scaled using the PxMeshScale class in a similar manner. The following code creates a shape with a PxConvexMesh scaled by a factor of x along (sqrt(1/2), 1.0, -sqrt(1/2)), by a factor of y along (0,1,0) and a by a factor of z along (sqrt(1/2), 1.0, sqrt(1/2))::
PxMeshScale scale(PxVec3(x,y,z), PxQuat quat(PxPi*0.25f, PxVec3(0,1,0)));
PxConvexMeshGeometry geom(myTriMesh,scale);
PxShape* myConvexMeshShape = PxRigidActorExt::createExclusiveShape(*myActor,geom,*myMaterial);
Height fields can also be scaled, using scale factors stored in PxHeightFieldGeometry. In this case the scale is assumed to be along the axes of the rows, columns and height directions of the height field. The scaling of is demonstrated in SampleNorthPole in SampleNorthPoleBuilder.cpp::
PxHeightFieldGeometry hfGeom(heightField, PxMeshGeometryFlags(), heightScale, hfScale, hfScale);
PxShape* hfShape = PxRigidActorExt::createExclusiveShape(*hfActor, hfGeom, getDefaultMaterial());
In this example, the coordinates along the x and z axes are scaled by hfScale, while the sample heights are scaled by heightScale.
===============================
PxGeometryHolder
===============================
When a geometry is provided for a shape, either on creation or with *PxShape::setGeometry()*, the geometry is copied into the SDK's internal structures. If you know the type of a shape's geometry you may retrieve it directly::
PxBoxGeometry boxGeom;
bool status = shape->getBoxGeometry(geometry);
The status return code is set to false if the shape's geometry is not of the expected type.
However, it is often convenient to retrieve a geometry object from a shape without first knowing its type - for example, to call a function which takes a PxGeometry reference as an argument.
PxGeometryHolder is a union-like class that allows the return of a PxGeometry object by value, regardless of type. Its use is illustrated in the *createRenderObjectFromShape()* function in PhysXSample.cpp::
PxGeometryHolder geom = shape->getGeometry();
switch(geom.getType())
{
case PxGeometryType::eSPHERE:
shapeRenderActor = SAMPLE_NEW(RenderSphereActor)(renderer, geom.sphere().radius);
break;
case PxGeometryType::eCAPSULE:
shapeRenderActor = SAMPLE_NEW(RenderCapsuleActor)(renderer, geom.capsule().radius,
geom.capsule().halfHeight);
break;
...
}
The function *PxGeometryHolder::any()* returns a reference to a PxGeometry object. For example, to compare two shapes in a scene for overlap::
bool testForOverlap(const PxShape& s0, const PxShape& s1)
{
return PxGeometryQuery::overlap(s0.getGeometry().any(), PxShapeExt::getGlobalPose(s0),
s1.getGeometry().any(), PxShapeExt::getGlobalPose(s1));
}
====================
Vertex and Face Data
====================
Convex meshes, triangle meshes, and height fields can all be queried for vertex and face data. This is particularly useful, for example, when rendering the mesh of the convex shape. The function::
RenderBaseActor* PhysXSample::createRenderObjectFromShape(PxShape* shape,
RenderMaterial* material)
in PhysXSample.cpp contains a switch statement with a case for each shape type, illustrating the steps required to query the vertices and faces.
It is possible to get information about triangle from a triangle mesh or height field using PxMeshQuery::getTriangle function. You can also retrieve adjacent triangle indices for the given triangle (triangle triangleNeighbour[i] shares the edge vertex[i]-vertex[(i+1)%3] with triangle indexed as 'triangleIndex', where vertex is in the range from 0 to 2). To enable this feature the triangle mesh is cooked with buildTriangleAdjacencies parameter set to true.
+++++++++++++++++++++++++++++++
Convex Meshes
+++++++++++++++++++++++++++++++
A convex mesh contains an array of vertices, an array of faces, and an index buffer which concatenates the vertex indices for each face. To unpack a convex mesh, the first step is to extract the shared convex mesh::
PxConvexMesh* convexMesh = geom.convexMesh().convexMesh;
Then obtain references to the vertex and index buffers::
PxU32 nbVerts = convexMesh->getNbVertices();
const PxVec3* convexVerts = convexMesh->getVertices();
const PxU8* indexBuffer = convexMesh->getIndexBuffer();
Now iterate over the array of faces to triangulate them::
PxU32 offset = 0;
for(PxU32 i=0;i<nbPolygons;i++)
{
PxHullPolygon face;
bool status = convexMesh->getPolygonData(i, face);
PX_ASSERT(status);
const PxU8* faceIndices = indexBuffer + face.mIndexBase;
for(PxU32 j=0;j<face.mNbVerts;j++)
{
vertices[offset+j] = convexVerts[faceIndices[j]];
normals[offset+j] = PxVec3(face.mPlane[0], face.mPlane[1], face.mPlane[2]);
}
for(PxU32 j=2;j<face.mNbVerts;j++)
{
*triangles++ = PxU16(offset);
*triangles++ = PxU16(offset+j);
*triangles++ = PxU16(offset+j-1);
}
offset += face.mNbVerts;
}
Observe that the vertex indices of the polygon begin at indexBuffer[face.mIndexBase], and the count of vertices is given by face.mNbVerts.
+++++++++++++++++++++++++++++++
Triangle Meshes
+++++++++++++++++++++++++++++++
Triangle meshes contain arrays of vertices and index triplets which define the triangles by indexing into the vertex buffer. The arrays can be accessed directly from the shared triangle mesh::
PxTriangleMesh* tm = geom.triangleMesh().triangleMesh;
const PxU32 nbVerts = tm->getNbVertices();
const PxVec3* verts = tm->getVertices();
const PxU32 nbTris = tm->getNbTriangles();
const void* tris = tm->getTriangles();
The indices may be stored with either 16-bit or 32-bit values, specified when the mesh was originally cooked. To determine the storage format at runtime, use the API call::
const bool has16bitIndices = tm->has16BitTriangleIndices();
Assuming that the triangle indices are stored in 16-bit format, find the jth vertex of the ith triangle by::
const PxU16* triIndices = (const PxU16*)tris;
const PxU16 index = triIndices[3*i +j];
The corresponding vertex is::
const PxVec3& vertex = verts[index];
+++++++++++++++++++++++++++++++
Height Fields
+++++++++++++++++++++++++++++++
The storage of height field data is platform-dependent, and therefore direct access to the height field samples is not provided. Instead, calls are provided to render the samples to a user-supplied buffer.
Again, the first step is to retrieve the geometry for the height field::
const PxHeightFieldGeometry& geometry = geom.heightField();
The height field has three scaling parameters::
const PxReal rs = geometry.rowScale;
const PxReal hs = geometry.heightScale;
const PxReal cs = geometry.columnScale;
And a shared data structure, which stores the row and column count::
PxHeightField* hf = geometry.heightField;
const PxU32 nbCols = hf->getNbColumns();
const PxU32 nbRows = hf->getNbRows();
To render the height field, first extract the samples to an array::
const PxU32 nbVerts = nbRows * nbCols;
PxHeightFieldSample* sampleBuffer = new PxHeightFieldSample[nbVerts];
hf->saveCells(sampleBuffer, nbVerts * sizeof(PxHeightFieldSample));
The samples are stored in row-major order; that is, row0 is stored first, followed by row1, then row2, and so on. Thus the sample corresponding to the ith row and the jth column is i*nbCols + j.
Evaluate the scaled vertices of the height field as follows::
PxVec3* vertices = new PxVec3[nbVerts];
for(PxU32 i = 0; i < nbRows; i++)
{
for(PxU32 j = 0; j < nbCols; j++)
{
vertices[i * nbCols + j] = PxVec3(PxReal(i) * rs, PxReal(sampleBuffer[j +
(i*nbCols)].height) * hs, PxReal(j) * cs);
}
}
Then tessellate the field from the samples as required.
+++++++++++++++++++++++++++++++
Heightfield Modification
+++++++++++++++++++++++++++++++
Heightfield samples can be modified at runtime in rectangular blocks. In the following code snippet we create a HF and modify its samples::
// create a 5x5 HF with height 100 and materials 2,3
PxHeightFieldSample samples1[25];
for (PxU32 i = 0; i < 25; i ++)
{
samples1[i].height = 100;
samples1[i].materialIndex0 = 2;
samples1[i].materialIndex1 = 3;
}
PxHeightFieldDesc heightFieldDesc;
heightFieldDesc.nbColumns = 5;
heightFieldDesc.nbRows = 5;
heightFieldDesc.convexEdgeThreshold = 3;
heightFieldDesc.samples.data = samples1;
heightFieldDesc.samples.stride = sizeof(PxHeightFieldSample);
PxPhysics* physics = getPhysics();
PxHeightField* pHeightField = cooking->createHeightField(heightFieldDesc, physics->getPhysicsInsertionCallback());
// create modified HF samples, this 10-sample strip will be used as a modified row
// Source samples that are out of range of target heightfield will be clipped with no error.
PxHeightFieldSample samplesM[10];
for (PxU32 i = 0; i < 10; i ++)
{
samplesM[i].height = 1000;
samplesM[i].materialIndex0 = 1;
samplesM[i].materialIndex1 = 127;
}
PxHeightFieldDesc desc10Rows;
desc10Rows.nbColumns = 1;
desc10Rows.nbRows = 10;
desc10Rows.samples.data = samplesM;
desc10Rows.samples.stride = sizeof(PxHeightFieldSample);
pHeightField->modifySamples(1, 0, desc10Rows); // modify row 1 with new sample data
PhysX does not keep a mapping from the heightfield to heightfield shapes that reference it. Call PxShape::setGeometry on each shape which references the height field, to ensure that internal data structures are updated to reflect the new geometry::
PxShape *hfShape = userGetHfShape(); // the user is responsible for keeping track of
// shapes associated with modified HF
hfShape->setGeometry(PxHeightFieldGeometry(pHeightField, ...));
Please also note that PxShape::setGeometry() does not guarantee correct/continuous behavior when objects are resting on top of old or new geometry.
The method PxHeightField::getTimestamp() returns the number of times a heightfield has been modified.

View File

@ -0,0 +1,453 @@
.. _GeometryQueries:
----------------
Geometry Queries
----------------
===============================
Introduction
===============================
This chapter describes how to use PhysX' collision functionality with individual geometry objects. There are four main kinds of geometry queries:
* raycasts ("raycast queries") test a ray against a geometry object.
* sweeps ("sweep queries") move one geometry object along a line to find the first point of intersection with another geometry object.
* overlaps ("overlap queries") determine whether two geometry objects intersect.
* penetration depth computations ("minimal translational distance queries", abbreviated here to "MTD") test two overlapping geometry objects to find the direction along which they can be separated by the minimum distance.
In addition, PhysX provides helpers to compute the AABB of a geometry object, and to compute the distance between a point and a geometry object.
In all of the following functions, a geometry object is defined by its shape (a *PxGeometry* structure) and its pose (a *PxTransform* structure). All transforms and vectors are interpreted as being in the same space, and the results are also returned in that space.
===============================================================================
========
Raycasts
========
.. image:: ../images/GeomQueryRaycast.png
A raycast query traces a point along a line segment until it hits a geometry object. PhysX supports raycasts for all geometry types.
The following code illustrates how to use a raycast query::
PxRaycastHit hitInfo;
PxU32 maxHits = 1;
PxHitFlags hitFlags = PxHitFlag::ePOSITION|PxHitFlag::eNORMAL|PxHitFlag::eUV;
PxU32 hitCount = PxGeometryQuery::raycast(origin, unitDir,
geom, pose,
maxDist,
hitFlags,
maxHits, &hitInfo);
The arguments are interpreted as follows:
* *origin* is the start point of the ray.
* *unitDir* is a unit vector defining the direction of the ray.
* *maxDist* is the maximum distance to search along the ray. It must be in the [0, inf) range. If the maximum distance is 0, a hit will only be returned if the ray starts inside a shape, as detailed below for each geometry.
* *geom* is the geometry to test against.
* *pose* is the pose of the geometry.
* *hitFlags* specifies the values that should be returned by the query, and options for processing the query.
* *maxHits* is the maximum number of hits to return.
* *hitInfo* specifies the *PxRaycastHit* structure(s) into which the raycast results will be stored.
The returned result is the number of intersections found. For each intersection, a *PxRaycastHit* is populated. The fields of this structure are as follows::
PxRigidActor* actor;
PxShape* shape;
PxVec3 position;
PxVec3 normal;
PxF32 distance;
PxHitFlags flags;
PxU32 faceIndex;
PxF32 u, v;
Some fields are optional, and the flags field indicates which members have been filled with result values. The query will fill fields in the output structure if the corresponding flags were set in the input - for example, if the *PxHitFlag::ePOSITION* is set in the input hitFlags, the query will fill in the *PxRaycastHit::position* field, and set the *PxHitFlag::ePOSITION* flag in *PxRaycastHit::flags*. If the input flag is not set for a specific member, the result structure may or may not contain valid data for that member. Omitting the *eNORMAL* and *ePOSITION* flags in the input can sometimes result in faster queries.
For a raycast which is not initially intersecting the geometry object, the fields are populated as follows (optional fields are listed together with the flag that controls them):
* *actor* and *shape* are not filled (these fields are used only in scene-level raycasts, see :ref:`SceneQueries`).
* *position* (*PxHitFlag::ePOSITION*) is the position of the intersection.
* *normal* (*PxHitFlag::eNORMAL*) is the surface normal at the point of intersection.
* *distance* is the distance along the ray at which the intersection was found.
* *flags* specifies which fields of the structure are valid.
* *faceIndex* is the index of the face which the ray hit. For triangle mesh and height field intersections, it is a triangle index. For convex mesh intersections it is a polygon index. For other shapes it is always set to 0xffffffff.
* *u* and *v* (*PxHitFlag::eUV*) are the barycentric coordinates of the intersection. These fields (and the flag) are supported only for meshes and heightfields.
The position field is related to the barycentric coordinates via the following formula, where v0, v1 and v2 are the vertices from the hit triangle:
position = (1 - u - v)*v0 + u*v1 + v*v2;
This mapping is implemented in *PxTriangle::pointFromUV()*.
See :ref:`Geometry` for details of how to retrieve face and vertex data from triangle meshes, convex meshes and height fields using face and vertex indices.
Exceptions to the above behavior may apply if a ray starts inside an object, in which case PhysX may not be able to compute meaningful output values for some fields. In these cases the field will remain unmodified and the corresponding flag will not be set. Specific details vary by geometry type, and are described below.
The exact conditions for raycast intersections are as follows:
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Raycasts against Spheres, Capsules, Boxes and Convex Meshes
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
For solid objects (sphere, capsule, box, convex) at most 1 result is returned. If the ray origin is inside a solid object:
* the reported hit distance is set to zero.
* the hit normal is set to be the opposite of the ray's direction, and the *PxHitFlag::eNORMAL* flag is set in the output.
* the hit impact position is set to the ray's origin and the *PxHitFlag::ePOSITION* flag is set in the output.
If the start or end point of a ray is very close to the surface of the object, it may be treated as being on either side of the surface.
+++++++++++++++++++++++
Raycasts against Planes
+++++++++++++++++++++++
For raycasts, a plane is treated as an infinite single-sided quad that includes its boundary (note that this is not the same as for overlaps). At most one result is returned, and if the ray origin is behind the plane's surface, no hit will be reported even in case the ray intersects the plane.
If the start or end point of a ray is very close to the plane, it may be treated as being on either side of the plane.
.. _raycasts_vs_meshes:
++++++++++++++++++++++++++++++++
Raycasts against Triangle Meshes
++++++++++++++++++++++++++++++++
Triangle meshes are treated as thin triangle surfaces rather than solid objects. They may be configured to return either an arbitrary hit, the closest hit, or multiple hits.
* if maxHits is 1 and *PxHitFlag::eMESH_ANY* is not set, the query will return the closest intersection.
* if maxHits is 1 and *PxHitFlag::eMESH_ANY* is set, the query will return an arbitrary intersection. Use this when it is sufficient to know whether or not the ray hit the mesh, e.g. for line-of-sight queries or shadow rays.
* if maxHits is greater than 1, the query will return multiple intersections, up to maxHits. If more than maxHits intersection points exist, there is no guarantee that the results will include the closest. Use this for e.g. wall-piercing bullets that hit multiple triangles, or where special filtering is required. Note that *PxHitFlag::eMESH_MULTIPLE* must be used in this case.
In general "any hit" queries are faster than "closest hit" queries, and "closest hit" queries are faster than "multiple hits" queries.
By default, back face hits (where the triangle's outward-facing normal has a positive dot product with the ray direction) are culled, and so for any triangle hit the reported normal will have a negative dot product with the ray direction. This behavior may be modified by the mesh instance's *PxMeshGeometryFlag::eDOUBLE_SIDED* flag and the query's *PxHitFlag::eMESH_BOTH_SIDES* flag:
* if either *PxMeshGeometryFlag::eDOUBLE_SIDED* or *PxHitFlag::eMESH_BOTH_SIDES* is set, culling is disabled.
* if *PxMeshGeometryFlag::eDOUBLE_SIDED* is set, the reported normal is reversed for a back face hit.
For example a transparent glass window could be modeled as a double-sided mesh, so that a ray would hit either side with the reported normal facing opposite to the ray direction. A raycast tracing the path of a bullet that may penetrate the front side of a mesh and emerge from the back could use *eMESH_BOTH_SIDES* to find both front and back facing triangles even when the mesh is single-sided.
The following diagram shows what happens with different flags, for a single raycast intersecting a mesh in several places.
.. image:: ../images/RayMeshDS_Permutations.png
:scale: 75 %
To use *PxHitFlag::eMESH_BOTH_SIDES* for selected meshes rather than all, set the flag inside the *PxQueryFilterCallback*.
If the start or end point of a ray is very close to the surface of a triangle, it may be treated as being on either side of the triangle.
If the start or end point of a ray is very close to the surface of a triangle, it may be treated as being on the either side of the triangle.
++++++++++++++++++++++++++++++
Raycasts against Heightfields
++++++++++++++++++++++++++++++
* Heightfields are treated the same way as triangle meshes with normals oriented (in shape space) in +y direction.
* Double-sided heightfields are treated the same way as double sided triangle meshes.
===============================================================================
========
Overlaps
========
.. image:: ../images/GeomQueryOverlap.png
Overlap queries simply check whether two geometry objects overlap. One of the geometries must be a box, sphere, capsule or convex, and the other may be of any type.
The following code illustrates how to use an overlap query::
bool isOverlapping = overlap(geom0, pose0, geom1, pose1);
Overlaps do not support hit flags and return only a boolean result.
* A plane is treated as a solid half-space: that is, everything behind the plane is considered part of the volume.
* Triangle meshes are treated as thin triangle surfaces rather than solid objects.
* Heightfields are treated as triangle surface. Overlap geometries that do not intersect with the heightfield surface will not report a hit.
If more than a boolean result is needed for meshes and heightfields, use the *PxMeshQuery* API instead (see :ref:`MeshQuery`).
===============================================================================
=================
Penetration Depth
=================
.. image:: ../images/GeomQueryPenetration.png
When two objects are intersecting, PhysX can compute the minimal distance and direction by which the objects must be translated to separate them (this quantity is sometimes referred to as MTD, for *minimum translational distance*, as it is the vector of minimal length by which translation will separate the shapes). One geometry object must be a box, sphere, capsule or convex mesh, and the other may be of any type.
The following code illustrates how to use a penetration depth query::
bool isPenetrating = PxGeometryQuery::computePenetration(direction, depth,
geom0, pose0,
geom1, pose1);
The arguments are interpreted as follows:
* *direction* is set to the direction in which the first object should be translated in order to depenetrate from the second.
* *distance* is set to the distance by which the first object should be translated in order to depenetrate from the second.
* *geom0* is the first geometry.
* *pose0* is the transform of the first geometry.
* *geom1* is the second geometry.
* *pose2* is the transform of the second geometry.
The function returns true if the objects are penetrating, in which case it sets the direction and depth fields. Translating the first object by the depenetration vector D = direction * depth will separate the two objects. If the function returns true, the returned depth will always be positive or zero. If objects do not overlap, the function returns false, and the values of the direction and distance fields are undefined.
For simple (convex) shapes, returned results are accurate.
For meshes and heightfields, an iterative algorithm is used and dedicated functions are exposed in *PxExtensions*::
PxU32 nb;
bool status = PxComputeTriangleMeshPenetration(direction, depth,
geom, geomPose,
meshGeom, meshPose,
maxIter, &nb);
PxU32 nb;
bool status = PxComputeHeightFieldPenetration(direction, depth,
geom, geomPose,
heightFieldGeom, heightFieldPose,
maxIter, &nb);
Here, *maxIter* is the maximum number of iterations for the algorithm, and *nb* is an optional output argument which will be set to the number of iterations performed. If no overlap is detected, the function returns false. The code will attempt at most maxIter iterations but may exit earlier if a depenetration vector is found. Usually maxIter = 4 gives good results.
These functions only compute an approximate depenetration vector, and work best when the amount of overlap between the geometry object and the mesh/heightfield is small. In particular, an intersection with a triangle will be ignored when the object's center is behind the triangle, and if this holds for all intersecting triangles then no overlap is detected, and the functions do not compute an MTD vector.
===============================================================================
======
Sweeps
======
.. image:: ../images/GeomQuerySweep.png
A sweep query traces one geometry object through space to find the impact point on a second geometry object, and reports information concerning the impact point if one is found. PhysX only supports sweep queries where the first geometry object (the one that is traced through space) is a sphere, box, capsule or convex geometry. The second geometry object may be of any type.
The following code illustrates how to use a sweep query::
PxSweepHit hitInfo;
PxHitFlags hitFlags = PxHitFlag::ePOSITION|PxHitFlag::eNORMAL;
PxReal inflation = 0.0f;
PxU32 hitCount = PxGeometryQuery::sweep(unitDir, maxDist,
geomToSweep, poseToSweep,
geomSweptAgainst, poseSweptAgainst,
hitInfo,
hitFlags,
inflation);
The arguments are interpreted as follows:
* *unitDir* is a unit vector defining the direction of the sweep.
* *maxDist* is the maximum distance to search along the sweep. It must be in the [0, inf) range, and is clamped by SDK code to at most *PX_MAX_SWEEP_DISTANCE*. A sweep of length 0 is equivalent to an overlap check.
* *geomToSweep* is the geometry to sweep. Supported geometries are: box, sphere, capsule or convex mesh.
* *poseToSweep* is the initial pose of the geometry to sweep.
* *geomSweptAgainst* is the geometry to sweep against (any geometry type can be used here).
* *poseSweptAgainst* is the pose of the geometry to sweep against.
* *hitInfo* is the returned result. A sweep will return at most one hit.
* *hitFlags* determines how the sweep is processed, and which data is returned if an impact is found.
* *inflation* inflates the first geometry with a shell extending outward from the object surface, making any corners rounded. It can be used to ensure a minimum margin of space is kept around the geometry when using sweeps to test whether movement is possible.
As with raycasts, fields will be filled in the output structure if the corresponding flags were set in the input hitFlags. The fields of PxSweepHit are as follows::
PxRigidActor* actor;
PxShape* shape;
PxVec3 position;
PxVec3 normal;
PxF32 distance;
PxHitFlags flags;
PxU32 faceIndex;
* *actor* and *shape* are not filled (these fields are used only in scene-level sweeps, see :ref:`SceneQueries`).
* *position* (*PxHitFlag::ePOSITION*) is the position of the intersection. When there are multiple impact points, such as two boxes meeting face-to-face, PhysX will select one point arbitrarily. More detailed information for meshes or height fields may be obtained using the functions in :ref:`MeshQuery`.
* *normal* (*PxHitFlag::eNORMAL*) is the surface normal at the point of impact. It is a unit vector, pointing outwards from the hit object and backwards along the sweep direction (in the sense that the dot product between the sweep direction and the impact normal is negative).
* *distance* is the distance along the ray at which the intersection was found.
* *flags* specifies which fields of the structure are valid.
* *faceIndex* is the index of the face hit by the sweep. This is a face from the hit object, not from the swept object. For triangle mesh and height field intersections, it is a triangle index. For convex mesh intersections it is a polygon index. For other shapes it is always set to 0xffffffff. For convex meshes the face index computation is rather expensive. The face index computation can be disabled by not providing the scene query hit flag *PxHitFlag::eFACE_INDEX*. If needed the face index can also be computed externally using the function *PxFindFaceIndex* which is part of the PhysX extensions library.
Unlike raycasts, u,v coordinates are not supported for sweeps.
For the geometry object swept against:
* A plane is treated as a solid half-space: that is, everything behind the plane is considered part of the volume to sweep against.
* The same backface-culling rules as for raycasts apply for sweeps, with the notable difference that *eMESH_MULTIPLE* is not supported.
++++++++++++++++
Initial Overlaps
++++++++++++++++
Similarly to a raycast starting inside an object, a sweep may start with the two geometries initially intersecting. By default PhysX will detect and report the overlap. Use *PxSweepHit::hadInitialOverlap()* to see if the hit was generated by an initial overlap.
For triangle meshes and height fields, backface culling is performed before overlap checks, and thus no initial overlap is reported if a triangle is culled.
Depending on the value of *PxHitFlag::eMTD*, PhysX may also calculate the MTD. If *PxHitFlag::eMTD* is not set:
* the distance is set to zero.
* the normal is set to be the opposite of the sweep direction, and the *PxHitFlag::eNORMAL* flag is set in the *PxSweepHit* result structure.
* the position is undefined, and the *PxHitFlag::ePOSITION* flag is *not* set in the *PxSweepHit* result structure.
* the faceIndex is a face from the second geometry object. For a heightfield or triangle mesh, it is the index of the first overlapping triangle found. For other geometry types, the index is set to 0xffffffff.
If *PxHitFlag::eMTD* is set, the hit results are defined as follows:
* the distance is set to the penetration depth.
* the normal is set to the depenetration direction, and the *PxHitFlag::eNORMAL* flag is set in the *PxSweepHit* result structure.
* the position is a point on the sweep geometry object (i.e. the first geometry argument) and the *PxHitFlag::ePOSITION* flag is set in the *PxSweepHit* result structure.
* the faceIndex is a face from the second geometry object:
* For triangle meshes and heightfields it is the last penetrated triangle found during the last iteration of the depenetration algorithm.
* For other geometry types, the index is set to 0xffffffff.
This flag will incur additional processing overhead in the case of an initial overlap. In addition, the following restrictions apply:
* *PxHitFlag::eMTD* is incompatible with *PxHitFlag::ePRECISE_SWEEP* and *PxHitFlag::eASSUME_NO_INITIAL_OVERLAP* (see below). Using *PxHitFlag::eMTD* in conjunction with either of these flags will result in a warning being issued and the flag(s) that are incompatible with *PxHitFlag::eMTD* being ignored.
Testing for initial overlaps sometimes uses a specialized code path and incurs a performance penalty. If is it possible to guarantee that geometry objects are not initially overlapping, the check for overlaps can be suppressed with *PxHitFlag::eASSUME_NO_INITIAL_OVERLAP*. There are some restrictions on the use of this flag (also, see :ref:`sweep_pitfalls`)
* Using *PxHitFlag::eASSUME_NO_INITIAL_OVERLAP* flag when the geometries initially overlap produces undefined behavior.
* *PxHitFlag::eASSUME_NO_INITIAL_OVERLAP* in combination with zero sweep distance produces a warning and undefined behavior.
.. note:: sweeps with *PxHitFlag::eMTD* use two kinds of backface culling for triangles. First, the triangles are culled based on sweep direction to determine whether there is an overlap. If an overlap is detected, they are further culled by whether the centroid is behind the triangle, and if no triangles are found, the direction will be set opposite to the sweep direction and the distance to 0.
.. note:: in most cases, translating the first geometry object by -normal*distance will separate the objects. However, an iterative depenetration algorithm is used to find the MTD for triangle meshes and height fields, and the MTD result may not provide complete depenetration from the mesh in extreme cases. In this case the query should be called a second time after the translation has been applied.
.. note:: a known issue in PhysX 3.3 is that the face index for a sweep against a convex mesh is undefined when the eMTD flag is not set.
++++++++++++++
Precise Sweeps
++++++++++++++
*PxHitFlag::ePRECISE_SWEEP* enables more accurate sweep code (by default a potentially faster but less accurate solution is used). The *ePRECISE_SWEEP* flag is not compatible with the inflation parameter, or with the flag *PxHitFlag::eMTD*.
++++++++++++++++++++++++++++
Sweeps against Height Fields
++++++++++++++++++++++++++++
* Height fields are treated as thin triangle surfaces rather than solid objects.
* For single-sided height fields the normal of the hit will face in +y local space direction.
* Height fields are treated as double sided if either one of eDOUBLE_SIDED or eMESH_BOTH_SIDES flags are used.
* The returned hit normal will always face the sweep direction.
* eMESH_ANY flag has no effect.
* ePRECISE_SWEEP flag has no effect.
.. _sweep_pitfalls:
++++++++
Pitfalls
++++++++
There are some pitfalls to be aware of when using sweeps:
* Due to numerical precision issues, incorrect results may be returned when the objects have very large size disparities.
* Due to algorithmic differences, a sweep query may detect a different set of initially overlapping shapes than an overlap query. In particular, it is not sufficient to perform an overlap check in order to determine the safety of the *PxHitFlag::eIGNORE_INITIAL_OVERLAP* flag. Applications that need consistent overlap/sweep/penetration depth information should use sweep checks with initial overlap testing and the *PxHitFlag::eMTD* flag.
===============================================================================
====================================
Additional PxGeometryQuery functions
====================================
.. image:: ../images/GeomQueryPointDistance.png
The following function computes the distance between a point and a geometry object. Only solid objects (box, sphere, capsule, convex) are supported::
PxReal dist = PxGeometryQuery::pointDistance(point, geom, pose, closestPoint);
*closestPoint* is an optional output argument which returns the closest point.
.. image:: ../images/GeomQueryWorldBounds.png
The following function computes the axis-aligned bounding box (AABB) enclosing a geometry object, given its pose::
PxBounds3 bounds = PxGeometryQuery::getWorldBounds(geom, pose, inflation);
The bounding box is scaled by the *inflation* value, which defaults to 1.01f if not explicitly specified.
===============================================================================
.. _MeshQuery:
===========
PxMeshQuery
===========
PhysX provides additional functionality for obtaining multiple results for triangle mesh and height field overlaps, and for sweeping against arrays of triangles. Only boxes, spheres and capsules may be tested against meshes or heightfields using these functions.
+++++++++++++
Mesh Overlaps
+++++++++++++
The following code illustrates how to process the mesh triangles touching a given spherical volume::
PxU32 triangleIndexBuffer[bufferSize];
PxU32 startIndex = 0;
bool bufferOverflowOccured = false;
PxU32 nbTriangles = PxMeshQuery::findOverlapTriangleMesh(sphereGeom, spherePose,
meshGeom, meshPose,
triangleIndexBuffer, bufferSize,
startIndex, bufferOverflowOccured);
for(PxU32 i=0; i < nbTriangles; i++)
{
PxTriangle tri;
PxU32 vertexIndices[3];
PxMeshQuery::getTriangle(meshGeom, meshPose, triangleIndexBuffer[i], tri, vertexIndices);
... // process triangle info
}
The *findOverlapTriangleMesh* method is used to extract the indices of the triangles:
* *sphereGeom* and *spherePose* specify the region to test for overlap.
* *meshGeom* and *meshPose* specify the mesh and its pose.
* *triangleIndexBuffer* and *triangleSize* specify the output buffer and its size.
* *startIndex* is used to restart the query if the buffer size is exceeded. In this case, to query for more triangles set this parameter to the number retrieved so far.
* *bufferOverflowOccured* is set if more triangles would be returned from the query than would fit in the buffer.
Similar query functionality exists for height fields.
++++++++++++++++++++++++
Sweeps against Triangles
++++++++++++++++++++++++
Sometimes, for example, when using the mesh overlap API, it is convenient to be able to sweep against groups of triangles. PhysX provides a function specifically for this purpose, with the following signature::
bool sweep(const PxVec3& unitDir,
const PxReal distance,
const PxGeometry& geom,
const PxTransform& pose,
PxU32 triangleCount,
const PxTriangle* triangles,
PxSweepHit& sweepHit,
PxHitFlags hitFlags = PxHitFlag::eDEFAULT,
const PxU32* cachedIndex = NULL,
const PxReal inflation = 0.0f,
bool doubleSided = false);
The arguments are interpreted as follows:
* *unitDir*, *distance*, *geom* and *pose* function identically to the first four parameters of PxGeometryQuery::sweep(). *distance* is clamped to PX_MAX_SWEEP_DISTANCE.
* *triangleCount* is the number of triangles contained in the buffer against which to sweep.
* *triangles* is the buffer of triangles.
* *hitFlags* specifies the required information in the output.
* *cachedIndex*, if set, specifies the index of a triangle to test first. This can be a useful optimization when repeatedly sweeping against the same set of triangles.
* *inflation* functions identically to the inflation parameter of PxGeometryQuery::sweep().
* *doubleSided* indicates whether the input triangles are double-sided or not. This is equivalent to the *PxMeshGeometryFlag::eDOUBLE_SIDED* flag - that is, it suppresses backface culling, and for any hit the returned normal faces opposite to the sweep direction (see :ref:`raycasts_vs_meshes`).
This function has extra limitations compared to the other sweep queries:
* the geometry type must be either a sphere, a capsule or a box. Convex geometry is not supported.
* the function returns a single hit. Multiple hits (and in particular PxHitFlag::eMESH_MULTIPLE) are not supported.
* The function always returns the closest hit.
* The only supported flags are PxHitFlag::eDEFAULT, PxHitFlag::eASSUME_NO_INITIAL_OVERLAP, PxHitFlag::ePRECISE_SWEEP, PxHitFlag::eMESH_BOTH_SIDES and PxHitFlag::eMESH_ANY.
The function tests each input triangle in the order they are given. By default, the function will test all triangles and return the closest sweep hit (if a hit has been found). If PxHitFlag::eMESH_ANY is used, the function will return as soon as a hit is found (skipping the remaining untested triangles). This flag can also be used to emulate PxHitFlag::eMESH_MULTIPLE, by calling the function repeatedly with PxHitFlag::eMESH_ANY, using as a starting point the previously returned hit triangle (whose index, between 0 and 'triangleCount', is available in sweepHit.faceIndex).

View File

@ -0,0 +1,50 @@
.. _HelloWorld:
-----------------
Snippets
-----------------
========================
What are PhysX Snippets?
========================
In the context of the PhysX SDK, a 'Snippet' is a simple, minimalistic code sample.
PhysX-SDK version 3.3.0 offers a collection of Snippets to illustrate usage of the PhysX API in a
concise format, free from the complexity of a sample framework or game engine. The Snippets folder is
in the top-level directory of the PhysX SDK, alongside directories for Documentation, Include, Samples, etc.
The folder {SDK Root}/Snippets/compiler/{platform} contains the Snippets solution file, e.g.
Snippets/compiler/vc14win64/Snippets.sln
Although a few of the Snippets support rendering, ( Win32, Win64, OSX and Linux only ) most Snippets do not provide rendering, require no input,
and provide only limited output through messages. Although Snippets can be run from a command prompt or by double-clicking the executable
icon, the best way to explore Snippets is by viewing the code in the Visual Studio IDE, and running the program in the debugger.
.. _HelloWorldBasics:
===============================
HelloWorld: PhysX Basics
===============================
SnippetHelloWorld illustrates basic use of PhysX, from startup to shutdown of a simple scene, and
is a good place to start learning the PhysX API. The simplest Snippets comprise a single source file, but SnippetHelloWorld,
among others, supports optional rendering through a second source file. SnippetHelloWorld creates a number of box stacks on
a plane, and if rendering is enabled, allows the user to create new stacks and fire a ball from the camera position.
The primary code for SnippetHelloWorld is found in {SDK Root}/Snippets/SnippetHelloWorld/SnippetHelloWorld.cpp.
.. _PVDandHelloWorld:
==================================================
Using PhysX Visual Debugger with SnippetHelloWorld
==================================================
As is the case with any Snippet built against PROFILE, CHECKED or DEBUG configurations of the PhysX runtime,
HelloWorld will automatically connect to the PhysX Visual Debugger if that application is already running
when the Snippet executable is launched. For Snippets without rendering, PVD provides an easy way to visualize
the contents of the PhysX scene presented in the Snippet. In the screenshot image below, PhysX Visual Debugger appears on
the right hand side, while Visual Studio and Snippet Hello World are on the left.
.. image:: ../images/HelloWorld.png

View File

@ -0,0 +1,50 @@
User's Guide
============
.. image:: ../images/PhysXLogoBlack.png
Built |today|
Contents:
.. toctree::
:maxdepth: 3
License
Introduction
HelloWorld
BuildingWithPhysX
API
Startup
Threading
Geometry
RigidBodyOverview
RigidBodyCollision
RigidBodyDynamics
Simulation
AdvancedCollisionDetection
Joints
Articulations
OriginShift
GPURigidBodies
GeometryQueries
SceneQueries
Vehicles
CharacterControllers
DebugVisualization
VisualDebugger
Statistics
Serialization
ExtendingSerialization
BestPractices
MigrationFrom28
MigrationTo33
MigrationTo34
MigrationTo40

View File

@ -0,0 +1,58 @@
.. _INTRODUCTION:
.. |reg| unicode:: U+000AE .. REGISTERED SIGN
:ltrim:
----------------
Welcome to PhysX
----------------
Welcome to the NVIDIA |reg| PhysX |reg| SDK version 4! Unlike the change from version 2 to 3, the upgrade to version 4 does not bring any significant API changes, and in fact most interfaces have not changed since version 3.4. The behaviors of existing algorithms have also largely stayed the same. Migrating to version 4 should be very easy and straightforward -- see :ref:`migrationTo40`. Nonetheless we have decided to mark this release with a major version increment because we believe it is hard to overstate the significance of two major innovations we have added: The TGS solver and the reduced coordinate articulations feature. These new simulation options are taking PhysX from being a leading game engine technology to also being a highly accurate engineering simulation tool suitable for robotics R&D and reinforcement learning, while maintaining best in class performance for all application domains. See :ref:`TemporalGaussSeidel` and :ref:`Articulations` for more information on these new features.
=====================
About this User Guide
=====================
This Guide will help the reader to understand the features of the PhysX SDK and how to make use of them. The Guide is not exhaustive on the details of the API; the reader should refer to the PhysX API Reference Documentation for this. ( See Documentation/physxapi/_build/html under the main directory where the PhysX
SDK distro was unpacked.) Furthermore this guide does not currently go into detail about the algorithms used in PhysX.
The github PhysX 4 Issues page is a good place to ask questions and report bugs.
==========================
A brief overview of PhysX
==========================
PhysX is a library for representing three dimensional worlds made of discrete entities named actors which can in turn be composed of multiple shapes. PhysX lets the user create and destroy such actors, and tracks their explicit or proximity based interactions. Actors can either be static, be moved around by the user, or be moved by PhysX according to the laws of classical mechanics. PhysX' dynamics simulation capability includes support for collision, joints and actuation using maximal and/or reduced coordinates. Furthermore, the world may be queried by the user using a number of different tools ranging from simple ray-casts to sweep and overlap tests. PhysX provides extensions for special purpose functionality such as vehicle simulation.
PhysX is designed to be robust, high performance, scalable, portable, as well as easy to integrate and use. These capabilities make PhysX suitable as a foundation technology for game engines and other real time simulation systems.
It is important to note that PhysX does not run any code on GPUs by default. However PhysX can be configured to take advantage of CUDA capable GPUs, which provides a performance benefit proportional to the arithmetic complexity of a scene. GPU acceleration extensions are provided as an optional binary DLL. See :ref:`GPURigidBodies` for details on how to enable GPU acceleration. Even without GPU acceleration, PhysX is fully multithreaded and SIMD-accelerated to take full advantage of modern multi-core CPUs.
=================
World and Objects
=================
The basic concepts of the world within a PhysX simulation are easy to describe:
* The PhysX world comprises a collection of Scenes, each containing objects called Actors;
* Each Scene defines its own reference frame encompassing all of space and time;
* Actors in different Scenes do not interact with each other;
* Characters and vehicles are complex specialized objects made from Actors;
* Actors have physical state : position and orientation; velocity or momentum; energy; etc,
* Actor physical state may evolve over time due to applied forces, constraints such as joints or contacts, and interactions between Actors.
============================
Rendering and Visualization
============================
3d rendering will of course be a vital technology to visualize the simulated world in almost every application. Production quality graphics and audio are
outside the scope of PhysX. At the minimum the user needs to take the updated state of any moved visible actors and update them in the respective rendering representation. Some of our example programs come with rudimentary built-in visualization, and we also provide a stand-alone debugging tool called PhysX Visual Debugger (PVD). PVD provides a graphical view of the PhysX scene together with various tools to inspect and visualize variables of every PhysX object. Additionally it can also record and visualize memory and timing data. See :ref:`physxvisualdebugger` for details.

View File

@ -0,0 +1,625 @@
.. _Joints:
---------------------
Joints
---------------------
.. |pi| unicode:: U+003C0
=====================
Joint Basics
=====================
A joint constrains the way two actors move relative to one another. A typical use for a joint would be to model a door hinge or the shoulder of a character. Joints are implemented in the PhysX extensions library and cover many common scenarios, but if you have use cases that are not met by the joints packaged with PhysX, you can implement your own. Since joints are implemented as extensions, the pattern for creating them is slightly different from other PhysX objects.
Creation of simple joints and limits is demonstrated in the SnippetJoint snippet.
To create a joint, call the joint's creation function::
PxRevoluteJointCreate(PxPhysics& physics,
PxRigidActor* actor0, const PxTransform& localFrame0,
PxRigidActor* actor1, const PxTransform& localFrame1);
This has the same pattern for all joints: two actors, and for each actor a constraint frame.
One of the actors must be movable, either a *PxRigidDynamic* or a *PxArticulationLink*. The other may be of one of those types, or a *PxRigidStatic*. Use a NULL pointer here to indicate an implicit actor representing the immovable global reference frame.
Each localFrame argument specifies a constraint frame relative to the actor's global pose. Each joint defines a relationship between the global positions and origins of the constraint frames that will be enforced by the PhysX constraint solver. In this example, the revolute joint constrains the origin points of the two frames to be coincident and their x-axes to coincide, but allows the two actors to rotate freely relative to one another around this common axis.
PhysX supports six different joint types:
* a **fixed** joint locks the orientations and origins rigidly together
* a **distance** joint keeps the origins within a certain distance range
* a **spherical** joint (also called a *ball-and-socket*) keeps the origins together, but allows the orientations to vary freely.
* a **revolute** joint (also called a *hinge*) keeps the origins and x-axes of the frames together, and allows free rotation around this common axis.
* a **prismatic** joint (also called a *slider*) keeps the orientations identical, but allows the origin of each frame to slide freely along the common x-axis.
* a **D6** joint is a highly configurable joint that allows specification of individual degrees of freedom either to move freely or be locked together. It can be used to implement a wide variety of mechanical and anatomical joints, but is somewhat less intuitive to configure than the other joint types. This joint is covered in detail below.
All joints are implemented as plugins to the SDK through the PxConstraint class. A number of the properties for each joint are configured using the PxConstraintFlag enumeration.
.. note:: As in the rest of the PhysX API, all joint angles for limits and drive targets are specified in radians.
+++++++++++++
Visualization
+++++++++++++
All standard PhysX joints support debug visualization. You can visualize the joint frames of each actor, and also any limits the joint may have.
By default, joints are not visualized. To visualize a joint, set its visualization constraint flag and the appropriate scene-level visualization parameters::
scene->setVisualizationParameter(PxVisualizationParameter::eJOINT_FRAMES, 1.0f);
scene->setVisualizationParameter(PxVisualizationParameter::eJOINT_LIMITS, 1.0f);
...
joint->setConstraintFlag(PxConstraintFlag::eVISUALIZATION)
+++++++++++++++
Force Reporting
+++++++++++++++
The force applied at a joint may be retrieved after simulation with a call to getForce()::
scene->fetchResults(...)
joint->getConstraint().getForce(force, torque);
The force is resolved at the origin of actor1's joint frame.
Note that this force is only updated while the joint's actors are awake.
++++++++
Breakage
++++++++
All of the standard PhysX joints can be made *breakable*. A maximum breaking force and torque may be specified, and if the force or torque required to maintain the joint constraint exceeds this threshold, the joint will break. Breaking a joint generates a simulation event (see PxSimulationEventCallback::onJointBreak), and the joint no longer partakes in simulation, although it remains attached to its actors until it is deleted.
By default the threshold force and torque are set to FLT_MAX, making joints effectively unbreakable. To make a joint breakable, specify the force and torque thresholds.
joint->setBreakForce(100.0f, 100.0f);
A constraint flag records whether a joint is currently broken:
bool broken = (joint->getConstraintFlags() & PxConstraintFlag::eBROKEN) != 0;
Breaking a joint causes a callback via PxSimulationEventCallback::onConstraintBreak. In this callback, a pointer to the joint and its type are specified in the externalReference and type field of the PxConstraintInfo struct. If you have implemented your own joint types, use the PxConstraintInfo::type field to determine the dynamic type of the broken constraint. Otherwise, simply cast the externalReference to a PxJoint::
class MySimulationEventCallback
{
void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count)
{
for(PxU32 i=0; i<count; i++)
{
PxJoint* joint = reinterpret_cast<PxJoint*>(constraints[i].externalReference);
...
}
}
}
++++++++++
Projection
++++++++++
Under stressful conditions, PhysX' dynamics solver may not be able to accurately enforce the constraints specified by the joint. PhysX provides kinematic *projection* which tries to bring violated constraints back into alignment even when the solver fails. Projection is not a physical process and does not preserve momentum or respect collision geometry. It is best avoided if practical, but can be useful in improving simulation quality where joint separation results in unacceptable artifacts.
By default projection is disabled. To enable projection, set the linear and angular tolerance values beyond which a joint will be projected, and set the constraint projection flag::
joint->setProjectionLinearTolerance(0.1f);
joint->setConstraintFlag(PxConstraintFlag::ePROJECTION, true);
Very small tolerance values for projection may result in jittering around the joint.
A constraint with projection enabled can be part of a graph of rigid bodies connected by constraints. If this graph is acyclic, the algorithm will choose a root node among the connected rigid bodies, traverse the graph, and project the bodies towards the root. If the constraint graph has cycles, the algorithm will split the graph into multiple acyclic subgraphs, dropping edges that create cycles, and do the projection separately for each. Please note that having more than one constraint attached to a fixed anchor (world or static/kinematic rigid body) in a graph does count as a cycle (for example, a chain of rigid bodies connected with constraints and both ends attached to world anchors).
If multiple constraints fight over the same body or conflicting projection directions are specified, the projection direction will be chosen based on the following priorities (highest first):
* world attachment or a rigid static actor with a projecting constraint
* kinematic actor with a projecting constraint
* all dominant dynamic actor (has projecting constraints and all of them are one-way projecting towards this dynamic)
* dominant dynamic actor (same as above but there is at least one two-way projecting constraint as well)
* partially dominant dynamic actor (has at least one one-way projecting constraint towards this dynamic and at least one one-way projecting constraint towards an other actor)
* world attachment or a rigid static actor without any projecting constraints
* kinematic actor without any projecting constraints
* dynamic actor with or without two-way projecting constraints to other dynamics (among these, the one with the highest constraint count wins)
++++++
Limits
++++++
Some PhysX joints constrain not just relative rotation or translation, but can also enforce *limits* on the range of that motion. For example, in its initial configuration the revolute joint allows free rotation around its axis, but by specifying and enabling a limit, lower and upper bounds may be placed upon the angle of rotation.
Limits are a form of collision, and like collision of rigid body shapes, stable limit behavior requires a *contactDistance* tolerance specifying how far from the limit the joint configuration may be before the solver tries to enforce it. Note that enforcement always starts before the limit is violated, so the role played by contactDistance for limits is analogous to the role a positive contactDistance value plays in collision detection. A larger contact makes the limit less likely to be violated even at high relative velocity. However, because the limit is active more of the time, the joint is more expensive to simulate.
Limit configuration is specific to each type of joint. To set a limit, configure the limit geometry and set the joint-specific flag indicating that the limit is enabled::
revolute->setLimit(PxJointAngularLimitPair(-PxPi/4, PxPi/4, 0.1f)); // upper, lower, tolerance
revolute->setRevoluteJointFlag(PxRevoluteJointFlag::eLIMIT_ENABLED, true);
Limits may be either *hard* or *soft*. When a hard limit is reached, relative motion will simply stop dead if the limit is configured with zero restitution, or bounce if the restitution is non-zero. When a soft limit is violated, the solver will pull the joint back towards the limit using a spring specified by the limit's spring and damping parameters. By default, limits are hard and without restitution, so when the joint reaches a limit motion will simply stop. To specify softness for a limit, declare the limit structure and set the spring and damping parameters directly::
PxJointAngularLimitPair limitPair(-PxPi/4, PxPi/4, 0.1f));
limitPair.spring = 100.0f;
limitPair.damping = 20.0f;
revolute->setRevoluteJointLimit(limitPair);
revolute->setRevoluteJointFlag(PxRevoluteJointFlag::eLIMIT_ENABLED, true);
.. note:: Limits are not projected.
When using spring limits, the eACCELERATION flag is strongly recommended. This flag will automatically scale the strength of the spring according to the masses and inertias of objects that the limit is acting upon, and can substantially reduce the amount of tuning required for good, stable behavior.
+++++++++
Actuation
+++++++++
Some PhysX joints may be actuated by a motor or a spring implicitly integrated by the PhysX solver. While driving simulations with actuated joints is more expensive than simply applying forces, it can provide much more stable control of simulation. See :ref:`PxD6Joint`, :ref:`PxPrismaticJoint`, and :ref:`PxRevoluteJoint` for details
.. note:: The force generated by actuation is not included in the force reported by the solver, nor does it contribute towards exceeding the joint's breakage force threshold.
.. note:: Changing the drive parameters for a joint, or activating or deactivating the drive, does not wake sleeping bodies attached to the joint. If required, wake these bodies manually.
When using spring drives (in particular, drives on the D6 joint), the eACCELERATION flag is strongly recommended. This flag will automatically scale the strength of the spring according to the masses and inertias of objects that the limit is acting upon, and can substantially reduce the amount of tuning required for good, stable behavior.
.. _joint_mass_scaling:
++++++++++++
Mass Scaling
++++++++++++
PhysX joints may apply scale to the mass and moment of inertia of the two connected bodies for the purposes of resolving a joint. For example, if you have two objects in a ragdoll of masses 1 and 10, PhysX will typically resolve the joint by changing the velocity of the lighter body much more than the heavier one. You can apply a mass scale of 10 to the first body to make PhysX change the velocity of both bodies by an equal amount. To ensure the same property holds for both linear and angular velocity, you should adjust the inertia scales in accordance with the bodies' inertias as well. Applying mass scales such that the joint sees similar effective masses and inertias makes the solver converge faster, which can make individual joints seem less rubbery or separated, and sets of jointed bodies appear less twitchy
Many applications that prioritize visual behavior over adherence to physical laws can benefit from tuning these scale values. But if you use this feature, bear in mind that mass and inertia scaling is fundamentally nonphysical. In general momentum will not be conserved, the energy of the system may increase, the force reported for the joint may be incorrect, and non-physical tuning of breakage thresholds and force limits may be required.
===============
Fixed Joint
===============
.. image:: ../images/fixedJoint.png
The fixed joint constrains two objects so that the positions and orientations of their constraint frames are the same.
.. note:: All joints are enforced by the dynamics solver, so although under ideal conditions the objects will maintain their spatial relationship, there may be some drift. A common alternative, which is cheaper to simulate and does not suffer from drift, is to construct a single actor with multiple shapes. However fixed joints are useful, for example, when a joint must be breakable or report its constraint force.
===============
Spherical Joint
===============
.. image:: ../images/sphericalJoint.png
A spherical joint constrains the origins of the actor's constraint frames to be coincident.
The spherical joint supports a cone limit, which constrains the angle between the X-axes of the two constraint frames. Actor1's X-axis is constrained by a limit cone whose axis is the x-axis of actor0's constraint frame. The allowed limit values are the maximum rotation around the y- and z- axes of that frame. Different values for the y- and z- axes may be specified, in which case the limit takes the form of an elliptical angular cone::
joint->setLimitCone(PxJointLimitCone(PxPi/2, PxPi/6, 0.01f);
joint->setSphericalJointFlag(PxSphericalJointFlag::eLIMIT_ENABLED, true);
Note that very small or highly elliptical limit cones may result in solver jitter.
.. note:: *Visualization of the limit surface can help considerably in understanding its shape.*
.. _PxRevoluteJoint:
===============
Revolute Joint
===============
.. image:: ../images/revoluteJoint.png
A revolute joint removes all but a single rotational degree of freedom from two objects. The axis along which the two bodies may rotate is specified by the common origin of the joint frames and their common x-axis. In theory, all origin points along the axis of rotation are equivalent, but simulation stability is best in practice when the point is near where the bodies are closest.
The joint supports a rotational limit with upper and lower extents. The angle is zero where the y- and z- axes of the joint frames are coincident, and increases moving from the y-axis towards the z-axis::
joint->setLimit(PxJointLimitPair(-PxPi/4, PxPi/4, 0.01f);
joint->setRevoluteJointFlag(PxRevoluteJointFlag::eLIMIT_ENABLED, true);
The joint also supports a motor which drives the relative angular velocity of the two actors towards a user-specified target velocity. The magnitude of the force applied by the motor may be limited to a specified maximum::
joint->setDriveVelocity(10.0f);
joint->setRevoluteJointFlag(PxRevoluteJointFlag::eDRIVE_ENABLED, true);
By default, when the angular velocity at the joint exceeds the target velocity the motor acts as a brake; a freespin flag disables this braking behavior.
The drive force limit for a revolute joint may be interpreted either as a force or an impulse, depending on the value of PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES
.. _PxPrismaticJoint:
===============
Prismatic Joint
===============
.. image:: ../images/prismJoint.png
A prismatic joint prevents all rotational motion, but allows the origin of actor1's constraint frame to move freely along the x-axis of actor0's constraint frame. The prismatic joint supports a single limit with upper and lower bounds on the distance between the two constraint frames' origin points::
joint->setLimit(PxJointLimitPair(-10.0f, 20.0f, 0.01f);
joint->setPrismaticJointFlag(PxPrismaticJointFlag::eLIMIT_ENABLED, true);
===============
Distance Joint
===============
.. image:: ../images/distanceJoint.png
The distance joint keeps the origins of the constraint frames within a certain range of distance. The range may have both upper and lower bounds, which are enabled separately by flags::
joint->setMaxDistance(10.0f);
joint->setDistanceJointFlag(eMAX_DISTANCE_ENABLED, true);
In addition, when the joint reaches the limits of its range motion beyond this distance may either be entirely prevented by the solver, or pushed back towards its range with an implicit spring, for which spring and damping parameters may be specified.
.. _PxD6Joint:
========
D6 Joint
========
The D6 joint is by far the most complex of the standard PhysX joints. In its default state it behaves like a fixed joint - that is, it rigidly fixes the constraint frames of its two actors. However, individual degrees of freedom may be unlocked to permit any combination of rotation around the x-, y- and z- axes, and translation along these axes.
++++++++++++++++++++++++++
Locking and Unlocking Axes
++++++++++++++++++++++++++
To unlock and lock degrees of freedom, use the joint's setMotion function::
d6joint->setMotion(PxD6Axis::eX, PxD6Motion::eFREE);
Unlocking translational degrees of freedom allows the origin point of actor1's constraint frame to move along a subset of the axes defined by actor0's constraint frame. For example, unlocking just the X-axis creates the equivalent of a prismatic joint.
Rotational degrees of freedom are partitioned as *twist* (around the X-axis of actor0's constraint frame) and *swing* (around the Y- and Z- axes). Different effects are achieved by unlocking various combinations of twist and swing.
* if just a single degree of angular freedom is unlocked, the result is always equivalent to a revolute joint. It is recommended that if just one angular freedom is unlocked, it should be the twist degree, because the joint has various configuration options and optimizations that are designed for this case.
..
* if both swing degrees of freedom are unlocked but the twist degree remains locked, the result is a *zero-twist* joint. The x-axis of actor1 swings freely away from the x-axis of actor0 but twists to minimize the rotation required to align the two frames. This creates a kind of isotropic universal joint which avoids the problems of the usual 'engineering style' universal joint (see below) that is sometimes used as a kind of twist constraint. There is a nasty singularity at |pi| radians (180 degrees) swing, so a swing limit should be used to avoid the singularity.
..
* if one swing and one twist degree of freedom are unlocked but the remaining swing is kept locked, a *zero-swing* joint results (often also called a *universal* joint). If for example the SWING1 (y-axis rotation) is unlocked, the x-axis of actor1 is constrained to remain orthogonal to the z-axis of actor0. In character applications, this joint can be used to model an elbow swing joint incorporating the twist freedom of the lower arm or a knee swing joint incorporating the twist freedom of the lower leg. In vehicle applications, these joints can be used as 'steered wheel' joints in which the child actor is the wheel, free to rotate about its twist axis, while the free swing axis in the parent acts as the steering axis. Care must be taken with this combination because of anisotropic behavior and singularities (beware the dreaded gimbal lock) at angles of |pi|/2 radians (90 degrees), making the zero-twist joint a better behaved alternative for most use cases.
..
* if all three angular degrees are unlocked, the result is equivalent to a spherical joint.
Three of the joints from PhysX 2 that have been removed from PhysX 3 can be implemented as follows:
* The cylindrical joint (with axis along the common x-axis of the two constraint frames) is given by the combination::
d6joint->setMotion(PxD6Axis::eX, PxD6Motion::eFREE);
d6joint->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE);
* the point-on-plane joint (with plane axis along the x-axis of actor0's constraint frame) is given by the combination::
d6joint->setMotion(PxD6Axis::eY, PxD6Motion::eFREE);
d6joint->setMotion(PxD6Axis::eZ, PxD6Motion::eFREE);
d6joint->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE);
d6joint->setMotion(PxD6Axis::eSWING1, PxD6Motion::eFREE);
d6joint->setMotion(PxD6Axis::eSWING2, PxD6Motion::eFREE);
* the point-on-line joint (with axis along the x-axis of actor0's constraint frame) is given by the combination::
d6joint->setMotion(PxD6Axis::eX, PxD6Motion::eFREE);
d6joint->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE);
d6joint->setMotion(PxD6Axis::eSWING1, PxD6Motion::eFREE);
d6joint->setMotion(PxD6Axis::eSWING2, PxD6Motion::eFREE);
.. note:: Angular projection is implemented only for the cases when two or three angular degrees of freedom are locked.
++++++
Limits
++++++
Instead of specifying that an axis is free or locked, it may also be specified as limited. The D6 supports different limits which may be used in any combination.
A single linear limit with only an upper bound is used to constrain any of the translational degrees of freedom. The limit constrains the distance between the origins of the constraint frames when projected onto these axes. For example, the combination::
d6joint->setMotion(PxD6Axis::eX, PxD6Motion::eFREE);
d6joint->setMotion(PxD6Axis::eY, PxD6Motion::eLIMITED);
d6joint->setMotion(PxD6Axis::eZ, PxD6Motion::eLIMITED);
d6joint->setDistanceLimit(PxJointLinearLimit(1.0f, 0.1f));
constrains the y- and z- coordinates of actor1's constraint frame to lie within the unit disc. Since the x-axis is unconstrained, the effect is to constrain the origin of actor1's constraint frame to lie within a cylinder of radius 1 extending along the x-axis of actor0's constraint frame.
The D6 joint also supports per-axis linear limit pairs, with the *PxD6Joint::setLinearLimit/PxD6Joint::getLinearLimit* functions. This can be used to implement a prismatic joint when only one of the linear axis is limited and the others remain locked. Otherwise in 2D and 3D this gives birth to quad-shaped and box-shaped limit volumes, as opposed to the disc-shaped and sphere-shaped volumes generated by *PxD6Joint::setDistanceLimit*.
The twist degree of freedom is limited by a pair limit with upper and lower bounds, identical to the limit of the revolute joint.
If both swing degrees of freedom are limited, a limit cone is generated, identical to the limit of the spherical joint. As with the spherical joint, very small or highly elliptical limit cones may result in solver jitter.
If only one swing degree of freedom is limited, the corresponding angle from the cone limit is used to limit rotation. If the other swing degree is locked, the maximum value of the limit is |pi| radians (180 degrees). If the other swing degree is free, the maximum value of the limit is |pi|/2 radians (90 degrees).
++++++
Drives
++++++
The D6 has a linear drive model, and two possible angular drive models. The drive is a *proportional derivative* drive, which applies a force as follows:
*force = spring * (targetPosition - position) + damping * (targetVelocity - velocity)*
The drive model may also be configured to generate a proportional acceleration instead of a force, factoring in the masses of the actors to which the joint is attached. Acceleration drive is often easier to tune than force drive.
The linear drive model for the D6 has the following parameters:
* target position, specified in actor0's constraint frame
* target velocity, specified in actor0's constraint frame
* spring
* damping
* forceLimit - the maximum force the drive can apply (note that this can be an impulse, depending on PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES)
* acceleration drive flag
The drive attempts to follow the desired position input with the configured stiffness and damping properties. A physical lag due to the inertia of the driven body acting through the drive spring will occur; therefore, sudden step changes will result over a number of time steps. Physical lag can be reduced by stiffening the spring or supplying a velocity target.
With a fixed position input and a zero target velocity, a position drive will spring about that drive position with the specified springing/damping characteristics::
// set all translational degrees free
d6joint->setMotion(PxD6Axis::eX, PxD6Motion::eFREE);
d6joint->setMotion(PxD6Axis::eY, PxD6Motion::eFREE);
d6joint->setMotion(PxD6Axis::eZ, PxD6Motion::eFREE);
// set all translation degrees driven:
PxD6Drive drive(10.0f, -20.0f, PX_MAX_F32, true);
d6joint->setDrive(PxD6JointDrive::eX, drive);
d6joint->setDrive(PxD6JointDrive::eY, drive);
d6joint->setDrive(PxD6JointDrive::eZ, drive);
// Drive the joint to the local(actor[0]) origin - since no angular
// dofs are free, the angular part of the transform is ignored
d6joint->setDrivePosition(PxTransform(1.0f));
d6joint->setDriveVelocity(PxVec3(PxZero));
Angular drive differs from linear drive in a fundamental way: it does not have a simple and intuitive representation free from singularities. For this reason, the D6 joint provides two angular drive models - twist and swing and SLERP (Spherical Linear Interpolation).
The two models differ in the way they estimate the path in quaternion space between the current orientation and the target orientation. In a SLERP drive, the quaternion is used directly. In a twist and swing drive, it is decomposed into separate twist and swing components and each component is interpolated separately. Twist and swing is intuitive in many situations; however, there is a singularity when driven to 180 degrees swing. In addition, the drive will not follow the shortest arc between two orientations. On the other hand, SLERP drive will follow the shortest arc between a pair of angular configurations, but may cause unintuitive changes in the joint's twist and swing.
The angular drive model has the following parameters:
* An angular velocity target specified relative to actor0's constraint frame
* An orientation target specified relative to actor0's constraint frame
* drive specifications for SLERP (slerpDrive), swing (swingDrive) and twist (twistDrive):
* spring - amount of torque needed to move the joint to its target orientation proportional to the angle from the target (not used for a velocity drive).
* damping - applied to the drive spring (used to smooth out oscillations about the drive target).
* forceLimit - the maximum torque the drive can apply (note that this can be an impulsive torque, depending on the value PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES)
* acceleration drive flag. If this flag is set the acceleration (rather than the force) applied by the drive is proportional to the angle from the target.
Best results will be achieved when the drive target inputs are consistent with the joint freedom and limit constraints.
.. note:: if any angular degrees of freedom are locked, the SLERP drive parameters are ignored. If all angular degrees of freedom are unlocked, and parameters are set for multiple angular drives, the SLERP parameters will be used.
++++++++++++++++++++++++++++++++++++
Configuring Joints for Best Behavior
++++++++++++++++++++++++++++++++++++
The behavior quality of joints in PhysX is largely determined by the ability of the iterative solver to converge. Better convergence can be achieved simply by increasing the attributes of the PxRigidDynamic which controls the solver iteration count. However, joints can also be configured to produce better convergence.
* the solver can have difficulty converging well when a light object is constrained between two heavy objects. Mass ratios of higher than 10 are best avoided in such scenarios.
* when one body is significantly heavier than the other, make the lighter body the second actor in the joint. Similarly, when one of the objects is static or kinematic (or the actor pointer is NULL) make the dynamic body the second actor.
A common use for joints is to move objects around in the world. Best results are obtained when the solver has access to the velocity of motion as well as the change in position.
* if you want a very stiff controller that moves the object to specific position each frame, consider jointing the object to a kinematic actor and use the setKinematicTarget function to move the actor.
* if you want a more springy controller, use a D6 joint with a drive target to set the desired position and orientation, and control the spring parameters to increase stiffness and damping. In general, acceleration drive is much easier to tune than force drive.
When using mass scaling or when constraining bodies with infinite inertia along some axes, the reduction in degrees of freedom of the rigid bodies combined with small inaccuracies in floating point calculation can produce arbitrarily stiff constraint responses trying to correct unnoticeably small errors. This can appear, for example, when attempting to perform 2D-simulation using infinite inertia to suppress velocity out of the plane of simulation. In these cases, set the flag PxConstraintFlag::eDISABLE_PREPROCESSING, and set the minResponseThreshold on the constraint to a small value, e.g. 1e-8. This will result in such stiff constraint rows being ignored when encountered, and can considerably improve simulation quality.
=====================
Custom Constraints
=====================
It is also possible to add new joint types to PhysX. Use the existing joints in the PhysXExtensions library as a reference, and also the source for SnippetCustomJoint, which shows how to implement a Pulley Joint. Serializing custom objects is discussed in the chapter :ref:`serialization`, so the discussion here is limited to how to achieve the desired behavior in simulation. This is an advanced topic, and assumes familiarity with the mathematics underlying rigid body simulation. The presentation here assumes that the joint constrains two bodies; the case for a static body is equivalent to a dynamic body of infinite mass whose transform is the identity.
The functions which implement dynamic behavior of joints are PhysX *shaders*, similar in nature to the PxFilterShader (see :ref:`collisionFiltering`). In particular, the functions may execute in parallel and asynchronously, and should not access any state except that passed in as parameters.
To create a custom joint class, define the following:
* the functions which implement the behavior of the constraint. The functions must be stateless, because they may be called simultaneously from multiple threads. When each function is called, PhysX passes a *constant block* which can be used to store the joint configuration parameters (offsets, axes, limits etc).
* a static instance of PxConstraintShaderTable containing pointers to the functions
* a class implementing the PxConstraintConnector interface, that connects the custom joint to PhysX.
++++++++++++++++++++++++++++
Defining Constraint Behavior
++++++++++++++++++++++++++++
There are two functions that define the joint behavior: the *solver preparation* function, which generates inputs to PhysX' velocity-based constraint solver, and the *projection* function, which allows direct correction of position error.
The processing sequence during simulation is as follows:
* in the simulate() function, before starting simulation the scene updates an internal copy of the joint's constant block (so that the joint's copy may be modified during simulation without causing races).
* collision detection runs, and may wake bodies. If the joint connects two bodies, the simulation will ensure that either both bodies are awake, or neither is.
* for every joint connected to an awake body, the simulation calls the solver preparation function.
* the solver updates body velocities and positions.
* if the constraint's ePROJECTION flag is set, the simulation calls the joint projection function.
The Solver Preparation Function
===============================
The solver preparation function for a joint has the following signature::
PxU32 prepare(Px1DConstraint* constraints,
PxVec3& bodyAWorldOffset,
PxU32 maxConstraints,
PxConstraintInvMassScale &invMassScale,
const void* constantBlock,
const PxTransform& bA2w,
const PxTransform& bB2w);
The parameters are as follows:
* *constraints* is the output buffer of constraint rows.
* *bodyAWorldOffset* is the point, specified in world space as an offset from the origin of bodyA, at which the constraint forces act to enforce the joint. The constraint solver ignores this value as the information is already encoded in the constraint array, but when reporting forces it is necessary to choose a point at which the force is considered to act. For PhysX joints, the attachment point of the joint on body B is used.
* *maxConstraints* is the size of the buffer, which limits the number of constraint rows that may be generated.
* *invMassScale* is the inverse mass scales which should be applied to the bodies for the purpose of resolving the joint. In the standard joints, these are just the joint's mass scaling parameters (see :ref:`joint_mass_scaling`).
* *constantBlock* is the simulation's copy of the joint constant block.
* *bA2w* is the transform of the first body. It is the identity transform if the actor is static, or a NULL pointer was supplied in constraint creation.
* *bB2w* is the transform of the second body. It is the identity transform if the actor is static, or a NULL pointer was supplied in constraint creation.
The role of the solver preparation function is to populate the buffer of Px1DConstraints, provide the point of application for force reporting, and provide the mass scaling properties. The return value is the number of Px1DConstraints generated in the output buffer.
Notice that although the joint parameters (relative pose etc) are typically specified relative to an actor, the solver preparation function works with the transforms of the underlying rigid bodies. The constraint infrastructure (see :ref:`constraint_infrastructure`) assists joints in maintaining consistency when, for example, the application modifies the center of mass of an actor.
Each Px1D constraint constrains one degree of freedom between the two bodies. The structure looks like this::
struct Px1DConstraint
{
PxVec3 linear0;
PxReal geometricError;
PxVec3 angular0;
PxReal velocityTarget;
PxVec3 linear1;
PxReal minImpulse;
PxVec3 angular1;
PxReal maxImpulse;
union
{
struct SpringModifiers
{
PxReal stiffness;
PxReal damping;
} spring;
struct RestitutionModifiers
{
PxReal restitution;
PxReal velocityThreshold;
} bounce;
} mods;
PxReal forInternalUse;
PxU16 flags;
PxU16 solveHint;
}
Each Px1DConstraint is either a hard constraint (for example, one axis of a fixed joint) or a soft constraint (for example, a spring). A joint may have a mixture of hard and soft constraint rows - for example, the actuated joint at a rag doll shoulder often has:
* 3 hard 1D-constraints which prevent the shoulder from separating.
* 3 hard 1D-constraints constraining the angular degrees of freedom within some limits.
* 3 soft constraints simulating resistance to angular motion from muscles.
The constraint is treated as hard unless the Px1DConstraintFlag::eSPRING flag is set.
For both soft and hard constraints, the *solver velocity* for each row is the quantity::
v = body0vel.dot(lin0, ang0) - body1vel.dot(lin1, ang1)
Hard Constraints
----------------
For a hard constraint, the solver attempts to generate:
* a set of motion solver velocities vMotion for objects which, when integrated, respect the constraint errors, represented by the equation::
vMotion + (geometricError / timestep) = velocityTarget
* a set of post-simulation solver velocities vNext for the objects which respect the constraints::
vNext = velocityTarget
The motion velocities are used for integration and then discarded. The post-simulation velocities are the values that getLinearVelocity() and getAngularVelocity() return.
There are two special options for hard constraints, both most often used to implement limits: restitution and velocity biasing. They are set by the constraint flags eRESTITUTION and eKEEPBIAS, are mutually exclusive, and restitution takes priority (in the sense that if restitution is set, biasing is ignored).
Restitution simulates bouncing (off a limit, for example). If the impact solver velocity vCurrent at the start of simulation exceeds the restitution velocity threshold, the target velocity of the constraint will be set to::
restitution * -vCurrent
and the input velocityTarget field will be ignored. To use restitution, set Px1DConstraintFlag::eRESTITUTION.
Velocity biasing generates post-simulation velocities to satisfy the same constraints as for the motion velocities::
vNext + (geometricError / timestep) = velocityTarget
This can be useful if, for example, the joint is approaching a limit but has not yet reached it. If the target velocity is 0 and the geometric error is the distance remaining to the limit, the solver will constrain the velocity below that required to violate the limit after integration. The joint should then converge smoothly to the limit.
Soft Constraints
----------------
Alternatively, the solver can attempt to resolve the velocity constraint as an implicit spring. In this case, the motion velocity vMotion and post-simulation velocity vNext are the same. The solver solves the equation::
F = stiffness * -geometricError + damping * (velocityTarget - v)
where F is the constraint force.
Springs are fully implicit: that is, the force or acceleration is a function of the position and velocity after the solve. There is one special option that applies only to soft constraints: acceleration springs (PxConstraintFlag::eACCELERATION). With this option the solver will scale the magnitude of the force in accordance with the response of the two bodies; effectively it implicitly solves the equation::
acceleration = stiffness * -geometricError + damping * (velocityTarget - v)
Force Limits and Reporting
--------------------------
All constraints support limits on the minimum or maximum impulse applied for each row. There is a special flag for force limits: eHAS_DRIVE_FORCE_LIMIT. If this flag is set, the force limits will be scaled by the timestep unless PxConstraintFlag::eLIMITS_ARE_FORCES is set for the constraint.
The flag eOUTPUT_FORCE flag on a 1D constraint determines whether the force applied for this row should be included in the constraint force output. The reporting force is also used internally to determine joint breakage. For example, if creating a spherical joint with angular drive that breaks when the stress on the linear part exceeds a threshold, set the flag for the linear equality rows but not the angular drive rows.
Solver Preprocessing
--------------------
The joint solver attempts to preprocess hard constraints to improve convergence. The solveHint value controls preprocessing for each row:
* if the constraint is a hard equality constraint with unbounded impulse limits (i.e. the impulse limits are -PX_MAX_FLT and PX_MAX_FLT), set this to PxConstraintSolveHint::eEQUALITY.
* If one of the force limits is zero and the other unbounded, set it to PxConstraintSolveHint::eINEQUALITY.
* for all soft constraints, and hard constraints with impulse limits other than the above, set it to PxConstraintSolveHint::eNONE.
The solver does not check that the hint value is consistent with the values in the Px1DConstraint. Using inconsistent values may result in undefined behavior.
The Projection Function
=======================
The other behavior that joints may specify for simulation is *projection*. This is a purely positional correction designed to act when the velocity-based solver fails. The projection function has the following signature::
typedef void (*PxConstraintProject)(const void* constantBlock,
PxTransform& bodyAToWorld,
PxTransform& bodyBToWorld,
bool projectToA);
It receives the constant block and the two body transforms. It should update the bodyBToWorld transform if the projectToA flag is set, and otherwise the bodyBToWorld transform. See the implementations in the extensions library for examples of how to define projection functions.
+++++++++++++++++++++++++++
The Constraint Shader Table
+++++++++++++++++++++++++++
After coding the behavior functions, define a structure of type PxConstraintShaderTable, which holds the pointers to the constraint functions. This structure will be passed as an argument to PxPhysics::createConstraint, and is shared by all instances of the joint::
struct PxConstraintShaderTable
{
PxConstraintSolverPrep solverPrep;
PxConstraintProject project;
PxConstraintVisualize visualize;
};
The constraint visualizer allows the joint to generate visualization information using the PxConstraintVisualizer interface. The functionality of this interface is somewhat biased towards the standard joints; examples of its use can be found in the extensions library.
.. _constraint_infrastructure:
+++++++++++++++
Data Management
+++++++++++++++
Next, define the class which lets PhysX manage the joint. This class should inherit from the PxConstraintConnector interface.
To create a joint, call PxPhysics::createConstraint. The arguments to this function are the constrained actors, the connector object, the shader table, and the size of the joint's constant block. The return value is a pointer to PxConstraint object.
PxConstraintConnector has a number of data management callbacks::
virtual void* prepareData();
virtual void onConstraintRelease();
virtual void onComShift(PxU32 actor);
virtual void onOriginShift(const PxVec3& shift);
virtual void* getExternalReference(PxU32& typeID);
These functions are usually boilerplate; sample implementations can be found for the joints in the extensions library:
* The prepareData() function requests a pointer to the joint constant block, and allows the joint to update any state caches etc. When the function returns, the scene makes an internal copy of this data, so that the joint may be modified during simulation without race conditions. The function is called at the start of the simulation step after the joint is inserted into the scene, and on a subsequent simulation step if PhysX is informed that the joint's state has changed. To inform PhysX that the joint state has changed, call PxConstraint::markDirty().
* onConstraintRelease() is associated with joint deletion. To delete a joint, call PxConstraint::release() on the constraint. When it is safe to destroy the joint (because no internal references are being held by currently executing simulation threads) the constraint code will call PxConstraint::onConstraintRelease(). This function can safely run the destructor and release the joint's memory etc.
* onComShift() is called when the application calls setCMassLocalPose() on one of the actors connected by the joint. This is provided because the solver preparation and projection functions are defined using the frame of the underlying rigid body, but the joint configuration is typically defined in terms of the actors.
* onOriginShift() is called when the application shifts the origin of a scene. This is necessary because some joints may have a NULL actor, signifying that they are attached to the world frame.
* getExternalReference() is used by PhysX to report simulation events involving constraints, particularly breakage. The returned pointer is passed directly to the application in the event callback, along with the typeID which the application can use in order to cast the pointer to the appropriate type. The typeID should be distinct for each custom joint type, and different from any of the values in PxJointConcreteType. If the joint also implements the PxBase interface, use the concrete type value from PxBase for the typeID.

View File

@ -0,0 +1,21 @@
.. _LICENSE:
.. |reg| unicode:: U+000AE .. REGISTERED SIGN
:ltrim:
----------------
PhysX License
----------------
Copyright (c) 2008-2021 NVIDIA Corporation. All rights reserved. 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.

View File

@ -0,0 +1,458 @@
.. _migration:
--------------------------------------------
Migrating From PhysX SDK 2.x to 3.x
--------------------------------------------
This guide describes how to upgrade applications that have an integration of PhysX 2.x to using PhysX 3.x. As the changes are numerous and significant, the level of work involved in upgrading to PhysX 3 should be carefully assessed before starting to adapt an application's integration code.
===============================
Removed Features
===============================
This section lists features of PhysX 2 that do not have a PhysX 3 equivalent. Applications that rely on these features may need fundamental changes, or should stay with using PhysX 2.
+++++++++++++++++++++++++++++++
Compartments
+++++++++++++++++++++++++++++++
PhysX 2 scenes supported scene compartments. A separate compartment could be assigned to simulating rigid bodies, deformables or fluids.
The compartments could be simulated in parallel and the scene code contained some extra logic for interaction between compartments.
Compartments were added as an afterthought to an SDK that was not originally designed to support interaction between multiple simulation technologies.
This design deficiency was addressed from the ground up in PhysX 3 and comparments were no longer needed.
One missing detail is separate time steps are no longer directly supported in 3. A possible workaround is to create multiple PxScenes and step them
at different rates. In this scenario the force exchange implementation would be entirely up to the user. Another possible approach is to simulate
the entire scene using the minimum timestep formerly required for any of the compartments.
+++++++++++++++++++++++++++++++
Deformables
+++++++++++++++++++++++++++++++
PhysX 2 supported a wide range of deformable mesh simulation features such as environmental cloth, soft bodies, inflatable balloons and plastic
deformation of rigid metal. For performance and code quality reasons, 3.3 temporarily stopped supporting many of 2.8 deformable features in favor
of a much simpler and higher performance cloth simulation engine. In PhysX 3 dot releases, we will be incrementally adding back features such
as environmental simulation. For the time being there is no substitute for many applications of PhysX 2 deformables.
+++++++++++++++++++++++++++++++
NxUtilLib
+++++++++++++++++++++++++++++++
The assorted utility functions that were in this library was either moved elsewhere or deleted. Sweep, overlap and ray tests are available in PxGeometryQuery. Inertial tensor diagonalization is in PxDiagonalize(). Density computation from mass is gone. Floating point unit manipulation routines are gone. Geometrical helpers are in general gone.
+++++++++++++++++++++++++++++++
Anisotropic Friction
+++++++++++++++++++++++++++++++
Friction on a surface in PhysX 2 could be configured to be stronger in one direction than in another. This is no longer supported in PhysX 3, and there is no known workaround that will give comparable behavior.
===============================
Basics
===============================
+++++++++++++++++++++++++++++++
SDK Header
+++++++++++++++++++++++++++++++
In PhysX 2, the symbols of the SDK could be included in the user's relevant source files through the following header::
#include "NxPhysics.h"
In PhysX 3, this should be replaced with::
#include "PxPhysicsAPI.h"
+++++++++++++++++++++++++++++++
SDK Redistribution
+++++++++++++++++++++++++++++++
Unlike versions of PhysX prior to 2.8.4, PhysX 3 no longer needs a 'system software' installation on Windows.
+++++++++++++++++++++++++++++++
API Conventions
+++++++++++++++++++++++++++++++
The Nx prefix of API classes has changed to a Px prefix. Descriptors for many classes were removed and replaced with creation parameters inline in the creation function.
For example, a capsule was created with PhysX 2 like this::
NxCapsuleShapeDesc capsuleDesc;
capsuleDesc.height = height;
capsuleDesc.radius = radius;
capsuleDesc.materialIndex= myMaterial->getMaterialIndex();
NxShape* aCapsuleShape = aCapsuleActor->createShape(capsuleDesc);
In PhysX 3 it is created more succinctly like this::
PxShape* aCapsuleShape = PxRigidActorExt::createExclusiveShape(*aCapsuleActor,
PxCapsuleGeometry(radius, halfHeight), myMaterial);
++++++++++++++++
Callback Classes
++++++++++++++++
PhysX 2 callback classes are listed below, followed by the corresponding PhysX 3 class, if there is one:
+----------------------+------------------------------------------------------+
|NxUserAllocator |PxAllocatorCallback |
+----------------------+------------------------------------------------------+
|NxUserOutputStream |PxErrorCallback |
+----------------------+------------------------------------------------------+
|NxUserContactReport |PxSimulationEventCallback |
+----------------------+------------------------------------------------------+
|NxUserNotify |PxSimulationEventCallback |
+----------------------+------------------------------------------------------+
|NxUserTriggerReport |PxSimulationEventCallback |
+----------------------+------------------------------------------------------+
The following PhysX 2 callback classes have no PhysX 3 direct equivalent:
+----------------------+--------------------------------------------------------------------------------------------------+
|NxUserRaycastReport |Ray casting Results. Results are now passed to the user using a PxHitBuffer object. |
+----------------------+--------------------------------------------------------------------------------------------------+
|NxUserEntityReport |Sweep and Overlap results. Results are now passed to the user using a PxHitBuffer object. |
+----------------------+--------------------------------------------------------------------------------------------------+
|NxStream |Data serialization. Serialized data is now written directly to binary buffers. |
+----------------------+--------------------------------------------------------------------------------------------------+
Below is a list of new callback classes that offer functionality that did not exist in PhysX 2 yet:
+-----------------------------+--------------------------------------------------------------+
|PxBroadPhaseCallback |Broad-phase related events. |
+-----------------------------+--------------------------------------------------------------+
|PxSimulationFilterCallback |Contact filtering. |
+-----------------------------+--------------------------------------------------------------+
|PxUserControllerHitReport |Reports character controller events. |
+-----------------------------+--------------------------------------------------------------+
|PxControllerBehaviorCallback |Customizes behavior of character controller collisions. |
+-----------------------------+--------------------------------------------------------------+
|PxContactModifyCallback |Modification of contact constraints. |
+-----------------------------+--------------------------------------------------------------+
|PxCCDContactModifyCallback |Modification of CCD contact constraints. |
+-----------------------------+--------------------------------------------------------------+
|PxConstraintConnector |Custom constraints. |
+-----------------------------+--------------------------------------------------------------+
|PxProcessPxBaseCallback |Serialization. |
+-----------------------------+--------------------------------------------------------------+
|PxQueryFilterCallback |Scene query filtering. |
+-----------------------------+--------------------------------------------------------------+
|PxSpatialLocationCallback |Scene Queries against PxSpatialIndex. |
+-----------------------------+--------------------------------------------------------------+
|PxSpatialOverlapCallback |Scene Queries against PxSpatialIndex. |
+-----------------------------+--------------------------------------------------------------+
+++++++++++++++++++++++++++++++
Memory Management
+++++++++++++++++++++++++++++++
NxUserAllocator is renamed to PxAllocatorCallback. An important change since PhysX 2: The SDK now requires that the memory that is returned be 16-byte aligned. On many platforms malloc() returns memory that is 16-byte aligned, but on Windows the system function _aligned_malloc() provides this capability.
+++++++++++++++++++++++++++++++
Debug Rendering
+++++++++++++++++++++++++++++++
Debug visualization formerly provided by NxScene::getDebugRenderable() is now handled by PxScene::getRenderBuffer() and related functions.
+++++++++++++++++++++++++++++++
Error Reporting
+++++++++++++++++++++++++++++++
NxUserOutputStream is now called PxErrorCallback, but works the same way. There is no separate reportAssertViolation() function. Asserts are only contained in the debug build which only ships with the source release and go directly to platform hooks.
+++++++++++++++++++++++++++++++
Type Casting
+++++++++++++++++++++++++++++++
PhysX 2 style downcasting::
NxSphereShape * sphere = shape->isSphere();
is replaced by the following template syntax::
const PxRigidDynamic* myActor = actor->is<PxRigidDynamic>();
+++++++++++++++++++++++++++++++
Multithreading
+++++++++++++++++++++++++++++++
Compared to PhysX 2, there are now more situations where it is legal to call the SDK from multiple threads. See the section on Multithreading for details.
While PhysX 2 simulation threads were managed internally by the SDK, and the user could simply specify the number to use, PhysX 3 allows the application to take over all of the simulation's thread scheduling. It is also possible for the application to define its own tasks and submit them to the SDK's default scheduler. See the section on TaskManagement for details.
+++++++++++++++++++++++++++++++
Startup and Shutdown
+++++++++++++++++++++++++++++++
PxCreatePhysicsSDK() has been renamed PxCreatePhysics(), and the parameters have slightly changed. A foundation instance must first be created explicitly using PxCreateFoundation().
+++++++++++++++++++++++++++++++
Extensions
+++++++++++++++++++++++++++++++
A lot of non-essential utility code has been moved to the extensions library. For example, NxActor::addForceAtPos() is now exposed as PxRigidBodyExt::addForceAtPos(). If a former function appears to be missing, look there. It is available after calling PxInitExtensions().
+++++++++++++++++++++++++++++++
Heightfields
+++++++++++++++++++++++++++++++
Heightfields now need to be pre-cooked like convexes and meshes. PhysX 3 heightfields can be set to use the same internal collision logic as meshes so they have uniform behavior.
+++++++++++++++++++++++++++++++
Cooking
+++++++++++++++++++++++++++++++
The PhysX 2 cooking library was created by calling::
NxCookingInterface *gCooking = NxGetCookingLib(NX_PHYSICS_SDK_VERSION);
gCooking->NxInitCooking();
It can now be accessed through a single PxCreateCooking() call. Cooking function names are slightly changed, e.g. NxCookTriangleMesh() is now invoked as cooking.cookTriangleMesh().
+++++++++++++++++++++++++++++++
Serialization
+++++++++++++++++++++++++++++++
PhysX 3 has two serialization systems: 'RepX' based on XML, and a separate system for fast binary data. Neither approach is similar to PhysX 2's save-to-desc and load-from-desc based serialization code, though the PhysX 3 'RepX' serialization is similar to PhysX 2's NxUStream.
===============================
API Design Changes
===============================
+++++++++++++++++++++++++++++++
Changed Actor Hierarchy
+++++++++++++++++++++++++++++++
PhysX 2 only had a single actor class, and it was possible to call any method on any instance of this class even if it wasn't applicable to the kind of actor object in question. For example, isSleeping() could be called on static actors which did not have any sleep logic. In PhysX 3, we decoupled actor into a hierarchy of specialized sub-classes. For example, PxCloth and PxParticleSystem are now subclasses of PxActor.
+++++++++++++++++++++++++++++++
Actor Creation
+++++++++++++++++++++++++++++++
In PhysX 2, the objects inside each scene were created by the scene class itself. In PhysX 3, objects are created by PxPhysics, and need to be added to a scene as a separate subsequent step by calling::
mScene->addActor(actor);
===============================
Material Indexes
===============================
PhysX 2 uses so-called material indexes for stored materials. Material indices are supported in PhysX 3 only to specify per-triangle materials in meshes and heightfields. In other cases the material object is referenced directly.
===============================
Continuous Collision Detection
===============================
PhysX 2 uses CCD skeleton meshes for CCD. PhysX 3 no longer needs this data so all skeleton related code can simply be removed.
===============================
Pose Description
===============================
In PhysX 2 pose is specified using a matrix. In PhysX 3, pose is specified using a PxTransform type that consists of a PxVec3 for translation and a PxQuat for rotation.
Constructors are provided to convert 4x4 matrices to PxTransform objects and 3x3 matrices from quaternions, as well as conversely.
===============================
Shape Description
===============================
PhysX 2 has multiple subclasses of NxShape, one for each type of geometry, with corresponding NxShapeDesc classes. PhysX 3 has only a single PxShape class, to which a PxGeometry
object is passed on creation. To determine the geometry type of a shape, call PxShape::getGeometryType(). To extract a PxGeometry object from a shape of unknown type, use
PxShape::getGeometry().
+++++++++++++++++++++++++++++++
Skin Width
+++++++++++++++++++++++++++++++
PhysX 2's NX_SKIN_WIDTH and NxShapeDesc::skinWidth was replaced with PxShape::setContactOffset() and setRestOffset(). See :ref:`ShapeCollisionTuning`.
===============================
Joints
===============================
The D6 driveType in PhysX 2 no longer exists in PhysX 3. Now drive for D6 is
always spring-like: if you want position drive you set the 'spring' value non-zero, if you want velocity
drive you set the damping field non-zero, and if you set both you get a damped spring.
Some specialized joints like NxJointDriveDesc, NxJointLimitSoftDesc (PhysX 2 names) now were
moved to Extensions (see the extensions folder inside PhysX 3 include directory).
If you have used the deleted NxSpringAndDamperEffector, you should now use a joint with a spring property.
All special axes for a joint (rotation axis for revolute, translation axis for prismatic, twist axis for D6) now use the x-axis.
Joint limits now require a contact offset, which determines the distance from the limit at which it becomes active. It functions similarly to the
contactOffset parameter for collision detection.
===============================
Time Stepping
===============================
PhysX 2 had two different time stepping modes: NX_TIMESTEP_FIXED (SDK subdivided into fixes steps) and NX_TIMESTEP_VARIABLE (user specified steps). This was passed to the setTiming() function. This controlled SDK-internal substepping code that computed the proper size of the next time step, and called an internal simulate function with this elapsed time.
PhysX 3 discards with the substepping code altogether, and exposes only the internal simulate function directly::
mScene->simulate(mStepSize);
In PhysX 2 it was legal to call simulate with a timestep of zero to force the execution of various side-effects of simulation. PhysX 3 neither requires nor supports this.
The fetchResults function stayed the same, however there is no more flag to specify which simulation to fetch, as there is now only a single simulation.
+++++++++++++++++++++++++++++++
Simulation Parameters
+++++++++++++++++++++++++++++++
The global speeds below which objects go to sleep, NX_DEFAULT_SLEEP_LIN_VEL_SQUARED and NX_DEFAULT_SLEEP_ANG_VEL_SQUARED are gone. PhysX 3 instead features per-body function PxRigidDynamic::setSleepThreshold() which is an energy based setting, more similar to the PhysX 2 NX_DEFAULT_SLEEP_ENERGY.
The global NX_BOUNCE_THRESHOLD is replaced by PxSceneDesc::bounceThresholdVelocity.
The NX_DYN_FRICT_SCALING, NX_STA_FRICT_SCALING scaling factors have been removed. These values should now be pre-baked into friction coefficients.
The NX_MAX_ANGULAR_VELOCITY value has been removed.
NX_ADAPTIVE_FORCE has been renamed PxScenFlag.ADAPTIVE_FORCE.
===============================
Collision Filtering
===============================
PhysX 2 supported multiple fixed function mechanisms for filtering pairwise shape collisions such as collision groups.
In PhysX 2 multiple group tags could be created, specified as collidable with each other and assigned to shapes.
PhysX 3, supports user callbacks for collision filtering with a restriction that arbitrary memory cannot be accessed by filtering code
so that it can be executed on PS3 SPUs or on GPUs with optimal performance. If performance is not a priority, similar functionality can be achieved
via conventional callbacks (PxSimulationFilterCallback).
When migrating PhysX 2 code, note that we provide the class PxDefaultSimulationFilterShader in PhysX 3, which emulates a portion of PhysX 2 filtering behavior. Start by checking if this class is sufficient. As this is an extension class, the source code is available and may be extended or customized.
To migrate your fixed function PhysX 2 filtering code on your own, you need to be aware of its exact behavior and implement it as a callback or shader. Let us look at the precise 2.8 mechanisms and make some recommendations for porting::
virtual void NxScene::setShapePairFlags(NxShape& shapeA,
NxShape& shapeB,
NxU32 nxContactPairFlag //0 or NX_IGNORE_PAIR
)
virtual void NxScene::setActorPairFlags(NxActor& actorA,
NxActor& actorB,
NxU32 nxContactPairFlag
)
The first function stored explicit shape pairs in a hash, and a lookup returned the bit indicating to filter or not. The second did the same for actor pairs. Because of the arbitrary size of the pair hash, implementing this mechanism as a shader with fixed memory is difficult in practice, but implementing as a callback should be trivial using a data structure such as the STL hash_map where Key is a struct holding the two pointers and Data is the bit flag.
Another scheme provided by PhysX 2 were collision groups::
virtual void NxShape::setGroup(NxCollisionGroup collisionGroup)
virtual void NxScene::setGroupCollisionFlag(NxCollisionGroup group1,
NxCollisionGroup group2,
bool enable
)
This approach let the user assign shapes to one of 32 collision groups, and then let each pair of groups be assigned a boolean pair flag. This approach lends itself better to a shader based implementation. To do this, you should reserve a word of each shape's filterData (say word0) to hold the group index,
and assign this as before. Next, define a matrix to hold the group pair bits, and a function to set it::
NxU32 groupCollisionFlags[32];
//init all group pairs to true:
for (unsigned i = 0; i < 32; i ++)
groupCollisionFlags[i] = 0xffffffff;
void setU32CollisionFlag(NxU32 groups1, NxU32 groups2, bool enable)
{
NX_ASSERT(groups1 < 32 && groups2 < 32);
if (enable)
{
//be symmetric:
groupCollisionFlags[groups1] |= (1 << groups2);
groupCollisionFlags[groups2] |= (1 << groups1);
}
else
{
groupCollisionFlags[groups1] &= ~(1 << groups2);
groupCollisionFlags[groups2] &= ~(1 << groups1);
}
}
Unfortunately it is not possible to change this state after the scene is created. This is because if the matrix could change during simulation, it would force an arbitrary amount of existing contact pairs to be refiltered. In a large simulation, this could be an unacceptable amount of computation. Therefore the matrix must be initialized to its final state before the scene is created, like this::
PxSceneDesc desc;
...
desc.filterShaderData = groupCollisionFlags;
desc.filterShaderDataSize = 32 * sizeof(PxU32);
scene = sdk.createScene(desc);
Finally, you need to code the filter shader to access this data::
PxFilterFlags FilterShader(
PxFilterObjectAttributes attributes0, PxFilterData filterData0,
PxFilterObjectAttributes attributes1, PxFilterData filterData1,
PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
{
// let triggers through, and do any other prefiltering you need.
if(PxFilterObjectIsTrigger(attributes0) || PxFilterObjectIsTrigger(attributes1))
{
pairFlags = PxPairFlag::eTRIGGER_DEFAULT;
return PxFilterFlag::eDEFAULT;
}
// generate contacts for all that were not filtered above
pairFlags = PxPairFlag::eCONTACT_DEFAULT;
PxU32 ShapeGroup0 = filterData0.word0 & 31;
PxU32 ShapeGroup1 = filterData1.word0 & 31;
PxU32* groupCollisionFlags = (PxU32*)constantBlock;
if ((groupCollisionFlags[ShapeGroup0] & (1 << ShapeGroup1)) == 0)
return PxFilterFlag::eSUPPRESS;
else
return PxFilterFlag::eDEFAULT;
}
===============================
Scene Queries
===============================
The API for scene query functions that return multiple intersections (e.g. PxScene::raycast(...)) has changed.
In PhysX 3, raycast/overlap/sweep functions expect a pre-allocated buffer or a callback class as a parameter in order to return multiple intersections.
If you do not know the maximum number of intersections in advance you can inherit from PxHitCallback and override processTouches virtual function
to receive an arbitrary number of intersections via multiple callbacks using only a fixed size buffer.
Please refer to the Scene Query section of the guide for more details and examples.
+++++++++++++++++++++++++++++++
Raycasts
+++++++++++++++++++++++++++++++
The interface for making raycasts was changed in PhysX 3. Now you should pass an origin (PxVec3) and
a direction (PxVec3) instead of a NxRay that combined these fields in PhysX 2.
+++++++++++++++++++++++++++++++
Overlaps
+++++++++++++++++++++++++++++++
Routines like overlapSphereShapes, overlapAABBShapes, overlapOBBShapes, overlapCapsuleShapes are
now all covered with PxScene::overlap (passing in a PxSphereGeometry, PxBoxGeometry or
PxCapsuleGeometry as a first parameter).
+++++++++++++++++++++++++++++++
Sweep Tests
+++++++++++++++++++++++++++++++
PhysX 2 provides a linearCapsuleSweep that takes two points to define the capsule's two spherical ends.
In PhysX 3 we have a general sweep() routine that takes a PxGeometry and an initial PxTransform position.
Capsules were defined in PhysX 2 as two points. They should be converted to an initial transformation (PxTransform) that consists of
PxVec3 for position and PxQuat for rotation. PxCapsuleGeometry's length is along the x axis in local space.

View File

@ -0,0 +1,745 @@
.. _migrationTo33:
----------------------------------------------------
Migrating From PhysX SDK 3.2 to 3.3
----------------------------------------------------
* This guide highlights all significant parts of the API that have changed in the last dot release. An application with a working integration of the older version of PhysX should be able to easily migrate to the newer version by following these pointers.
=====================================================
Math Classes
=====================================================
The static createIdentity() and createZero() methods are now deprecated, and will be removed in a future release. The preferred method
is to use the constructors PxMat33(PxIdentity), PxMat44(PxIdentity), PxQuat(PxIdentity), PxTransform(PxIdentity) for identity transforms, and
PxMat33(PxZero) and PxMat44(PxZero) for zero matrices.
=====================================================
Scene Query API
=====================================================
* The Scene Query API underwent significant changes. The highlights are:
* Former raycastAny, raycastMultiple, raycastSingle API calls are now folded into a single PxScene::raycast call
* Same for overlaps and sweeps
* Same for PxBatchQuery and PxVolumeCache
* For PxScene queries a deprecated backwards compatibility mapping was added to aid the transition
* This mapping will be removed in the next dot release
* There are now dedicated callback and buffer classes for receiving query results, replacing PxRaycastHit array and count parameters.
* Same for sweeps and overlaps
* See PxRaycastBuffer, PxSweepBuffer, PxOverlapBuffer, PxRaycastCallback, PxSweepCallback, PxOverlapCallback
* The way results are returned is now more robust and it is possible to transparently handle unbounded number of results without dynamic allocations.
* Header PxSceneQueryFiltering.h was renamed to PxQueryFiltering.h, PxSceneQueryReport.h to PxQueryReport.h
* PxHitFlag::eIMPACT changed to PxHitFlag::ePOSITION
* PxRaycastHit.impact renamed to PxRaycastHit.position (same for PxSweepHit.impact)
* PxQueryFlag::eNO_BLOCK and PxQueryFlag::eANY_HIT flags were added
* The following classes were renamed
* PxSceneQueryHit -> PxQueryHit
* PxSceneQueryFlags -> PxHitFlags
* PxSceneQueryHitType -> PxQueryHitType
* PxSceneQueryFilterData -> PxQueryFilterData
* PxSceneQueryFilterCallback -> PxQueryFilterCallback
* PxSceneQueryFilterFlags -> PxQueryFlags
* PxSceneQueryCache -> PxQueryCache
* PxCCTNonWalkableMode -> PxControllerNonWalkableMode
* PxControllerFlags -> PxControllerCollisionFlags
* PxCCTHit -> PxControllerHit
* PxConstraintDominance -> PxDominanceGroupPair
* PxActorTypeSelectionFlags -> PxActorTypeFlags
* PxFindOverlapTriangleMeshUtil -> PxMeshOverlapUtil
* Old versions are #defined to new versions to simplify transition. These #defines are deprecated and will be phased out.
* queryClient parameter was removed from raycast/sweep/overlap parameter list and added to PxQueryFilterData
* The fix is to simply pass the same value via PxQueryFilterData::clientId
* PxBatchQueryDesc now requires 3 parameters at construction time, PxU32 maxRaycastsPerExecute, PxU32 maxSweepsPerExecute, PxU32 maxOverlapsPerExecute
* Each of these numbers is an upper bound on the number of PxBatchQuery::raycast(), sweep() and overlap() calls before a call to execute()
* Previously there was no way to check for results buffer overflow in batch query code since sizes of these buffers were not specified.
* The fix is to specify the batch query result (different from hit) buffer sizes at construction.
* PxBatchQueryDesc no longer directly holds pointers to memory for queries, in 3.3 these are moved to PxBatchQueryMemory.
* It is now possible to set a new batch query memory descriptor before each execute
* userRaycastHitBuffer has been renamed to userRaycastTouchBuffer
* raycastHitBufferSize has been renamed to raycastTouchBufferSize
* same for overlaps and sweeps (userSweepHitBuffer, sweepHitBufferSize, userOverlapHitBuffer, overlapHitBufferSize)
* A code snippet below illustrates the migration for these code changes
* PxQueryFilterData constructors are now explicit. This means that previously it was possible to write
* scene->raycast(..., PxQueryFlag::eDYNAMIC | PxQueryFlag::eSTATIC, ...), causing PxQueryFilterData to be implicitly constructed by the compiler
* now it is required to explicitly write: scene->raycast(...,PxQueryFilterData(PxQueryFlag::eDYNAMIC | PxQueryFlag::eSTATIC), ...)
* This change was made to improve type safety and reduce confusion while reading the code employing implicit constructors
* PxRaycastBufferN, PxOverlapBufferN and PxSweepBufferN were added for convenience
* A buffer object with space for 10 touching hits and one blocking hit can now be conveniently declared as PxRaycastBufferN<10> hits;
* PxRaycastHit and PxSweepHit now inherit from PxLocationHit (formerly from PxSceneQueryImpactHit)
* bool PxLocationHit::hadInitialOverlap() function was added to determine if a swept shape was overlapping at sweep distance=0 or if a raycast hit a shape at distance=0.
* Functionality of PxSceneQueryFlag::eINITIAL_OVERLAP and PxSceneQueryFlag::eINITIAL_OVERLAP_KEEP was replaced with PxHitFlag::eASSUME_NO_INITIAL_OVERLAP and PxLocationHit::hadInitialOverlap().
* Overlap scene queries with preFilter or postFilter returning multiple eBLOCK hits would previously return multiple results as touching hits.
* eBLOCK should not be returned from user filters for overlap(). Doing so will result in undefined behavior, and a warning will be issued.
* If the PxQueryFlag::eNO_BLOCK flag is set, the eBLOCK will instead be automatically converted to an eTOUCH and the warning suppressed.
* Sweeps in 3.3 execute using a new faster code path, in some cases with reduced precision.
If you encounter precision issues not previously experienced in earlier versions of PhysX, use ePRECISE_SWEEP flag to enable
the backwards compatible more accurate sweep code.
* Snippets demonstrating API migration:
Former raycastSingle call::
PxRaycastHit hit;
bool hadHit = scene->raycastSingle(..., hit, ...);
if (hadHit) doStuff(hit);
Is now::
PxRaycastBuffer buf;
Bool hadHit = scene->raycast(..., buf, ...);
if (hadHit) doStuff(buf.block);
Former raycastAny call::
PxSceneQueryHit hit;
bool hadHit = scene->raycastAny(hit);
if (hadHit) doStuff(hit);
Is now::
PxRaycastBuffer buf; // declare a hit buffer with room for a single blocking hit
PxFilterData fdAny; fdAny.flags |= PxQueryFlag::eANY_HIT;
bool hadHit = scene->raycast(buf, PxHitFlags(), fdAny);
if (hadHit) doStuff(buf.block);
Former Multiple call::
PxRaycastHit buffer[N];
bool hasBlock;
PxI32 result = Scene->raycastMultiple(buffer, N, hasBlock);
if (result == -1)
handleOverflow();
else
{
if (hasBlock)
{
doBlocking(buffer[result-1]);
doTouches(buffer, result-1);
}
else
{
doTouches(buffer, result);
}
}
Is now::
PxRaycastBufferN<N> buf;
scene->raycast(buf);
if (buf.hasBlock)
doBlocking(buf.block);
doTouches(buf.touches, buf.nbTouches);
or::
for (PxU32 i = 0; i < buf.getNbAnyHits(); i++) // "any" in this context refers to blocking or
// touching hits
doAnyHit(buf.getAnyHit(i));
Former batch query memory setup code in 3.2::
const PxU32 maxRaycastHits = 16, maxRaycastQueries = 8;
PxRaycastQueryResult* resultBuffer = new PxRaycastQueryResult[maxRaycastQueries];
PxRaycastHitBuffer* hitBuffer = new PxRaycastHit[maxRaycastHits];
PxBatchQueryDesc desc; // required no arguments, there was no safety check for maximum number
// of queries per batch (not hits per query)
desc.userRaycastResultBuffer = resultBuffer;
desc.userRaycastHitBuffer = hitBuffer;
desc.raycastHitBufferSize = maxRaycastHits;
PxBatchQuery* bq = PxCreateBatchQuery(desc);
for (PxU32 iQuery = 0; iQuery < maxRaycastQueries; iQuery++)
bq->raycastSingle(...); // up to 8 raycast queries are allowed per PxBatchQuery::execute()
// call but there was no overflow check in 3.2
bq->execute();
for (PxU32 iResult = 0; iResult < nQueries; iResult++)
{
for (PxU32 iHit = 0; iHit < resultBuffer[i].nbHits; iHit++)
{
bool isBlocking = (iHit == resultBuffer[i].nbHits &&
(resultBuffer[iResult].hits[iHit].flags & PxSceneQueryFlag::eBLOCKING_HIT));
processHit(resultBuffer[iResult].hits[iHit], isBlocking);
}
}
Batch query setup code in 3.3::
const PxU32 maxRaycastHits = 16, maxRaycastQueries = 8;
PxBatchQueryDesc desc(maxQueries, 0, 0); // note the new required maximum of queries per batch
// (this is different from maximum hits)
PxBatchQuery* bq = scene->createBatchQuery(desc);
PxRaycastQueryResult* resultBuffer = new PxRaycastQueryResult[maxRaycastQueries];
PxRaycastHitBuffer hitBuffer = new PxRaycastHit[maxRaycastHits];
PxBatchQueryMemory mem(maxQueries, 0, 0); // maximum number of queries for each type
// (raycasts, overlaps, sweeps)
mem.userRaycastResultBuffer = resultBuffer;
mem.userRaycastTouchBuffer = hitBuffer;
mem.raycastTouchBufferSize = maxHits;
PxBatchQuery* bq = PxCreateBatchQuery(desc);
bq->setUserMemory(mem);
for (PxU32 iQuery = 0; iQuery < maxRaycastQueries; iQuery++)
bq->raycastSingle(...); // up to 8 raycast queries are allowed per PxBatchQuery::execute()
// with query count overflow check as of 3.3
bq->execute();
for (PxU32 iResult = 0; iResult < nQueries; iResult++)
{
// note that the blocking hit is now reported in resultBuffer[i].block and touching hits in
// resultBuffer[i].touches
for (PxU32 iHit = 0; iHit < resultBuffer[i].nbTouches; iHit++)
processTouchingHit(resultBuffer[iResult].touches[iHit]);
processBlockingHit(resultBuffer[iResult].block);
}
=====================================================
SPU batch queries
=====================================================
In 3.2 the number of SPUs to be used per batch query was controlled by a global setting via setSceneParamInt call::
PxPS3Config::setSceneParamInt(getScene(), PxPS3ConfigParam::eSPU_RAYCAST, 3);
In 3.3 PxBatchQuery no longer automatically executes on multiple SPUs but requires a separate PPU thread, this design allows higher
flexibility, such as executing batches on multiple SPU and PPU threads simultaneously, better control of parallel execution and allows the user to fine tune thread load balancing.
Here's one possible way to run batch queries on multiple SPUs in 3.3::
struct BQThread : shdfnd::Thread
{
Ps::Sync mBatchReady;
Ps::Sync mBatchCompleted;
PxBatchQuery* mBatch;
PX_FORCE_INLINE BQThread() { mBatch = NULL; }
PX_FORCE_INLINE void submitBatch(PxBatchQuery* batch) { mBatch = batch; }
virtual void execute()
{
// execute submitted batches until quit is signalled
for(;;)
{
mBatchReady.wait();
mBatchReady.reset();
if (quitIsSignalled())
break;
mBatch->execute();
mBatch = NULL;
mBatchCompleted.set();
} // for (;;)
quit(); // shutdown thread
}
};
// main thread code:
// pre-create and launch batch execute threads
for (PxU32 iThread = 0; iThread < nThreads; iThread++)
{
BQThread* t = PX_NEW(BQThread);
t->start();
mThreads.pushBack(t);
}
// submit batches
for (PxU32 iThread = 0; iThread < nThreads; iThread++)
{
// create batches
PxBatchQuery* threadBatch = createBatch(...);
threadBatch->setRunOnSpu(true);
mThreads[iThread]->submitBatch(threadBatch);
mThreads[iThread]->mBatchReady.set();
}
// execute another batch on PPU in the meantime.
PxBatchQuery* threadBatch = createBatch(...);
threadBatch->setRunOnSpu(false);
threadBatch->execute();
// do other PPU work...
// wait for SPU batches to complete:
for (PxU32 i=0; i<mThreads.size(); ++i)
{
mThreads[i]->mBatchCompleted.wait();
mThreads[i]->mBatchCompleted.reset();
releaseBatch(mThreads[i]->mBatch);
}
// terminate batch threads
for (PxU32 i=0; i<mThreads.size(); ++i)
{
mThreads[i]->signalQuit();
mThreads[i]->mBatchReady.set();
mThreads[i]->waitForQuit();
PX_DELETE(mThreads[i]);
}
Whether the batch is executed on SPU or PPU is determined by either bool PxBatchQueryDesc::runOnSpu or PxBatchQuery::setRunOnSpu(bool),
by default batch query is executed on SPU::
PxBatchQueryDesc desc;
...
desc.runOnSpu = true;
...
=====================================================
Core PhysX
=====================================================
* The following methods require that the corresponding objects have been added to a scene. Calling these methods for objects which are not in a scene will result in undefined behavior. In the CHECKED build configuration an error message will get sent.
* addForce/addTorque/clearForce/clearTorque() on a PxRigidBody
* isSleeping/wakeUp/putToSleep() on a PxRigidDynamic, PxArticulation or PxCloth
* PxScene::resetFiltering() and the deprecated counterparts on PxShape and PxParticleBase
* The sleep behavior of dynamic rigid bodies has changed significantly. Among the changes are\:
* The wakeUp() method of PxRigidDynamic and PxArticulation has lost the wake counter parameter. Use the newly introduced method setWakeCounter() instead to set a specific value.
* Putting a dynamic rigid actor to sleep will clear any pending force updates.
* Switching a dynamic actor to kinematic will put the actor to sleep immediately.
* Switching a kinematic actor back to dynamic will not affect the sleep state (previously the actor was woken up).
* Calling wakeUp/putToSleep() on a kinematically controlled dynamic actor is not valid any longer. The sleep state of a kinematic actor is solely defined based on whether a target pose has been set (see API documentation of isSleeping() for details).
* A call to PxRigidBody::setCMassLocalPose() does not wake up the actor anymore. Add a call to PxRigidBody::wakeUp() to get the old behavior back. Note: this also affects related methods in PhysXExtensions like PxRigidBodyExt::updateMassAndInertia() etc.
* Adding or removing a PxConstraint to/from the scene does not wake the connected actors up automatically anymore (note: this applies to PxJoint in PhysX Extensions as well).
* If a non-zero velocity or force is set through PxRigidBody::setLinearVelocity(), ::setAngularVelocity(), ::addForce() or ::addTorque(), the actor will get woken up automatically even if the autowake parameter is false.
* PxRigidBody::clearForce() and ::clearTorque() do not have the autowake parameter, to optionally wake the actor up, anymore. These methods will not change the sleep state any longer. Call ::wakeUp() subsequently to get the old default behavior.
* Shapes may now be shared between actors. This change has several ramifications\:
* PxShape::getActor() now returns a pointer rather than a reference. If the shape is shareable, the pointer is NULL.
* The following methods of PxShape have been removed: getGlobalPose(), raycast(), sweep(), overlap(), getWorldBounds(). Replacements can be found in PxShapeExt.
* PxShape now has the same reference counting semantics as meshes and materials, so that release() releases the user reference, and when the last reference is released, the shape is destroyed.
* Shapes created through PxRigidActor::createShape() are still destroyed automatically when the actor is released. However, after serializing and deserializing such a shape, the regular reference counting semantics apply.
* return results from scene queries which previously specified a shape now specify an actor also.
* Shape local transforms cannot be specified on shape creation anymore. Instead set the local transform after creation with PxShape::setLocalPose().
* The PxObserver/PxObservable system has been replaced by the PxDeletionListener API. The supported object types have been extended from PxActor to all core objects inheriting from PxBase. Furthermore, two kinds of deletion events are now distinguished: user release and memory release. The following snippet shows pseudocode for the transition from the previous to the new API:
old API::
class MyObserver : public PxObserver
{
public:
virtual void onRelease(const PxObservable& observable);
}
MyObserver myObs;
PxRigidDynamic* d = create...;
d->registerObserver(myObs);
new API::
class MyDelListener : public PxDeletionListener
{
public:
virtual void onRelease(const PxBase* observable, void* userData,
PxDeletionEventFlag::Enum deletionEvent);
}
MyDelListener myDelListener;
PxPhysics* physics = create...;
PxRigidDynamic* d = create...;
physics->registerDeletionListener(myDelListener, PxDeletionEventFlag::eUSER_RELEASE, true);
PxBase* b = d;
physics->registerDeletionListenerObjects(myDelListener, &b, 1);
* The contactStream in PxContactPair is now stored in a variable-size compressed contact stream. This is used to save memory. As such, you can **no longer** simply cast it to a PxContactPoint* and access the data. Instead, you must either use PxContactPair::extractContacts or us a PxContactStreamIterator to interpret the data. Please see the callbacks section of the user guide for further information.
* The friction API and behavior for dynamic rigid bodies has changed slightly\:
* Friction mode flags eENABLE_ONE_DIRECTIONAL_FRICTION and eENABLE_TWO_DIRECTIONAL_FRICTION have been replaced by PxFrictionType::Enum PxSceneDesc::frictionType.
* PxSceneDesc::contactCorrelationDistance has been deprecated, and it no longer has an influence on how many friction anchors are created in a single frame, only on when they are removed in later frames. This may cause a very minor change in friction behavior.
* PxShape::resetFiltering() and PxParticleBase::resetFiltering() have been deprecated. Please use one of the new overloaded methods PxScene::resetFiltering() instead.
* PxClientBehaviorBit and PxActorClientBehaviorBit have been renamed to PxClientBehaviorFlag and PxActorClientBehaviorFlag respectively.
* PxActorTypeSelectionFlag and PxActorTypeSelectionFlags have been renamed to PxActorTypeFlag and PxActorTypeFlags respectively.
* PxConstraintDominance has been renamed to PxDominanceGroupPair
* The parameter 'spring' on articulation joints has been renamed 'stiffness'.
* The parameter 'tangentialSpring' on articulation joints has been renamed 'tangentialStiffness'.
* PxConstraintFlag::Type has been renamed to PxConstraintFlag::Enum
* Discrete contact reports are no longer produced for pairs without PxPairFlag::eDETECT_DISCRETE_CONTACT raised in the filter shader. Previously, discrete contact generation would always have been performed regardless of the presence of the PxPairFlag::eDETECT_DISCRETE_CONTACT flag. This change potentially improves performance when using specific shapes for CCD-only collision, which would have previously generated discrete contacts and then ignored them in the solver.
* Trigger reports are no longer produced for pairs without PxPairFlag::eDETECT_DISCRETE_CONTACT raised in the filter shader. PxPairFlag::eTRIGGER_DEFAULT has been modified to include the PxPairFlag::eDETECT_DISCRETE_CONTACT flag.
=====================================================
PhysX Extensions
=====================================================
* Joint limits have been more carefully separated into PxJointLinearLimit, PxJointAngularLimitPair, PxJointLinearLimitPair.
* PxJoint::getType() is deprecated. Joints now inherit from PxBase, and getConcreteType() replaces getType(). Alternatively, to dynamically cast to a particular joint type, use e.g. joint->is<PxD6Joint>() which will return a pointer to a D6 joint if the type matches, otherwise NULL.
* The parameter 'spring' in joint limits and drives has been renamed 'stiffness'.
* Dominance settings no longer apply to joints. To achieve this effect, use setInvMassScale. For example if actor0 in the joint is to affect actor1 but not conversely, use setInvMassScale0(0.0f), setInverseInertiaScale0(0.0f).
=====================================================
PhysX Character Controller
=====================================================
* When creating a PxControllerManager, a reference to a PxScene has to be provided. As a consequence, creating a controller from a PxControllerManager now only requires the controller descriptor as an argument.
* On PxControllerManager::release(), all associated PxObstacleContext instances will get deleted automatically. Make sure to not access PxObstacleContext instances after the corresponding manager has been released.
=====================================================
PhysX Vehicles
=====================================================
* A new struct has been introduced to hold the enumerated list that began with PxVehicleDrive4W::eFRONT_LEFT_WHEEL. The changes are
* PxVehicleDrive4W::eFRONT_LEFT_WHEEL -> PxVehicleDrive4WWheelOrder::eFRONT_LEFT
* PxVehicleDrive4W::eFRONT_RIGHT_WHEEL -> PxVehicleDrive4WWheelOrder::eFRONT_RIGHT
* PxVehicleDrive4W::eREAR_LEFT_WHEEL -> PxVehicleDrive4WWheelOrder::eREAR_LEFT
* PxVehicleDrive4W::eREAR_RIGHT_WHEEL -> PxVehicleDrive4WWheelOrder::eREAR_RIGHT
* A new struct has been introduced to hold the enumerated list that began with PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL. The changes are
* PxVehicleDrive4W::eANALOG_INPUT_ACCEL -> PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL
* PxVehicleDrive4W::eANALOG_INPUT_BRAKE -> PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE
* PxVehicleDrive4W::eANALOG_INPUT_HANDBRAKE -> PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE
* PxVehicleDrive4W::eANALOG_INPUT_STEER_LEFT -> PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT
* PxVehicleDrive4W::eANALOG_INPUT_STEER_RIGHT -> PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT
* PxVehicleDrive4W::eMAX_NUM_DRIVE4W_ANALOG_INPUTS -> PxVehicleDrive4WControl::eMAX_NB_DRIVE4W_ANALOG_INPUTS
* A new struct has been introduced to hold the enumerated list that began with PxVehicleDrive4W::eFRONT_LEFT_WHEEL. The changes are
* PxVehicleDriveTank::eTANK_WHEEL_FRONT_LEFT -> PxVehicleDriveTankWheelOrder::eFRONT_LEFT
* PxVehicleDriveTank::eTANK_WHEEL_FRONT_RIGHT -> PxVehicleDriveTankWheelOrder::eFRONT_RIGHT,
* PxVehicleDriveTank::eTANK_WHEEL_1ST_FROM_FRONT_LEFT -> PxVehicleDriveTankWheelOrder::e1ST_FROM_FRONT_LEFT,
* PxVehicleDriveTank::eTANK_WHEEL_1ST_FROM_FRONT_RIGHT -> PxVehicleDriveTankWheelOrder::e1ST_FROM_FRONT_RIGHT
* PxVehicleDriveTank::eTANK_WHEEL_2ND_FROM_FRONT_LEFT -> PxVehicleDriveTankWheelOrder::e2ND_FROM_FRONT_LEFT
* PxVehicleDriveTank::eTANK_WHEEL_2ND_FROM_FRONT_RIGHT -> PxVehicleDriveTankWheelOrder::e2ND_FROM_FRONT_RIGHT
* PxVehicleDriveTank::eTANK_WHEEL_3RD_FROM_FRONT_LEFT -> PxVehicleDriveTankWheelOrder::e3RD_FROM_FRONT_LEFT
* PxVehicleDriveTank::eTANK_WHEEL_3RD_FROM_FRONT_RIGHT -> PxVehicleDriveTankWheelOrder:: e3RD_FROM_FRONT_RIGHT
* PxVehicleDriveTank::eTANK_WHEEL_4TH_FROM_FRONT_LEFT -> PxVehicleDriveTankWheelOrder::e4TH_FROM_FRONT_LEFT
* PxVehicleDriveTank::eTANK_WHEEL_4TH_FROM_FRONT_RIGHT -> PxVehicleDriveTankWheelOrder::e4TH_FROM_FRONT_RIGHT
* PxVehicleDriveTank::eTANK_WHEEL_5TH_FROM_FRONT_LEFT -> PxVehicleDriveTankWheelOrder::e5TH_FROM_FRONT_LEFT
* PxVehicleDriveTank::eTANK_WHEEL_5TH_FROM_FRONT_RIGHT -> PxVehicleDriveTankWheelOrder::e5TH_FROM_FRONT_RIGHT
* PxVehicleDriveTank::eTANK_WHEEL_6TH_FROM_FRONT_LEFT -> PxVehicleDriveTankWheelOrder::e6TH_FROM_FRONT_LEFT
* PxVehicleDriveTank::eTANK_WHEEL_6TH_FROM_FRONT_RIGHT -> PxVehicleDriveTankWheelOrder::e6TH_FROM_FRONT_RIGHT
* PxVehicleDriveTank::eTANK_WHEEL_7TH_FROM_FRONT_LEFT -> PxVehicleDriveTankWheelOrder::e7TH_FROM_FRONT_LEFT
* PxVehicleDriveTank::eTANK_WHEEL_7TH_FROM_FRONT_RIGHT -> PxVehicleDriveTankWheelOrder::e7TH_FROM_FRONT_RIGHT
* PxVehicleDriveTank::eTANK_WHEEL_8TH_FROM_FRONT_LEFT -> PxVehicleDriveTankWheelOrder::e8TH_FROM_FRONT_LEFT
* PxVehicleDriveTank::eTANK_WHEEL_8TH_FROM_FRONT_RIGHT -> PxVehicleDriveTankWheelOrder::e8TH_FROM_FRONT_RIGHT
* PxVehicleDriveTank::eTANK_WHEEL_9TH_FROM_FRONT_LEFT -> PxVehicleDriveTankWheelOrder::e9TH_FROM_FRONT_LEFT
* PxVehicleDriveTank::eTANK_WHEEL_9TH_FROM_FRONT_RIGHT -> PxVehicleDriveTankWheelOrder::e9TH_FROM_FRONT_RIGHT
* PxVehicleDriveTank::eTANK_WHEEL_9TH_FROM_FRONT_RIGHT -> PxVehicleDriveTankWheelOrder::e9TH_FROM_FRONT_RIGHT
* A new struct has been introduced to hold the enumerated list that began with PxVehicleDriveTank::eANALOG_INPUT_ACCEL. The changes are
* PxVehicleDriveTank::eANALOG_INPUT_ACCEL -> PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL
* PxVehicleDriveTank::eANALOG_INPUT_BRAKE_LEFT -> PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT
* PxVehicleDriveTank::eANALOG_INPUT_BRAKE_RIGHT -> PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT
* PxVehicleDriveTank::eANALOG_INPUT_THRUST_LEFT -> PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT
* PxVehicleDriveTank::eANALOG_INPUT_THRUST_RIGHT -> PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT
* PxVehicleDriveTank::eMAX_NUM_DRIVETANK_ANALOG_INPUTS -> PxVehicleDriveTankControl::eMAX_NB_DRIVETANK_ANALOG_INPUTS
* A new struct has been introduced to hold the enumerated list that began with PxVehicleDriveTank::eDRIVE_MODEL_STANDARD. The changes are
* PxVehicleDriveTank::eDRIVE_MODEL_STANDARD -> PxVehicleDriveTankControlModel::eSTANDARD
* PxVehicleDriveTank::eDRIVE_MODEL_SPECIAL -> PxVehicleDriveTankControlModel::eSPECIAL
* A new struct has been introduced to hold the enumerated list that began with eVEHICLE_TYPE_DRIVE4W. The changes are
* eVEHICLE_TYPE_DRIVE4W -> PxVehicleTypes::eDRIVE4W
* eVEHICLE_TYPE_DRIVETANK -> PxVehicleTypes::eDRIVETANK
* eVEHICLE_TYPE_NODRIVE -> PxVehicleTypes::eNODRIVE
* eMAX_NUM_VEHICLE_TYPES -> PxVehicleTypes::eMAX_NB_VEHICLE_TYPES
* A new struct has been introduced to hold the enumerated list that began with PxVehicleGraph::eCHANNEL_JOUNCE. The changes are
* PxVehicleGraph::eCHANNEL_JOUNCE -> PxVehicleWheelGraphChannel::eJOUNCE
* PxVehicleGraph::eCHANNEL_SUSPFORCE -> PxVehicleWheelGraphChannel::eSUSPFORCE
* PxVehicleGraph::eCHANNEL_TIRELOAD -> PxVehicleWheelGraphChannel::eTIRELOAD
* PxVehicleGraph::eCHANNEL_NORMALIZED_TIRELOAD -> PxVehicleWheelGraphChannel::eNORMALIZED_TIRELOAD
* PxVehicleGraph::eCHANNEL_WHEEL_OMEGA -> PxVehicleWheelGraphChannel::eWHEEL_OMEGA
* PxVehicleGraph::eCHANNEL_TIRE_FRICTION -> PxVehicleWheelGraphChannel::eTIRE_FRICTION
* PxVehicleGraph::eCHANNEL_TIRE_LONG_SLIP -> PxVehicleWheelGraphChannel::eTIRE_LONG_SLIP
* PxVehicleGraph::eCHANNEL_NORM_TIRE_LONG_FORCE -> PxVehicleWheelGraphChannel::eNORM_TIRE_LONG_FORCE
* PxVehicleGraph::eCHANNEL_TIRE_LAT_SLIP -> PxVehicleWheelGraphChannel::eTIRE_LAT_SLIP
* PxVehicleGraph::eCHANNEL_NORM_TIRE_LAT_FORCE -> PxVehicleWheelGraphChannel::eNORM_TIRE_LAT_FORCE
* PxVehicleGraph::eCHANNEL_NORM_TIRE_ALIGNING_MOMENT -> PxVehicleWheelGraphChannel::eNORM_TIRE_ALIGNING_MOMENT
* PxVehicleGraph::eMAX_NUM_WHEEL_CHANNELS -> PxVehicleWheelGraphChannel::eMAX_NB_WHEEL_CHANNELS
* A new struct has been introduced to hold the enumerated list that began with PxVehicleGraph::eCHANNEL_ENGINE_REVS. The changes are
* PxVehicleGraph::eCHANNEL_ENGINE_REVS -> PxVehicleDriveGraphChannel::eENGINE_REVS
* PxVehicleGraph::eCHANNEL_ENGINE_DRIVE_TORQUE -> PxVehicleDriveGraphChannel::eENGINE_DRIVE_TORQUE
* PxVehicleGraph::eCHANNEL_CLUTCH_SLIP -> PxVehicleDriveGraphChannel::eCLUTCH_SLIP
* PxVehicleGraph::eCHANNEL_ACCEL_CONTROL -> PxVehicleDriveGraphChannel::eACCEL_CONTROL
* PxVehicleGraph::eCHANNEL_BRAKE_CONTROL -> PxVehicleDriveGraphChannel::eBRAKE_CONTROL
* PxVehicleGraph::eCHANNEL_HANDBRAKE_CONTROL -> PxVehicleDriveGraphChannel::eHANDBRAKE_CONTROL
* PxVehicleGraph::eCHANNEL_STEER_LEFT_CONTROL -> PxVehicleDriveGraphChannel::eSTEER_LEFT_CONTROL
* PxVehicleGraph::eCHANNEL_STEER_RIGHT_CONTROL -> PxVehicleDriveGraphChannel::eSTEER_RIGHT_CONTROL
* PxVehicleGraph::eCHANNEL_GEAR_RATIO -> PxVehicleDriveGraphChannel::eGEAR_RATIO
* PxVehicleGraph::eMAX_NUM_ENGINE_CHANNELS -> PxVehicleDriveGraphChannel::eMAX_NB_DRIVE_CHANNELS
* A new struct has been introduced to hold the enumerated list that began with PxVehicleGraph::eGRAPH_TYPE_WHEEL. The changes are
* PxVehicleGraph::eGRAPH_TYPE_WHEEL -> PxVehicleGraphType::eWHEEL
* PxVehicleGraph::eGRAPH_TYPE_ENGINE -> PxVehicleGraphType::eDRIVE
* Non-persistent data is no longer stored in the vehicle. Instead of storing this data in each vehicle it is stored in an array and passed to PxVehicleUpdates as an extra function argument. A simple example of how to construct, use, and read this data is given below. This example code updates an array of vehicles and tests if they are in the air. If the vehicles are not in the air then the actor under each wheel is recorded and stored in an array::
void updateVehicles(const PxF32 timestep, const PxVec3& gravity,
const PxVehicleDrivableSurfaceToTireFrictionPairs& fricPairs,
PxVehicleWheels** vehicles, PxU32 numVehicles, std::vector<PxActor*>& hitActors)
{
//Count the total number of wheels.
unsigned int numWheels = 0;
for(unsigned int i = 0; i < numVehicles; i++)
{
numWheels += vehicles[i]->mWheelsSimData.getNbWheels();
}
//Allocate buffers to store results for each vehicle and each wheel.
PxVehicleWheelQueryResult* vehicleWheelQueryResults =
new PxVehicleWheelQueryResult[numVehicles];
PxWheelQueryResult* wheelQueryResults = new PxWheelQueryResult[numWheels];
PxU32 wheelCount = 0;
for(PxU32 i = 0; i < numVehicles; i++)
{
vehicleWheelQueryResults[i].nbWheelQueryResults =
vehicles[i]->mWheelsSimData.getNbWheels();
vehicleWheelQueryResults[i].wheelQueryResults = &wheelQueryResults[wheelCount];
wheelCount += vehicles[i]->mWheelsSimData.getNbWheels();
}
//Update the array of vehicles.
PxVehicleUpdates(timestep, gravity, fricPairs, numVehicles, vehicles,
vehicleWheelQueryResults);
//Test if each vehicle is in the air.
for(PxU32 i = 0; i < numVehicles; i++)
{
if(!PxVehicleIsInAir(vehicleWheelQueryResults[i]))
{
for(PxU32 j = 0; j < vehicleWheelQueryResults[i].nbWheelQueryResults; j++)
{
if(vehicleWheelQueryResults[i].wheelQueryResults[j].tireContactActor)
{
hitActors.push_back
(vehicleWheelQueryResults[i].wheelQueryResults[j].tireContactActor);
}
}
}
}
delete[] vehicleWheelQueryResults;
delete[] wheelQueryResults;
}
* The following accessors to non-persistent data associated with each wheel have been replaced as follows
* PxVehicleWheelsDynData::getSuspLineStart -> PxWheelQueryResult::suspLineStart
* PxVehicleWheelsDynData::getSuspLineDir -> PxWheelQueryResult::suspLineDir
* PxVehicleWheels::getSuspRaycast -> PxWheelQueryResult::suspLineStart, PxWheelQueryResult::suspLineDir, PxWheelQueryResult::suspLineLength
* PxVehicleWheelsDynData::getTireDrivableSurfaceShape -> PxWheelQueryResult::tireContactShape
* PxVehicleWheelsDynData::getTireDrivableSurfaceMaterial -> PxWheelQueryResult::tireSurfaceMaterial
* PxVehicleWheelsDynData::getTireDrivableSurfaceType -> PxWheelQueryResult::tireSurfaceType
* PxVehicleWheelsDynData::getTireDrivableSurfaceContactPoint -> PxWheelQueryResult::tireContactPoint
* PxVehicleWheelsDynData::getTireDrivableSurfaceContactNormal -> PxWheelQueryResult::tireContactNormal
* PxVehicleWheelsDynData::getTireFriction -> PxWheelQueryResult::tireFriction
* PxVehicleWheelsDynData::getSuspJounce -> PxWheelQueryResult::suspJounce
* PxVehicleWheelsDynData::getSuspensionForce -> PxWheelQueryResult::suspSpringForce
* PxVehicleWheelsDynData::getTireLongitudinalDir -> PxWheelQueryResult::tireLongitudinalDir
* PxVehicleWheelsDynData::getTireLateralDir -> PxWheelQueryResult::tireLateralDir
* PxVehicleWheelsDynData::getTireLongSlip -> PxWheelQueryResult::longitudinalSlip
* PxVehicleWheelsDynData::getTireLatSlip ->PxWheelQueryResult::lateralSlip
* PxVehicleWheelsDynData::getSteer -> PxWheelQueryResult::steerAngle
* PxVehicleWheels::isInAir -> PxWheelQueryResult::isInAir
* PxVehicleWheels::setWheelShapeMapping and PxVehicleWheels::getWheelShapeMapping have been moved to PxVehicleWheelsSimData::setWheelShapeMapping and PxVehicleWheelsSimData::getWheelShapeMapping
* PxVehicleWheels::setSceneQueryFilterData and PxVehicleWheels::getSceneQueryFilterData have been moved to PxVehicleWheelsSimData::setSceneQueryFilterData and PxVehicleWheelsSimData::getSceneQueryFilterData
* PxVehicle4WEnable3WTadpoleMode and PxVehicle4WEnable3WDeltaMode now take an extra function argument: a non-const reference to a PxVehicleWheelsDynData
* PxVehicleWheels::isInAir() has been replaced with PxVehicleIsInAir(const PxVehicleWheelQueryResult& vehWheelQueryResults)
* PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs now takes an extra function argument "const bool isVehicleInAir". This can be calculated using the function PxVehicleIsInAir
* To improve api consistency PxVehicleTelemetryData::getNumWheelGraphs is now PxVehicleTelemetryData::getNbWheelGraphs
* To improve api consistency PX_MAX_NUM_WHEELS is now PX_MAX_NB_WHEELS
* To improve api consistency PxVehicleGraph::eMAX_NUM_TITLE_CHARS is now PxVehicleGraph::eMAX_NB_TITLE_CHARS
* PxVehicleTireData::mCamberStiffness has been replaced with PxVehicleTireData::mCamberStiffnessPerUnitGravity. PxVehicleTireData::mCamberStiffnessPerUnitGravity should be set so that it is equivalent to the old value of PxVehicleTireData::mCamberStiffness divided by the magnitude of gravitational acceleration (PxScene::getGravity().magnitude()). The advantage of using PxVehicleTireData::mCamberStiffnessPerUnitGravity is that it independent of length scale.
* PxVehicleComputeTireForceDefault has been removed from the public vehicle api. Custom tire shaders that call PxVehicleComputeTireForceDefault are best implemented by taking a copy of PxVehicleComputeTireForceDefault and calling the copy instead.
=====================================================
CCD
=====================================================
* The mechanism to activate CCD per shape has changed in 3.3. The PxShapeFlag::eUSE_SWEPT_BOUNDS that was used in 3.2 to active swept bounds per shape has been removed. In its place is a new flag PxRigidBodyFlag::eENABLE_CCD that is set per rigid actor. Setting this flag for an actor in 3.3 has approximately the same effect as setting PxShapeFlag::eUSE_SWEPT_BOUNDS on all the actor's shapes in 3.2.
* PxPairFlag::eSWEPT_INTEGRATION_LINEAR has been replaced with PxPairFlag::eCCD_LINEAR in PhysX 3.3.
* PxSceneFlag::eENABLE_SWEPT_INTEGRATION flag in 3.2 has been replaced with PxSceneFlag::eENABLE_CCD in PhysX 3.3.
* A simple example of how to enable CCD on a specific shape is given below. This demonstrates creating a body consisting of a large box and a smaller sphere, where the box is only used in discrete collision detection and the sphere is only used in CCD. The simulation filter shader shown here requires that the filter data of both shapes be flagged with eCCD_RESPONSE to generate a CCD response (PxPairFlag::eCCD_LINEAR). Likewise, the filter shader shown here is configured so that the filter data of both shapes need to be flagged with eDISCRETE_RESPONSE in order to generate a collision response (PxPairFlag::eRESOLVE_CONTACTS). A final remark is that the following shader requires that shapes of static actors have filter data with flags eDISCRETE_RESPONSE | eCCD_RESPONSE in order to ensure ccd and collision response from pairs that involve a static actor and a CCD-enabled dynamic actor::
struct CCDFilterTest
{
enum FilterFlags
{
eDISCRETE_RESPONSE = 1 << 0
eCCD_RESPONSE = 1 << 1
};
static PxFilterFlags filterShader(
PxFilterObjectAttributes attributes0,
PxFilterData filterData0,
PxFilterObjectAttributes attributes1,
PxFilterData filterData1,
PxPairFlags& pairFlags,
const void* constantBlock,
PxU32 constantBlockSize)
{
pairFlags = PxPairFlags(0);
PxU32 combo = filterData0.word0 & filterData1.word0;
if(combo & eDISCRETE_RESPONSE)
{
pairFlags |= PxPairFlag::eRESOLVE_CONTACTS;
}
if(combo & eCCD_RESPONSE)
{
pairFlags |= PxPairFlag::eCCD_LINEAR;
}
return PxFilterFlags();
}
};
....
PxRigidDynamic* dyn = getPhysics().createRigidDynamic(PxTransform(PxVec3(-4, -3.5, 0)));
PxBoxGeometry box;
box.halfExtents = PxVec3(1.f, 1.f, 1.f);
PxSphereGeometry sphere;
sphere.radius = 0.75f;
PxShape* boxShape = dyn->createShape(box, getDefaultMaterial());
PxShape* sphereShape = dyn->createShape(sphere, getDefaultMaterial());
PxFilterData data = boxShape->getSimulationFilterData();
data.word0 |= CCDFilterTest::eDISCRETE_RESPONSE;
boxShape->setSimulationFilterData(data);
data = sphereShape->getSimulationFilterData();
data.word0 |= CCDFilterTest::eCCD_RESPONSE;
sphereShape->setSimulationFilterData(data);
dyn->setRigidBodyFlag(PxRigidBodyFlag::eENABLE_CCD, true);
getActiveScene().addActor(*dyn);
=====================================================
PhysX Visual Debugger
=====================================================
* A new flag has been introduced to configure the visualizing of constraints:
PxVisualDebuggerFlag::eTRANSMIT_CONSTRAINTS;
* A new function has been introduced to configure PxVisualDebugger flags:
PxVisualDebugger::setVisualDebuggerFlags( PxVisualDebuggerFlags flags);
* A new funtion has been introduced to send error stream to PVD:
PxVisualDebugger::sendErrorMessage((PxErrorCode::Enum code, const char* message, const char* file, PxU32 line);
* The following functions were renamed:
PxVisualDebugger::getPvdConnectionFactory() -> PxVisualDebugger::getPvdConnection();
PxVisualDebugger::getPvdConnection() -> PxVisualDebugger::getPvdDataStream();
* The PVD connect function changed to the same method as previously 3.2 version:
PxVisualDebuggerExt::connect -> PxVisualDebuggerExt::createConnection;
* The constraint, contacts and scene queries visualizing can all be configed with PxVisualDebuggerFlag in 3.3. Here is an example for how to enable pvd visualizing the contacts :
mPhysics->getVisualDebugger()->setVisualDebuggerFlags(PxVisualDebuggerFlag::eTRANSMIT_CONTACTS | PxVisualDebuggerFlag::eTRANSMIT_CONSTRAINTS);
=====================================================
PhysX Cloth
=====================================================
There have been substantial changes to the PhysX 3.3 cloth solver that improve performance and behavior. This has resulted in
a reorganization of how constraints are stored and processed in the cloth fabric. Prior to PhysX 3.3 the cloth solver used fibers to
organize edge constraints into independent groups. In PhysX 3.3 it is no longer necessary to decompose constraints into fibers,
instead edge constraints now exist individually and are solved in larger, independent sets. Interface changes are detailed below:
* Previously there were multiple solver types to choose from for each group of constraints such as eFAST, eSTIFF, eBENDING, etc (previously PxClothPhaseSolverConfig::SolverType). There is now one type of solver for all edge constraints, this is a flexible distance constraint with controls to adjust stiffness within certain ranges of compression and stretch (see PxClothStretchConfig). Behaviors such as bending are now achieved by the way distance constraints are arranged geometrically, rather than through a specialized bending solver.
* To reduce stretching a new constraint type has been added called "tether" constraints. These constraints do not act along edges of the mesh, but act as long range attachments between particles that enforce a maximum distance between two points. See PxClothFabric::getTetherAnchors().
* Cloth cooking which was previously part of the PxCooking library has been moved to the extension library, see PxClothFabricCooker::
// PhysX 3.2.x
cooking->cookClothFabric(meshDesc, gravity, outputStream);
// PhysX 3.3
PxClothFabricCooker cooker(meshDesc, gravity, useGeodesicTethers);
cooker.save(outputStream, false);
* The PxClothCollisionData parameter has been removed from PxPhysx::createCloth(). The collision shapes can now be added after cloth creation using PxCloth::addCollisionSphere and PxCloth::addCollisionCapsule.
* PxCloth::wakeUp() does not have a parameter anymore. Use the newly introduced method setWakeCounter() instead to set a specific value.
* PxCloth::setDampingCoefficient now takes a PxVec3 instead of a PxReal to specify the damping per axis.
* PxCloth::setPhaseSolverConfig() has been renamed to PxCloth::setStretchConfig()
* PxCloth::lockClothReadData() has been renamed to PxCloth::lockParticleData()
* PxClothFabricTypes.h has been removed, this header has been merged with PxClothFabric.h
========================================================
RepX Serialization
========================================================
Substantial changes were made to the PhysX 3.3 serialization interface. Handling of collections and references between collections have been unified for RepX and binary serialization.
* The RepX and RepXUpgrader libraries have been removed. RepX functionality is now provided through PhysXExtensions.
* RepXCollection has been replaced with PxCollection, which is the common collection class for both RepX and binary serialization in 3.3. Collections are now instantiated on deserialization with PxSerialization::createCollectionFromXml(). Empty collections can be created with PxCreateCollection(). Serialization into RepX format is achieved through PxSerialization::serializeCollectionToXml().
* TRepXId has been replaced with PxSerialObjectId.
* RepXIdToRepXObjectMap and RepXObject have been replaced with new functionality in PxCollection, which now maps between serializable objects and PxSerialObjectId values.
* RepXExtension was removed. Serialization and deserialization of serializable types is achieved through the PxRepXSerializer interface.
* RepXUtility and PxToolkit functionality has been replaced with various functions in PxSerialization, PxCollection and PxScene.
* A PxCollection with all PxPhysics-level objects such as shapes, meshes or materials (formally referred to as buffers) can be created using PxCollectionExt::createCollection(PxPhysics&).
* Similarly PxCollectionExt::createCollection(PxScene&) can be used to create a collection of PxScene-level objects.
* Dependencies between objects and collections can be handled with PxSerialization::complete().
* The objects of a collection can be added to a scene with PxScene::addCollection().
* Operations on files are generally handled with abstract interfaces: PxOutputStream and PxInputData. Default implementations are available as PxDefaultFileOutputStream and PxDefaultFileInputData.
* RepXUpgrader::upgradeCollection was removed. RepX data can be converted to newer PhysX versions by deserializing and re-serializing a collection: PxSerialization::createCollectionFromXml(), PxSerialization::serializeCollectionToXml().
* Serialization functionality requires a PxSerializationRegistry instance which can be created with PxSerialization::createSerializationRegistry().
* XML serialization can be configured to store the cooked triangle and convex mesh data along with the plain data for faster loading.
* PhysXVehicles supports RepX serialization. PxSerializationRegistry needs to be provided to PxInitVehicleSDK() for vehicle serialization, PxCloseVehicleSDK() for cleanup.
* Custom class RepX serialization is supported in 3.3, more information please read :ref:`Serialization`.
========================================================
Binary Serialization
========================================================
The binary serialization interface has been refactored and unified with the RepX serialization interface.
* Most serialization functionality requires an instance of the new class PxSerializationRegistry. It is application managed and can be created with PxSerialization::createSerializationRegistry() and released with PxSerializationRegistry::release().
* The base class for serializable types has been renamed from PxSerializable to PxBase. Most of the serialization functionality moved to a separate PxSerializer class. A PxSerializer instance per serializable type is registered in the PxSerializationRegistry. All PhysX and PhysXExtension serializables are registered by default.
* PxCollection has been reworked.
* PxCollection::serialize() and PxCollection::deserialize() were replaced with PxSerialization::createCollectionFromBinary() PxSerialization::serializeCollectionToBinary() in PhysXExtensions.
* PxSerializable::collectForExport() has been replaced with PxCollection::add(). PxSerialzation::complete() helps to add required objects for resolving dependencies. PxSerializable::isSerializable() should be used check whether a collection can be successfully serialized.
* PxUserReferences was removed: PxCollection instances can now be used directly to resolve dependencies between collections on deserialization. PxSerialization::complete() supports creating collections with external dependencies to other collections.
* PxSerialObjectRef has been replaced with PxSerialObjectId.
* PxCollectForExportSDK() and PxCollectForExportScene() functions were replaced with PxCollectionExt::createCollection(PxPhysics& physics) and PxCollectionExt::createCollection(PxScene& scene).
* PxDumpMetaData() was replaced with PxSerialization::dumpBinaryMetaData().
* The PxBinaryConverter moved from PhysXCooking to PhysXExtensions. PxCooking::createBinaryConverter() was replaced with PxSerialization::createBinaryConverter().
* PhysXVehicles supports binary serialization. PxSerializationRegistry needs to be provided to PxInitVehicleSDK() for vehicle serialization, PxCloseVehicleSDK() for cleanup.
* Custom class binary serialization is supported in 3.3, more information please read :ref:`Serialization`.
========================================================
PhysX TaskManager
========================================================
* The pxtask namespace has been removed and all its types are now included in the physx namespace with a Px prefix, for example pxtask::LightCpuTask has become physx::PxLightCpuTask

View File

@ -0,0 +1,138 @@
.. _migrationTo34:
----------------------------------------------------
Migrating From PhysX SDK 3.3 to 3.4
----------------------------------------------------
* This guide highlights all significant parts of the API that have changed in the last dot release. An application with a working integration of the older version of PhysX should be able to easily migrate to the newer version by following these pointers.
* Functionality shared with the APEX SDK was moved into a separate "PxShared" directory outside of the "PhysX" directory. Since the PxFoundation object is part of the PxShared library, it is versioned separately. PxCreateFoundation now takes PX_FOUNDATION_VERSION as an argument.
===============
Deprecated APIs
===============
+++++++++++++++++++++++++
PxRigidActor::createShape
+++++++++++++++++++++++++
PxRigidActor::createShape() is deprecated, and will be removed in PhysX 3.5. PxRigidActorExt::createExclusiveShape() replaces this method.
++++++++++++++++++++++++++++++++++++++++++++++++
PxSceneFlag::eDEPRECATED_TRIGGER_TRIGGER_REPORTS
++++++++++++++++++++++++++++++++++++++++++++++++
PxSceneFlag::eDEPRECATED_TRIGGER_TRIGGER_REPORTS is deprecated, and will be removed in PhysX 3.5. More details are mentioned under :ref:`migration_3_4_core_phsx` further below.
++++++++++++++++++++++++++++++++++++++++++++++++
PhysX particles
++++++++++++++++++++++++++++++++++++++++++++++++
The PhysX particle feature has been deprecated in PhysX version 3.4. The standalone library PhysX FleX is an alternative with a richer feature set.
.. _migration_3_4_core_phsx:
=====================================================
Core PhysX
=====================================================
* PxCreatePhysics now requires a PxFoundation object to be passed. Optionally it receives a pointer to a PxPvd object, used for connecting PhysX with the visual debugger.
* PxActor::isRigidStatic, isRigidDynamic, isParticleSystem, isParticleFluid, isArticulationLink, isCloth, isRigidActor, isRigidBody, isParticleBase have been removed. Use corresponding PxBase::is() with class template parameter for down casting.
* PxContactPairFlag::eINTERNAL_HAS_FACE_INDICES is obsolete and has been removed.
* Trigger shapes will no longer send notification events for interactions with other trigger shapes. For PhysX 3.4 there is the option to re-enable the reports by raising PxSceneFlag::eDEPRECATED_TRIGGER_TRIGGER_REPORTS but this option will no longer be available in PhysX 3.5. It is recommended to not make use of eDEPRECATED_TRIGGER_TRIGGER_REPORTS and instead use a trigger and a non-trigger shape, both with the same geometry and local pose, to emulate getting notifications for overlaps between trigger shapes.
* Implementations of PxSimulationEventCallback will have to provide an (empty) implementation of the newly added method *onAdvance()* to avoid compilation errors.
* The deprecated method PxPhysics::createHeightField(const PxHeightFieldDesc&) has been removed. Please use PxCooking::createHeightField(const PxHeightFieldDesc&, PxPhysicsInsertionCallback&) instead. The insertion callback can be obtained through PxPhysics::getPhysicsInsertionCallback().
* The deprecated flag PxActorTypeSelectionFlag/PxActorTypeSelectionFlags has been removed. Please use PxActorTypeFlag/PxActorTypeFlags instead.
* The deprecated class PxFindOverlapTriangleMeshUtil has been removed. Please use PxMeshOverlapUtil instead.
* The deprecated flag PxConstraintFlag::eREPORTING has been removed. Force reports are now always generated.
* The deprecated flag PxConstraintFlag::eDEPRECATED_32_COMPATIBILITY has been removed.
* PxRegisterHeightFields() now registers unified heightfields. To register legacy heightfields, call PxRegisterLegacyHeightFields(). Legacy heightfield collision is deprecated and will be removed in a future PhysX release.
* The following deprecated simulation event flags have been removed:
* PxContactPairHeaderFlag::eDELETED_ACTOR_0, ::eDELETED_ACTOR_1
(use PxContactPairHeaderFlag::eREMOVED_ACTOR_0, ::eREMOVED_ACTOR_1 instead)
* PxContactPairFlag::eDELETED_SHAPE_0, ::eDELETED_SHAPE_1
(use PxContactPairFlag::eREMOVED_SHAPE_0, ::eREMOVED_SHAPE_1 instead)
* PxTriggerPairFlag::eDELETED_SHAPE_TRIGGER, ::eDELETED_SHAPE_OTHER
(use PxTriggerPairFlag::eREMOVED_SHAPE_TRIGGER, ::REMOVED_SHAPE_OTHER instead)
* PxContactPair now reports separate pointers for contactPatches, contactPoints and contactImpulses rather than reporting a single pointer that the PxContactStreamIterator parses. The interface for PxContactStreamIterator has been modified accordingly. See the PxContactPair::extractContacts implementation for further guidance on how to iterate over this contact data if required.
===================
Contact Generation
===================
* PCM contact generation is now used by default. Legacy SAT-based contact generation can be re-enabled by clearing the PxSceneFlag::eENABLE_PCM from PxSceneDesc::flags.
* Unified heightfields are now the default heightfield collision approach. This approach mirrors the way in which mesh contact gen functions so permits meshes and heightfields to be used interchangeably with negligible behavioral difference. The legacy heightfield collision approach can be used by calling PxRegisterLegacyHeightFields().
* When unified heightfields are in use, the bounds of heightfield shapes will not be extruded by "thickness". If legacy heightfield collision is used, the bounds will still be extruded by thickness.
=============
PhysX Cooking
=============
* The deprecated flags PxMeshPreprocessingFlag::eREMOVE_UNREFERENCED_VERTICES and ::eREMOVE_DUPLICATED_TRIANGLES have been removed. Meshes get cleaned up by default unless PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH is set.
* PxCookingParams::meshSizePerformanceTradeOff and PxCookingParams::meshCookingHint have been moved to PxBVH33MidphaseDesc, since they only affect the BVH33.
* The PxGaussMapLimit.h file has been removed. The PxGetGaussMapVertexLimitForPlatform function has been moved to PxCooking.h, but the function is now deprecated, along with the PxPlatform enum. Instead there is now an explicit PxCookingParams::gaussMapLimit parameter. As far as transition to PhysX 3.4 is concerned there is nothing to do other than removing includes of PxGaussMapLimit.h, and perhaps including PxCooking.h instead if needed.
* Legacy convex hull generator (PxConvexMeshCookingType::eINFLATION_INCREMENTAL_HULL) uses inflation in all cases. To cook a convex mesh without inflation new (default) PxConvexMeshCookingType::eQUICKHULL algorithm must be used. This algorithm does not support inflation.
==================
Reference Counting
==================
* In previous releases, isReleasable() for shareable objects (shapes, triangle meshes, convex meshes, cloth fabrics, materials and heightfields) would return false once release() had been called on the object, which was only allowed once. In 3.4, reference counts can be manually incremented with acquireReference() and decremented with release(), and so the fact that release() has called once is not a reliable indicator of whether it can be called again.
* As a consequence of the above, applications must ensure they own at least one counted reference to each shareable object in a collection before calling PxCollectionExt::releaseObjects. The main case in which this might be different in 3.4 is when using PxRigidActor::createShape(), since in that case only the actor has a counted reference to the shape. In this specific case, the new parameter releaseExclusiveShapes to PxCollectionExt::releaseObjects may be helpful.
* Since there is no unique user release for shareable objects, they do not generate USER_RELEASE events when release() is called.
=====================================================
PhysX Visual Debugger
=====================================================
* PxVisualDebugger is deprecated, and new PxPvd has been introduced. More details are mentioned in :ref:`physxvisualdebugger`.
=====================================================
Scene queries
=====================================================
* PxPruningStructure enum has been renamed to PxPruningStructureType
* Deprecated type PxSceneQueryHit has been removed. Please use PxQueryHit instead.
* Deprecated type PxSceneQueryFilterData has been removed. Please use PxQueryFilterData instead.
* Deprecated type PxSceneQueryFilterCallback has been removed. Please use PxQueryFilterCallback instead.
* Deprecated type PxSceneQueryCache has been removed. Please use PxQueryCache instead.
* Deprecated types PxSceneQueryFlag(s) has been removed. Please use PxHitFlag(s) instead.
* Deprecated scene query functions have been removed (e.g. PxScene::raycastAny(), etc). To make the transition easier they are still available in PxSceneQueryExt.h, as part of PhysXExtensions. A previous PxScene::raycastAny(...) call should now either use PxSceneQueryExt::raycastAny(PxScene, ...), or PxScene::raycast(...).
* PxHitFlag::eFACE_INDEX was introduced. In order to receive the face index in sweeps against convex geometry, the flag needs to be set.
* PxHitFlag::eDISTANCE has been deprecated, since the distance is always needed and its computation cannot be skipped. Please simply avoid using that flag from now on. The flag has no effect and it will be removed in the next version.
* The "anyHit" parameter of the PxGeometryQuery::raycast() and PxShapeExt::raycast() functions has been removed. Please use PxHitFlag::eMESH_ANY instead.
* PxMeshQuery::sweep() now respects PxHitFlag::eMESH_BOTH_SIDES. So if you previously used that flag when calling that function, it was ignored, and the upgrade to 3.4 might start generating different results compared to 3.3. If keeping the previous behaviour is important, please disable PxHitFlag::eMESH_BOTH_SIDES in your PxMeshQuery::sweep() calls.
* Batched scene queries are marked as deprecated and will be replaced by new system in future releases.
* Volume cache feature is marked as deprecated, it will be removed in future releases.
* Spatial index feature is marked as deprecated, it will be removed in future releases.
=====================================================
PxExtensions
=====================================================
* The signatures for the PxComputeMeshPenetration and PxComputeHeightFieldPenetration functions have changed. The old functions are still available but they are now deprecated. It is recommended to transition to the new functions (with the same names but a different signature).

View File

@ -0,0 +1,115 @@
.. include:: <isonum.txt>
.. _migrationTo40:
----------------------------------------------------
Migrating From PhysX SDK 3.4 to 4.0
----------------------------------------------------
* This guide highlights all significant parts of the API that have changed in the last dot release. An application with a working integration of the older version of PhysX should be able to easily migrate to the newer version by following these pointers.
===============
Deprecated APIs
===============
Newly deprecated APIs are:
* The PxD6Joint::getTwist() function has been deprecated. Please use PxD6Joint::getTwistAngle() now.
* The PxD6Joint::setLinearLimit() and PxD6Joint::getLinearLimit() functions have been deprecated. Please use PxD6Joint::setDistanceLimit() and PxD6Joint::getDistanceLimit() now.
=====================================================
PhysX particles
=====================================================
The PhysX particle feature has been removed from PhysX version 4. The standalone library PhysX FleX is an alternative with a richer feature set.
=====================================================
PhysX clothing
=====================================================
The PhysX clothing feature has been removed from PhysX version 4. Please use the standalone library NvCloth instead.
=====================================================
PhysX distribution
=====================================================
++++++++++++++++++++++++++++++++++++++++++++++++
CMake build
++++++++++++++++++++++++++++++++++++++++++++++++
PhysX is now distributed without pre-built build configuration files (e.g. Microsoft Visual Studio project and solutions or makefiles). The build configuration files are now exclusively generated with CMake, which needs to be pre-installed along with Python.
* CMake minimum version 3.12
* Python minimum version 2.7.6
In order to build the PhysX SDK, Snippets and Samples (Windows only) the **generate_projects** script in the *physx/* directory of the PhysX distribution needs to be run. The script accepts a [tool/platform] **preset** either from prompt or as a command line parameter and generates the corresponding build configuration files in *physx/compiler/*.
The new PhysX SDK 4.0 build produces libs and binaries with different output paths and names compared to PhysX SDK 3.4. For example:
* Lib/vc14win64/PhysX3CHECKED_x64.lib |rarr| bin/win.x86_64.vc140.mt/checked/PhysX_64.lib
* Bin/vc14win64/PhysX3CHECKED_x64.dll |rarr| bin/win.x86_64.vc140.mt/checked/PhysX_64.dll
If the application needs to use the old naming scheme, please refer to the documentation of PxDelayLoadHook and PxGpuLoadHook.
The Linux build has been changed to produce static opposed to shared libraries. The compiler was switched from GCC to Clang.
The naming convention for directories has changed in the PhysX SDK 4.0. Instead of camel-casing, source and include directories are now named in all-lower-case.
++++++++++++++++++++++++++++++++++++++++++++++++
PxShared
++++++++++++++++++++++++++++++++++++++++++++++++
The PxShared library contains functionality shared beyond the PhysX SDK. For PhysX 4.0 it has been streamlined to a minimal set of headers.
* The PxFoundation singleton has been moved back to the PhysX SDK. PxFoundationVersion.h and PX_FOUNDATION_VERSION has been removed. PxCreateFoundation takes now PX_PHYSICS_VERSION as a parameter. Foundation dependent profiling macros (PX_PROFILE_ZONE, PX_PROFILE_START_CROSSTHREAD, PX_PROFILE_STOP_CROSSTHREAD) have been moved to physx/include/common/PxProfileZone.h. The foundation library binary has been renamed from PxFoundation[config]_[x64|x86] to [config]/PhysXFoundation_[64|32].
* The PxTask library has been moved back to the PhysX SDK.
* The PxCudaContextManager interface has been moved to physx/include/cudamanager.
* The PxPvdSDK library has been moved to the PhysX SDK. On Windows and Linux it has been changed from a dynamic to a static linked library.
=====================================================
Core PhysX
=====================================================
* The active transforms feature (PxActiveTransform, PxScene::getActiveTransforms, PxSceneFlag::eENABLE_ACTIVETRANSFORMS) has been removed from PhysX version 4. Please use the active actors feature instead.
* The PxRigidActor::createShape() function has been removed. Please use PxRigidActorExt::createExclusiveShape() instead.
* The PxVisualizationParameter::eDEPRECATED_BODY_JOINT_GROUPS parameter has been removed. It was not implemented and had no effect.
* The PxSceneDesc::maxNbObjectsPerRegion has been removed. It was not implemented and had no effect.
* The PxHitFlag::eDISTANCE flag has been removed. The distance was always computed with or without the flag.
* Switching a kinematic object to dynamic does not automatically wake up the object anymore. Explicit calls to *PxRigidDynamic::wakeUp()* are now needed.
* Switching a kinematic object to dynamic re-inserts the object into the broadphase, producing PxPairFlag::eNOTIFY_TOUCH_FOUND events instead of PxPairFlag::eNOTIFY_TOUCH_PERSISTS events.
* The PxSceneFlag::eSUPPRESS_EAGER_SCENE_QUERY_REFIT has been replaced by PxSceneQueryUpdateMode::enum. Please use PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_DISABLED instead.
* PxGetGaussMapVertexLimitForPlatform has been replaced with PxCookingParams::gaussMapLimit.
* PxBroadPhaseType::eABP is now the default broadphase, which might affect the performance of your application when upgrading. To select the previous default broadphase, use PxBroadPhaseType::eSAP explicitly in PxSceneDesc.
* PxTolerancesScale::mass got removed because it was not used anymore internally. Simply remove the corresponding setup code from your application.
* The multi-client feature has changed, in particular the client behavior flags are gone. Users must now implement the desired behaviors in their application. The PxClient values are still supported and stored in PhysX actors. The desired behavior and filtering can then be implemented by users directly in their callbacks, based on client IDs retrieved from provided actors.
* The legacy heightfields have been removed. What was previously called "unified heightfields" is now the only available implementation. Heightfields are handled as triangle meshes, so fast moving objects can now tunnel through the heightfield surface if continuous collision detection (CCD) is not used. Use CCD to solve this issue.
* PxCookingParams::meshCookingHint and PxCookingParams::meshSizePerformanceTradeOff have been moved to the PxBVH33MidphaseDesc structure. These parameters only affect the BVH33 structures.
* PxSceneFlag::eDEPRECATED_TRIGGER_TRIGGER_REPORTS has been removed. It is recommended to use a trigger and a non-trigger shape, both with the same geometry and local pose, to emulate getting notifications for overlaps between trigger shapes.
* PxConvexMeshCookingType::eINFLATION_INCREMENTAL_HULL and PxCookingParams::skinWidth have been removed. Please use the regular PxShape::setRestOffset.
* PxBVH34MidphaseDesc::numTrisPerLeaf has been renamed to PxBVH34MidphaseDesc::numPrimsPerLeaf
* PxFoundationDelayLoadHook has been merged into PxDelayLoadHook. The per build configuration callbacks for PxDelayLoadHook and PxGpuLoadHook have been removed. The application provided implementations of PxDelayLoadHook::getPhysXFoundationDllName, PxDelayLoadHook::getPhysXCommonDllName and PxGpuLoadHook::getPhysXGpuDllName now need to provide the correct DLL paths/names based on the application build configurations.
* A common base class for PxArticulation and PxArticulationReducedCoordinate was introduced: PxArticulationBase. This class encapsulates common functionality between both articulation implementations. API interfaces previously taking/returning a PxArticulation will now take/return PxArticulationBase. The user must statically up-cast to the correct type if non-common functionality is required.
* A common base class for PxArticulationJoint and PxArticulationJointReducedCoordinate was introduced: PxArticulationJointBase. This class encapsulates common functionality between both reduced and legacy articulations. API interfaces previously taking/return a PxArticulationJoint will now take/return PxArticulationJointBase. The user must statically up-cast to the correct type if implementation-specific functionality is required.
* Getting/setting linear damping, angular damping and max angular velocity has been migrated from PxRigidDynamic to PxRigidBody to permit usage of these methods with the new reduced coordinate articulations.
=====================================================
PhysX Extensions
=====================================================
* The PxD6Joint::getTwist() function has been deprecated. Please use PxD6Joint::getTwistAngle() now.
* The PxD6Joint::setLinearLimit() and PxD6Joint::getLinearLimit() functions have been deprecated. Please use PxD6Joint::setDistanceLimit() and PxD6Joint::getDistanceLimit() now.
* PxComputeHeightFieldPenetration has a new signature.
* PxComputeMeshPenetration has been removed. Use PxComputeTriangleMeshPenetration instead.
.. _migration_4_0_core_phsx:

View File

@ -0,0 +1,19 @@
.. _originshift:
------------
Scene Origin
------------
The further away objects move from the origin, the larger the chance to suffer from floating point precision issues. This can cause troubles especially in scenarios with big game worlds. To avoid these problems, a straightforward solution seems to teleport all objects towards the origin in certain intervals. However, this is not only cumbersome but can also be pretty expensive due to the invalidation of cached data and persistent state. To address some of these issues, PhysX offers an API to shift the origin of a scene.
=========================
Shifting The Scene Origin
=========================
The following method will shift the origin of a scene by a translation vector::
PxScene::shiftOrigin(const PxVec3& shift)
The positions of all objects in the scene and the corresponding data structures will get adjusted to reflect the new origin location (basically, the shift vector will get subtracted from all object positions). The intended use pattern for this API is to shift the origin such that object positions move closer towards zero. Please note that it is the user's responsibility to keep track of the summed total origin shift and adjust all input/output to/from PhysX accordingly.
Even though this method preserves some of the internally cached data, it is still an expensive operation and we recommend to use it only in the case where distance related precision issues may arise in areas far from the origin.
If extension modules of PhysX are used like the character controller or vehicle library, then it will be necessary to propagate the scene shift to those modules as well. Please refer to the API documentation of these modules for details.

View File

@ -0,0 +1,404 @@
.. _RigidBodyCollision:
------------------------
Rigid Body Collision
------------------------
=========================
Introduction
=========================
This section will introduce the fundamentals of rigid body collision.
.. _RigidBodyCollisionShapes:
=========================
Shapes
=========================
Shapes describe the spatial extent and collision properties of actors. They are used for three purposes within PhysX: intersection tests that determine the contacting features of rigid objects, scene query tests such as raycasts, and defining trigger volumes that generate notifications when other shapes intersect with them.
Shapes are reference counted, see :ref:`BasicReferenceCounting`.
Each shape contains a PxGeometry object and a reference to a PxMaterial, which must both be specified upon creation.
The following code creates a shape with a sphere geometry and a specific material::
PxShape* shape = physics.createShape(PxSphereGeometry(1.0f), myMaterial, true);
myActor.attachShape(*shape);
shape->release();
The method PxRigidActorExt::createExclusiveShape() is equivalent to the three lines above.
.. note:: for reference counting behavior of deserialized shapes refer to :ref:`DeserializeReferenceCounting`.
The parameter 'true' to createShape() informs the SDK that the shape will not be shared with other actors. You can use shape sharing to reduce the memory costs of your simulation when you have many actors with identical geometry, but shared shapes have a very strong restriction: you cannot update the attributes of a shared shape while it is attached to an actor.
Optionally you may configure a shape by specifying shape flags of type PxShapeFlags. By default a shape is configured as
* a simulation shape (enabled for contact generation during simulation)
* a scene query shape (enabled for scene queries)
* being visualized if debug rendering is enabled
When a geometry object is specified for a shape, the geometry object is copied into the shape. There are some restrictions on which geometries may be specified for a shape, depending on the shape flags and the type of the parent actors.
* TriangleMesh, HeightField and Plane geometries are not supported for simulation shapes that are attached to dynamic actors, unless the dynamic actors are configured to be kinematic.
* TriangleMesh and HeightField geometries are not supported for trigger shapes.
See the following sections for more details.
Detach the shape from the actor as follows::
myActor.detachShape(*shape);
========================================
Simulation Shapes and Scene Query Shapes
========================================
Shapes may be independently configured to participate in either or both of scene queries and contact tests. By default, a shape will participate in both.
The following pseudo-code configures a PxShape instance so that it no longer participates in shape pair intersection tests::
void disableShapeInContactTests(PxShape* shape)
{
shape->setFlag(PxShapeFlag::eSIMULATION_SHAPE,false);
}
A PxShape instance can be configured to participate in shape pair intersection tests as follows::
void enableShapeInContactTests(PxShape* shape)
{
shape->setFlag(PxShapeFlag::eSIMULATION_SHAPE,true);
}
To disable a PxShape instance from scene query tests::
void disableShapeInSceneQueryTests(PxShape* shape)
{
shape->setFlag(PxShapeFlag::eSCENE_QUERY_SHAPE,false);
}
Finally, a PxShape instance can be re-enabled in scene query tests::
void enableShapeInSceneQueryTests(PxShape* shape)
{
shape->setFlag(PxShapeFlag::eSCENE_QUERY_SHAPE,true);
}
.. note:: If the movement of the shape's actor does not need to be controlled by the simulation at all, i.e., the shape is used for scene queries only and gets moved manually if necessary, then memory can be saved by additionally disabling simulation on the actor (see the API documentation on PxActorFlag::eDISABLE_SIMULATION).
===============================================
Kinematic Triangle Meshes (Planes, Heighfields)
===============================================
It is possible to create a kinematic PxRigidDynamic which can have a triangle mesh (plane, heighfield) shape. If this shape has a simulation shape flag, this actor must stay kinematic. If you change the flag to not simulated, you can switch even the kinematic flag.
To setup kinematic triangle mesh see following code::
PxRigidDynamic* meshActor = getPhysics().createRigidDynamic(PxTransform(1.0f));
PxShape* meshShape;
if(meshActor)
{
meshActor->setRigidDynamicFlag(PxRigidDynamicFlag::eKINEMATIC, true);
PxTriangleMeshGeometry triGeom;
triGeom.triangleMesh = triangleMesh;
meshShape = PxRigidActorExt::createExclusiveShape(*meshActor,triGeom,
defaultMaterial);
getScene().addActor(*meshActor);
}
To switch a kinematic triangle mesh actor to a dynamic actor::
PxRigidDynamic* meshActor = getPhysics().createRigidDynamic(PxTransform(1.0f));
PxShape* meshShape;
if(meshActor)
{
meshActor->setRigidDynamicFlag(PxRigidDynamicFlag::eKINEMATIC, true);
PxTriangleMeshGeometry triGeom;
triGeom.triangleMesh = triangleMesh;
meshShape = PxRigidActorExt::createExclusiveShape(*meshActor, triGeom,
defaultMaterial);
getScene().addActor(*meshActor);
PxConvexMeshGeometry convexGeom = PxConvexMeshGeometry(convexBox);
convexShape = PxRigidActorExt::createExclusiveShape(*meshActor, convexGeom,
defaultMaterial);
======================
Broad-phase Algorithms
======================
PhysX supports several broad-phase algorithms:
* *sweep-and-prune (SAP)*
* *multi box pruning (MBP)*
* *automatic box pruning (ABP)*
*PxBroadPhaseType::eSAP* is a good generic choice with great performance when many objects are sleeping. Performance can degrade significantly though, when all objects are moving, or when large numbers of objects are added to or removed from the broad-phase. This algorithm does not need world bounds to be defined in order to work.
*PxBroadPhaseType::eMBP* is a new algorithm introduced in PhysX 3.3. It is an alternative broad-phase algorithm that does not suffer from the same performance issues as eSAP when all objects are moving or when inserting large numbers of objects. However its generic performance when many objects are sleeping might be inferior to eSAP, and it requires users to define world bounds in order to work.
*PxBroadPhaseType::eABP* is a revisited implementation of *PxBroadPhaseType::eMBP* introduced in PhysX 4. It automatically manages world bounds and broad-phase regions, thus offering the convenience of *PxBroadPhaseType::eSAP* coupled to the performance of *PxBroadPhaseType::eMBP*. While *PxBroadPhaseType::eSAP* can remain faster when most objects are sleeping and *PxBroadPhaseType::eMBP* can remain faster when it uses a large number of properly-defined regions, *PxBroadPhaseType::eABP* often gives the best performance on average and the best memory usage. It is a good default choice for the broadphase.
The desired broad-phase algorithm is controlled by the *PxBroadPhaseType* enum, within the *PxSceneDesc* structure.
===================
Regions of Interest
===================
A region of interest is a world-space AABB around a volume of space controlled by the broad-phase. Objects contained inside those regions are properly handled by the broad-phase. Objects falling outside of those regions lose all collision detection. Ideally those regions should cover the whole simulation space, while limiting the amount of covered empty space.
Regions can overlap, although for maximum efficiency it is recommended to minimize the amount of overlap between regions as much as possible. Note that two regions whose AABBs just touch are not considered overlapping. For example the *PxBroadPhaseExt::createRegionsFromWorldBounds* helper function creates a number of non-overlapping region bounds by simply subdividing a given world AABB into a regular 2D grid.
Regions can be defined by the *PxBroadPhaseRegion* structure, along with a user-data assigned to them. They can be defined at scene creation time or at runtime using the *PxScene::addBroadPhaseRegion* function. The SDK returns handles assigned to the newly created regions, that can be used later to remove regions using the *PxScene::removeBroadPhaseRegion* function.
A newly added region may overlap already existing objects. The SDK can automatically add those objects to the new region, if the *populateRegion* parameter from the *PxScene::addBroadPhaseRegion* call is set. However this operation is not cheap and might have a high impact on performance, especially when several regions are added in the same frame. Thus, it is recommended to disable it whenever possible. The region would then be created empty, and it would only be populated either with objects added to the scene after the region has been created, or with previously existing objects when they are updated (i.e. when they move).
Note that only *PxBroadPhaseType::eMBP* requires regions to be defined. The *PxBroadPhaseType::eSAP* and *PxBroadPhaseType::eABP* algorithms do not. This information is captured within the *PxBroadPhaseCaps* structure, which lists information and capabilities of each broad-phase algorithm. This structure can be retrieved by the *PxScene::getBroadPhaseCaps* function.
Runtime information about current regions can be retrieved using the *PxScene::getNbBroadPhaseRegions* and *PxScene::getBroadPhaseRegions* functions.
The maximum number of regions is currently limited to 256.
====================
Broad-phase Callback
====================
A callback for broad-phase-related events can be defined within the *PxSceneDesc* structure. This *PxBroadPhaseCallback* object will be called when objects are found out of the specified regions of interest, i.e. "out of bounds". The SDK disables collision detection for those objects. It is re-enabled automatically as soon as the objects re-enter a valid region.
It is up to users to decide what to do with out-of-bounds objects. Typical options are:
* delete the objects
* let them continue their motion without collisions until they re-enter a valid region
* artificially teleport them back to a valid place
.. _collisionFiltering:
===============================
Collision Filtering
===============================
In almost all applications beyond the trivial, the need arises to exempt certain pairs of objects from interacting, or to configure the SDK collision detection behavior in a particular way for an interacting pair. In the submarine sample, like indicated above, we need to be notified when the submarine touched a mine, or the chain of a mine, so that we can have them blow up. The crab's AI also needs to know when crabs touch the heightfield.
Before we can understand what the sample does to achieve this, we need to understand the possibilities of the SDK filtering system. Because filtering potentially interacting pairs happens in the deepest parts of the simulation engine, and needs to be applied to all pairs of objects that come near each other, it is particularly performance sensitive. The simplest way to implement it would be to always call a callback function to each potentially interacting pair, where the application, based on the two object pointers could determine, using some custom logic -- like consulting its world data base -- whether the pair should interact. Unfortunately this quickly becomes too slow if done for a very large game world, especially if the collision detection processing happens on a remote processor like the GPU or an other kind of vector processor with local memory, which would have to suspend its parallel computations, interrupt the main processor that runs game code, and have it execute the callback before it can continue. Even if it were to be executed on a CPU, it would likely be done so simultaneously on multiple cores or hyperthreads, and thread safe code would have to be put in place to make sure that concurrent access to shared data is safe. Far better is to use some kind of fixed function logic that can execute on the remote processor. This is what we did in PhysX 2.x -- unfortunately the simple group based filtering rules we provided were not flexible enough to cover all applications. In 3.0, we introduce both a shader system, which lets the developer implement an arbitrary system of rules using code that runs on the vector processor (and is therefore not able to access any eventual game data base in main memory), which is more flexible than 2.x fixed function filtering, but just as efficient, and a totally flexible callback mechanism where the filter shader calls a CPU callback function that is able to access any application data, at the cost of performance -- see PxSimulationFilterCallback for details. The best part is that an application can decide on a per-pair basis to make this speed vs. flexibility trade-off.
Let us look at the shader system first: Here is the filter shader implemented by SampleSubmarine::
PxFilterFlags SampleSubmarineFilterShader(
PxFilterObjectAttributes attributes0, PxFilterData filterData0,
PxFilterObjectAttributes attributes1, PxFilterData filterData1,
PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
{
// let triggers through
if(PxFilterObjectIsTrigger(attributes0) || PxFilterObjectIsTrigger(attributes1))
{
pairFlags = PxPairFlag::eTRIGGER_DEFAULT;
return PxFilterFlag::eDEFAULT;
}
// generate contacts for all that were not filtered above
pairFlags = PxPairFlag::eCONTACT_DEFAULT;
// trigger the contact callback for pairs (A,B) where
// the filtermask of A contains the ID of B and vice versa.
if((filterData0.word0 & filterData1.word1) && (filterData1.word0 & filterData0.word1))
pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND;
return PxFilterFlag::eDEFAULT;
}
SampleSubmarineFilterShader is a simple shader function that is an implementation of the PxSimulationFilterShader prototype declared in PxFiltering.h. The shader filter function (called SampleSubmarineFilterShader above) may not reference any memory other than arguments of the function and its own local stack variables -- because the function may be compiled and executed on a remote processor.
SampleSubmarineFilterShader() will be called for all pairs of shapes that come near each other -- more precisely: for all pairs of shapes whose axis aligned bounding boxes in world space are found to intersect for the first time. All behavior beyond that is determined by what SampleSubmarineFilterShader() returns.
The arguments of SampleSubmarineFilterShader() include PxFilterObjectAttributes and PxFilterData for the two objects, and a constant block of memory. Note that the pointers to the two objects are NOT passed, because those pointers refer to the computer's main memory, and that may, as we said, not be available to the shader, so the pointers would not be very useful, as dereferencing them would likely cause a crash. PxFilterObjectAttributes and PxFilterData are intended to contain all the useful information that one could quickly glean from the pointers. PxFilterObjectAttributes are 32 bits of data, that encode the type of object: For example PxFilterObjectType::eRIGID_STATIC or ::eRIGID_DYNAMIC. Additionally, it lets you find out if the object is kinematic, or a trigger.
Each PxShape has a member variable of type PxFilterData. This is 128 bits of user defined data that can be used to store application specific information related to collision filtering. This is the other variable that is passed to SampleSubmarineFilterShader() for each object.
There is also the constant block. This is a chunk of per-scene global information that the application can give to the shader to operate on. You will want to use this to encode rules about what to filter and what not.
Finally, SampleSubmarineFilterShader() also has a PxPairFlags parameter. This is an output, like the return value PxFilterFlags, though used slightly differently. PxFilterFlags tells the SDK if it should ignore the pair for good (eKILL), ignore the pair while it is overlapping, but ask again, when filtering related data changes for one of the objects (eSUPPRESS), or call the low performance but more flexible CPU callback if the shader cannot decide (eCALLBACK).
PxPairFlags specifies additional flags that stand for actions that the simulation should take in the future for this pair. For example, eNOTIFY_TOUCH_FOUND means notify the user when the pair really starts to touch, not just potentially.
Let us look at what the above shader does::
// let triggers through
if(PxFilterObjectIsTrigger(attributes0) || PxFilterObjectIsTrigger(attributes1))
{
pairFlags = PxPairFlag::eTRIGGER_DEFAULT;
return PxFilterFlag::eDEFAULT;
}
This means that if either object is a trigger, then perform default trigger behavior (notify the application about start and end of touch), and otherwise perform 'default' collision detection between them.
::
// generate contacts for all that were not filtered above
pairFlags = PxPairFlag::eCONTACT_DEFAULT;
// trigger the contact callback for pairs (A,B) where
// the filtermask of A contains the ID of B and vice versa.
if((filterData0.word0 & filterData1.word1) && (filterData1.word0 & filterData0.word1))
pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND;
return PxFilterFlag::eDEFAULT;
This says that for all other objects, perform 'default' collision handling. In addition, there is a rule based on the filterDatas that determines particular pairs where we ask for touch notifications. To understand what this means, we need to know the special meaning that the sample gives to the filterDatas.
The needs of the sample are very basic, so we will use a very simple scheme to take care of it. The sample first gives named codes to the different object types using a custom enumeration::
struct FilterGroup
{
enum Enum
{
eSUBMARINE = (1 << 0),
eMINE_HEAD = (1 << 1),
eMINE_LINK = (1 << 2),
eCRAB = (1 << 3),
eHEIGHTFIELD = (1 << 4),
};
};
The sample identifies each shape's type by assigning its PxFilterData::word0 to this FilterGroup type. Then, it puts a bit mask that specifies each type of object that should generate a report when touched by an object of type word0 into word1. This could be done in the samples whenever a shape is created, but because shape creation is a bit encapsulated in SampleBase, it is done after the fact, using this function::
void setupFiltering(PxRigidActor* actor, PxU32 filterGroup, PxU32 filterMask)
{
PxFilterData filterData;
filterData.word0 = filterGroup; // word0 = own ID
filterData.word1 = filterMask; // word1 = ID mask to filter pairs that trigger a
// contact callback;
const PxU32 numShapes = actor->getNbShapes();
PxShape** shapes = (PxShape**)SAMPLE_ALLOC(sizeof(PxShape*)*numShapes);
actor->getShapes(shapes, numShapes);
for(PxU32 i = 0; i < numShapes; i++)
{
PxShape* shape = shapes[i];
shape->setSimulationFilterData(filterData);
}
SAMPLE_FREE(shapes);
}
This sets up the PxFilterDatas of each shape belonging to the passed actor. Here are some examples how this is used in SampleSubmarine::
setupFiltering(mSubmarineActor, FilterGroup::eSUBMARINE, FilterGroup::eMINE_HEAD |
FilterGroup::eMINE_LINK);
setupFiltering(link, FilterGroup::eMINE_LINK, FilterGroup::eSUBMARINE);
setupFiltering(mineHead, FilterGroup::eMINE_HEAD, FilterGroup::eSUBMARINE);
setupFiltering(heightField, FilterGroup::eHEIGHTFIELD, FilterGroup::eCRAB);
setupFiltering(mCrabBody, FilterGroup::eCRAB, FilterGroup::eHEIGHTFIELD);
This scheme is probably too simplistic to use in a serious application, but it shows the basic usage of the filter shader, and it will ensure that SampleSubmarine::onContact() is called for all interesting pairs.
An alternative group based filtering mechanism is provided with source in the extensions function PxDefaultSimulationFilterShader. And, again, if this shader based system is too inflexible, consider using the callback approach provided with PxSimulationFilterCallback.
===============================
Aggregates
===============================
An aggregate is a collection of actors. Aggregates do not provide extra simulation or query features, but allow you to tell the SDK that a set of actors will be clustered together, which in turn allows the SDK to optimize its spatial data operations. A typical use case is a ragdoll, made of multiple different actors. Without aggregates, this gives rise to as many broad-phase entries as there are shapes in the ragdoll. It is typically more efficient to represent the ragdoll in the broad-phase as a single entity, and perform internal overlap tests in a second pass if necessary. Another potential use case is a single actor with a large number of attached shapes.
==============================
Creating an Aggregate
==============================
Create an aggregate from the *PxPhysics* object::
PxPhysics* physics; // The physics SDK object
PxU32 nbActors; // Max number of actors expected in the aggregate
bool selfCollisions = true;
PxAggregate* aggregate = physics->createAggregate(nbActors, selfCollisions);
The maximum number of actors is currently limited to 128, and for efficiency should be set as low as possible.
If you will never need collisions between the actors of the aggregate, disable them at creation time. This is much more efficient than using the scene filtering mechanism, as it bypasses all internal filtering logic. A typical use case would be an aggregate of static or kinematic actors.
Note that both the maximum number of actors and the self-collision attribute are immutable.
====================================
Populating an Aggregate
====================================
Adds an actor to an aggregate as follows::
PxActor& actor; // Some actor, previously created
aggregate->addActor(actor);
Note that if the actor already belongs to a scene, the call is ignored. Either add the actors to an aggregate and then add the aggregate to the scene, or add the aggregate to the scene and then the actors to the aggregate.
To add the aggregate to a scene (before or after populating it)::
scene->addAggregate(*aggregate);
Similarly, to remove the aggregate from the scene::
scene->removeAggregate(*aggregate);
===============================
Releasing an Aggregate
===============================
To release an aggregate::
PxAggregate* aggregate; // The aggregate we previously created
aggregate->release();
Releasing the PxAggregate does not release the aggregated actors. If the PxAggregate belongs to a scene, the actors are automatically re-inserted in that scene. If you intend to delete both the PxAggregate and its actors, it is most efficient to release the actors first, then release the PxAggregate when it is empty.
===============================================================
Amortizing Insertion
===============================================================
Adding many objects to a scene in one frame can be a costly operation. This can be the case for a ragdoll, which as discussed is a good candidate for PxAggregate. Another case is localized debris, for which self-collisions are often disabled. To amortize the cost of object insertion into the broad-phase structure over several, spawn the debris in a PxAggregate, then remove each actor from the aggregate and and re-insert it into the scene over those frames.
===============
Trigger Shapes
===============
Trigger shapes play no part in the simulation of the scene (though they can be configured to participate in scene queries). Instead, their role is to report that there has been an overlap with another shape. Contacts are not generated for the intersection, and as a result contact reports are not available for trigger shapes. Further, because triggers play no part in the simulation, the SDK will not allow the the eSIMULATION_SHAPE eTRIGGER_SHAPE flags to be raised simultaneously; that is, if one flag is raised then attempts to raise the other will be rejected, and an error will be passed to the error stream.
Trigger shapes have been used in SampleSubmarine to determine if the submarine has reached the treasure. In the following code the PxActor representing the treasure has its solitary shape configured as a trigger shapes::
PxShape* treasureShape;
gTreasureActor->getShapes(&treasureShape, 1);
treasureShape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, false);
treasureShape->setFlag(PxShapeFlag::eTRIGGER_SHAPE, true);
The overlaps with trigger shapes are reported in SampleSubmarine through the implementation of PxSimulationEventCallback::onTrigger in the PxSampleSubmarine class, a sub-class of PxSimulationEventCallback::
void SampleSubmarine::onTrigger(PxTriggerPair* pairs, PxU32 count)
{
for(PxU32 i=0; i < count; i++)
{
// ignore pairs when shapes have been deleted
if (pairs[i].flags & (PxTriggerPairFlag::eREMOVED_SHAPE_TRIGGER |
PxTriggerPairFlag::eREMOVED_SHAPE_OTHER))
continue;
if ((&pairs[i].otherShape->getActor() == mSubmarineActor) &&
(&pairs[i].triggerShape->getActor() == gTreasureActor))
{
gTreasureFound = true;
}
}
}
The code above iterates through all pairs of overlapping shapes that involve a trigger shape. If it is found that the treasure has been touched by the submarine then the flag gTreasureFound is set true.
====================
Interactions
====================
The SDK internally creates an interaction object for each overlapping pair reported by the broad-phase. These objects are not only created for pairs of colliding rigid bodies, but also for pairs of overlapping triggers. Generally speaking users should assume that such objects are created regardless of the involved objects' types (rigid body, trigger, etc) and regardless of involved *PxFilterFlag* flags.
There is currently a limit of 65535 such interaction objects for each actor. If more than 65535 interactions involve the same actor, then the SDK outputs an error message and the extra interactions are ignored.

View File

@ -0,0 +1,533 @@
.. _RigidBodyDynamics:
------------------------
Rigid Body Dynamics
------------------------
In this chapter we cover a number of topics that are also important to understand once you are comfortable with setting up a basic rigid body simulation world.
======================
Velocity
======================
A rigid body's motion is separated into linear and angular velocity components. During simulation, PhysX will modify the velocity of an object in accordance with gravity, other applied forces and torques and as a result of various constraints, such as collisions or joints.
A body's linear and angular velocities can be read using the following methods::
PxVec3 PxRigidBody::getLinearVelocity();
PxVec3 PxRigidBody::getAngularVelocity();
A body's linear and angular velocities can be set using the following methods::
void PxRigidBody::setLinearVelocity(const PxVec3& linVel, bool autowake);
void PxRigidBody::setAngularVelocity(const PxVec3& angVel, bool autowake);
===============================
Mass Properties
===============================
A dynamic actor needs mass properties: the mass, moment of inertia, and the center of mass frame which specifies the position of the actor's center of mass and its principal inertia axes. The easiest way to calculate mass properties is to use the PxRigidBodyExt::updateMassAndInertia() helper function, which will set all three properties based on the actor's shapes and a uniform density value. Variants of this function allow combinations of per-shape densities and manual specification of some mass properties. See the reference for PxRigidBodyExt for more details.
The Wobbly Snowmen in the North Pole Sample illustrate the use of different mass properties. The snowmen act like roly-poly toys, which are usually just an empty shell with the bottom filled with some heavy material. The low centers of mass cause them to move back to an upright position after they have been tilted. They come in different flavors, depending on how the mass properties are set:
The first is basically massless. There is just a little sphere with a relatively high mass at the bottom of the Actor. This results in a quite rapid movement due to the small resulting moments of inertia. The snowman feels light.
The second uses the mass of the bottom snowball only, resulting in a bigger inertia. Later on, the center of mass is moved to the bottom of the actor. This approximation is by no means physically correct, but the resulting snowman feels a bit more filled.
The third and fourth snowman use shapes to calculate the mass. The difference is that one calculates the moments of inertia first (from the real center of mass) and then the center of mass is moved to the bottom. The other calculates the moments of inertia about the low center of mass that we pass to the calculation routine. Note how much slower the wobbling is for the second case although both have the same mass. This is because the head accounts for much more in the moment of inertia (the distance from the center of mass squared).
The last snowman's mass properties are set up manually. The sample uses rough values for the moment of inertia to create a specific desired behavior. The diagonal tensor has a low value in X, and high values in Y and Z, producing a low resistance to rotation around the X-axis and high resistance around Y and Z. As a consequence, the snowman will wobble back and forth only around the X axis.
If you have a 3x3 inertia matrix (for example, you have real-life inertia tensors for your objects) use the PxDiagonalize() function to obtain principal axes and diagonal inertia tensors to initialize PxRigidDynamic actors.
When manually setting the mass/inertia tensor of bodies, PhysX requires positive values for the mass and each principal axis of inertia. However, it is legal to provide 0s in these values. When provided with a 0 mass or inertia value, PhysX interprets this to mean infinite mass or inertia around that principal axis. This can be used to create bodies that resist all linear motion or that resist all or some angular motion. Examples of the effects that could be achieved using this approach are:
* Bodies that behave as if they were kinematic.
* Bodies whose translation behaves kinematically but whose rotation is dynamic.
* Bodies whose translation is dynamic but whose rotation is kinematic.
* Bodies which can only rotate around a specific axis.
Some examples of what could be achieved are detailed below. First, let's assume that we are creating a common structure - a windmill. The code to construct the bodies that would be part of the windmill are provided below::
PxRigidDynamic* dyn = physics.createRigidDynamic(PxTransform(PxVec3(0.f, 2.5f, 0.f)));
PxRigidActorExt::createExclusiveShape(*dyn, PxBoxGeometry(2.f, 0.2f, 0.1f), material);
PxRigidActorExt::createExclusiveShape(*dyn, PxBoxGeometry(0.2f, 2.f, 0.1f), material);
dyn->setActorFlag(PxActorFlag::eDISABLE_GRAVITY, true);
dyn->setAngularVelocity(PxVec3(0.f, 0.f, 5.f));
dyn->setAngularDamping(0.f);
PxRigidStatic* st = mPhysics.createRigidStatic(PxTransform(PxVec3(0.f, 1.5f, -1.f)));
PxRigidActorExt::createExclusiveShape(*t, PxBoxGeometry(0.5f, 1.5f, 0.8f), material);
scene.addActor(dyn);
scene.addActor(st);
The above code creates a static box frame for the windmill and a cross to represent the blades of the turbine. We turn off gravity and angular damping on the windmill blade and give it an initial angular velocity. As a result, this turbine blade will rotate at a constant angular velocity indefinitely. However, if another object collided with the turbine, our windmill would cease to function correctly because the turbine blade would be knocked out of place. There are several options to make the turbine blade stay in the correct position when other bodies interact with it. One such approach might be to make the turbine have infinite mass and inertia. In this case, any interactions with bodies would not affect the turbine at all::
dyn->setMass(0.f);
dyn->setMassSpaceInertiaTensor(PxVec3(0.f));
This example retains the previous behavior of the turbine spinning at a constant angular velocity indefinitely. However, now the body's velocities cannot be affected by any constraints because the body has infinite mass and inertia. If a body collided with the turbine blade, the collision would behave as if the turbine blade was a kinematic body.
Another alternative would be to make the turbine have infinite mass and limit its rotation to just around the body's local z-axis. This would provide the same effect as applying a revolute joint between the turbine and the static windmill frame::
dyn->setMass(0.f);
dyn->setMassSpaceInertiaTensor(PxVec3(0.f, 0.f, 10.f));
In both examples, the body's mass was set to 0, indicating that the body has infinite mass so its linear velocity cannot be changed by any constraints. However, in this example, the body's inertia is configured to permit the body's angular velocity to be affected by constraints around one principal axis or inertia. This provides a similar effect to introducing a revolute joint. The value of the inertia around the z-axis can be increased or decreased to make the turbines more/less resistive to motion.
===============================
Applying Forces and Torques
===============================
The most physics-friendly way to interact with a body is to apply a force to it. In classical mechanics, most interactions between bodies are typically solved by using forces. Because of the law:
f = m*a (force = mass * acceleration)
Forces directly control a body's acceleration, but its velocity and position only indirectly. For this reason control by force may be inconvenient if you need immediate response. The advantage of forces is that regardless of what forces you apply to the bodies in the scene, the simulation will be able to keep all the defined constraints (joints and contacts) satisfied. For example gravity works by applying a force to bodies.
Unfortunately applying large forces to articulated bodies at the resonant frequency of a system may lead to ever increasing velocities, and eventually to the failure of the solver to maintain the joint constraints. This is not unlike a real world system, where the joints would ultimately break.
The forces acting on a body are accumulated before each simulation frame, applied to the simulation, and then reset to zero in preparation for the next frame. The relevant methods of PxRigidBody and PxRigidBodyExt are listed below. Please refer to the API reference for more detail::
void PxRigidBody::addForce(const PxVec3& force, PxForceMode::Enum mode, bool autowake);
void PxRigidBody::addTorque(const PxVec3& torque, PxForceMode::Enum mode, bool autowake);
void PxRigidBodyExt::addForceAtPos(PxRigidBody& body, const PxVec3& force,
const PxVec3& pos, PxForceMode::Enum mode, bool wakeup);
void PxRigidBodyExt::addForceAtLocalPos(PxRigidBody& body, const PxVec3& force,
const PxVec3& pos, PxForceMode::Enum mode, bool wakeup);
void PxRigidBodyExt::addLocalForceAtPos(PxRigidBody& body, const PxVec3& force,
const PxVec3& pos, PxForceMode::Enum mode, bool wakeup);
void PxRigidBodyExt::addLocalForceAtLocalPos(PxRigidBody& body, const PxVec3& force,
const PxVec3& pos, PxForceMode::Enum mode, bool wakeup);
The PxForceMode member defaults to PxForceMode::eFORCE to apply simple forces. There are other possibilities. For example PxForceMode::eIMPULSE will apply an impulsive force. PxForceMode::eVELOCITY_CHANGE will do the same, but also ignore the mass of the body, effectively leading to an instantaneous velocity change. See the API documentation of PxForceMode for the other possibilities.
.. note:: The methods in PxRigidBodyExt support only the force modes eFORCE and eIMPULSE.
There are further extension functions that compute the linear and angular velocity changes that would arise in the next simulation frame if an impulsive force or impulsive torque were to be applied::
void PxRigidBodyExt::computeVelocityDeltaFromImpulse(const PxRigidBody& body,
const PxVec3& impulsiveForce, const PxVec3& impulsiveTorque, PxVec3& deltaLinearVelocity,
PxVec3& deltaAngularVelocity);
A use case for this function might be to predict an updated velocity for an object so that asset loading may be initiated in advance of the simulation frame if the body is likely to exceed a threshold velocity at the end of the frame. The impulsive force and torque are simply the force and torque that are to be applied to the body multiplied by the timestep of the simulation frame. Neglecting the effect of constraint and contact forces, the change in linear and angular velocity that are expected to arise in the next simulation frame are returned in deltaLinearVelocity and deltaAngularVelocity. The predicted linear velocity can then be computed with body.getLinearVelocity() + deltaLinearVelocity, while the predicted angular velocity can be computed with body.getAngularVelocity() + deltaAngularVelocity. If required, it is possible to immediately update the velocity of the body using body.setLinearVelocity(body.getLinearVelocity() + deltaLinearVelocity) and body.setAngularVelocity(body.getAngularVelocity() + deltaAngularVelocity).
===============================
Gravity
===============================
Gravity is such a common force in simulations that PhysX makes it particularly simple to apply. For a scene-wide gravity effect, or any other uniform force field, set the PxScene class' gravity vector using PxScene::setGravity().
The parameter is the acceleration due to gravity. In meters and seconds, this works out to have a magnitude of about 9.8 on earth, and should point downwards. The force that will be applied at the center of mass of each body in the scene is this acceleration vector times the actor's mass.
Certain special effects can require that some dynamic actors are not influenced by gravity. To specify this set the flag::
PxActor::setActorFlag(PxActorFlag::eDISABLE_GRAVITY, true);
.. note:: Be careful when changing gravity (or enabling/disabling it) during the simulation. For performance reasons the change will not wake up sleeping actors automatically. Thus it may be necessary to iterate through all actors and call PxRigidDynamic::wakeUp() manually.
An alternative to PxActorFlag::eDISABLE_GRAVITY is to use a zero gravity vector for the whole scene, then apply your own gravity force to rigid bodies, each frame. This can be used to create radial gravity fields, as demonstrated in SampleCustomGravity.
===============================
Friction and Restitution
===============================
All physical objects have at least one material, which defines the friction and restitution properties used to resolve a collision with the objects.
To create a material, call PxPhysics::createMaterial()::
PxMaterial* mMaterial;
mMaterial = mPhysics->createMaterial(0.5f, 0.5f, 0.1f); // static friction, dynamic friction,
// restitution
if(!mMaterial)
fatalError("createMaterial failed!");
Materials are owned by the PxPhysics object, and can be shared among objects in multiple scenes. The material properties of two objects involved in a collision may be combined in various ways. See the reference documentation for PxMaterial for more details.
PhysX objects whose collision geometry is a triangle mesh or a heightfield (see :ref:`RigidBodyCollisionShapes`) can have a material per triangle.
Friction uses the coulomb friction model, which is based around the concepts of 2 coefficients: the static friction coefficient and the dynamic friction coefficient (sometimes called kinetic friction). Friction resists relative lateral motion of two solid surfaces in contact. These two coefficients define a relationship between the normal force exerted by each surface on the other and the amount of friction force that is applied to resist lateral motion. Static friction defines the amount of friction that is applied between surfaces that are not moving lateral to each-other. Dynamic friction defines the amount of friction applied between surfaces that are moving relative to each-other.
When using the default patch friction model, PhysX's friction model is, by default, stronger than analytical models. To achieve results much closer to analytical models, PhysX 4.0 introduced the PxMaterialFlag::eIMPROVED_PATCH_FRICTION flag. By default, this flag is not enabled to maintain legacy behavior.
The coefficient of restitution of two colliding objects is a fractional value representing the ratio of speeds after and before an impact, taken along the line of impact. A coefficient of restitution of 1 is said to collide elastically, while a coefficient of restitution < 1 is said to be inelastic.
.. _sleeping:
===============================
Sleeping
===============================
When an actor does not move for a period of time, it is assumed that it will not move in the future either until some external force acts on it that throws it out of equilibrium. Until then it is no longer simulated in order to save resources. This state is called sleeping. You can query an actor's sleep state with the following method::
bool PxRigidDynamic::isSleeping() const;
It is however often more convenient to listen for events that the SDK sends when actors fall asleep or wake up. To receive the following events, PxActorFlag::eSEND_SLEEP_NOTIFIES must be set for the actor::
void PxSimulationEventCallback::onWake(PxActor** actors, PxU32 count) = 0;
void PxSimulationEventCallback::onSleep(PxActor** actors, PxU32 count) = 0;
See the section :ref:`callbacks` and the subsection :ref:`sleep_callbacks` for more information.
An actor goes to sleep when its kinetic energy is below a given threshold for a certain time. Basically, every dynamic rigid actor has a wake counter which gets decremented by the simulation time step when the kinetic energy of the actor is below the specified threshold. However, if the energy is above the threshold after a simulation step, the counter gets reset to a minimum default value and the whole process starts anew. Once the wake counter reaches zero, it does not get decremented any further and the actor is ready to go to sleep. Please note that a zero wake counter does not mean that the actor has to be asleep, it only indicates that it is ready to go to sleep. There are other factors that might keep an actor awake for a while longer.
The energy threshold as well as the minimum amount of time an actor will stay awake can be manipulated using the following methods::
void PxRigidDynamic::setSleepThreshold(PxReal threshold);
PxReal PxRigidDynamic::getSleepThreshold() const;
void PxRigidDynamic::setWakeCounter(PxReal wakeCounterValue);
PxReal PxRigidDynamic::getWakeCounter() const;
.. note:: For kinematic actors, special sleep rules apply. A kinematic actor is asleep unless a target pose has been set (in which case it will stay awake until the end of the next simulation step where no target pose has been set anymore). As a consequence, it is not allowed to use setWakeCounter() for kinematic actors. The wake counter of a kinematic actor is solely defined based on whether a target pose has been set.
If a dynamic rigid actor is sleeping, the following state is guaranteed:
* The wake counter is zero.
* The linear and angular velocity is zero.
* There is no force update pending.
When an actor gets inserted into a scene, it will be considered asleep if all the points above hold, else it will be treated as awake.
In general, a dynamic rigid actor is guaranteed to be awake if at least one of the following holds:
* The wake counter is positive.
* The linear or angular velocity is non-zero.
* A non-zero force or torque has been applied.
As a consequence, the following calls will wake the actor up automatically:
* PxRigidDynamic::setWakeCounter(), if the wake counter value is larger than zero.
* PxRigidBody::setLinearVelocity(), ::setAngularVelocity(), if the velocity is non-zero.
* PxRigidBody::addForce(), ::addTorque(), if the torque is non-zero.
In addition, the following calls and events wake an actor up:
* PxRigidDynamic::setKinematicTarget() in the case of a kinematic actor (because this also sets the wake counter to a positive value).
* PxRigidActor::setGlobalPose(), if the autowake parameter is set to true (default).
* Simulation gets disabled for a PxRigidActor by raising PxActorFlag::eDISABLE_SIMULATION.
* PxScene::resetFiltering().
* PxShape::setSimulationFilterData(), if the subsequent re-filtering causes the type of the shape pair to transition between suppressed, trigger and contact.
* Touch with an actor that is awake.
* A touching rigid actor gets removed from the scene (this is the default behavior but it can be specified by the user, see note further below).
* Contact with a static rigid actor is lost.
* Contact with a dynamic rigid actor is lost and this actor is awake in the next simulation step.
.. note:: When removing a rigid actor from the scene or a shape from an actor, it is possible to specify whether to wake up the objects that were touching the removed object in the previous simulation step. See the API comments in PxScene::removeActor() and PxRigidActor::detachShape() for details.
To explicitly wake up a sleeping object, or force an object to sleep, use::
void PxRigidDynamic::wakeUp();
void PxRigidDynamic::putToSleep();
.. note:: It is not allowed to use these methods for kinematic actors. The sleep state of a kinematic actor is solely defined based on whether a target pose has been set.
The API reference documents exactly which methods cause an actor to be woken up.
.. _sleep_callbacks:
++++++++++++++++++++++++++++++++++++++++++
Sleep state change events
++++++++++++++++++++++++++++++++++++++++++
As mentioned above, PhysX provides an event system that reports changes to the sleep state of dynamic rigid bodies during *PxScene::fetchResults()*::
void PxSimulationEventCallback::onWake(PxActor** actors, PxU32 count) = 0;
void PxSimulationEventCallback::onSleep(PxActor** actors, PxU32 count) = 0;
It is important to understand the correct usage of these events, and their limitations:
* A body added since the previous *fetchResults()* or *flushSimulation()* will always generate an event, even if no sleep state transition occured.
* If there have been multiple changes in a body's sleep state since the previous *fetchResults()* or *flushSimulation()*, PhysX will report only the most recent.
Sometimes it is desirable to detect transitions between awake and asleep, e.g. when keeping track of the number of awake bodies. Suppose a sleeping body *B* is woken by the application, the counter is incremented, and during the next simulation step *B* stays awake. Even though *B*'s sleep state did not change during simulation, it has changed since the previous *fetchResults()*, and so an *onWake()* event will be generated for it. If the counter is incremented again in response to this event, its value will be incorrect.
To use sleep state events to detect transitions, a record of the sleep state for objects of interest has to be kept, for example in a hash. When processing an event, this record can be used to check whether there has been a transition.
===============================
Kinematic Actors
===============================
Sometimes controlling an actor using forces or constraints is not sufficiently robust, precise or flexible. For example moving platforms or character controllers often need to manipulate an actor's position or have it exactly follow a specific path. Such a control scheme is provided by kinematic actors.
A kinematic actor is controlled using the PxRigidDynamic::setKinematicTarget() function. Each simulation step PhysX moves the actor to its target position, regardless of external forces, gravity, collision, etc. Thus one must continually call setKinematicTarget(), every time step, for each kinematic actor, to make them move along their desired paths. The movement of a kinematic actor affects dynamic actors with which it collides or to which it is constrained with a joint. The actor will appear to have infinite mass and will push regular dynamic actors out of the way.
To create a kinematic actor, simply create a regular dynamic actor then set its kinematic flag::
PxRigidBody::setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true);
Use the same function to transform a kinematic actor back to a regular dynamic actor. While you do need to provide a mass for the kinematic actor as for all dynamic actors, this mass will not actually be used for anything while the actor is in kinematic mode.
Caveats:
- It is important to understand the difference between PxRigidDynamic::setKinematicTarget() and PxRigidActor::setGlobalPose() here. While setGlobalPose() would also move the actor to the desired position, it would not make that actor properly interact with other objects. In particular, with setGlobalPose() the kinematic actor would not push away other dynamic actors in its path, instead it would go right through them. The setGlobalPose() function can still be used though, if one simply wants to teleport a kinematic actor to a new position.
- A kinematic actor can push away dynamic objects, but nothing pushes it back. As a result, a kinematic can easily squish a dynamic actor against a static actor, or against another kinematic actor. As a result, the squished dynamic object can deeply penetrate the geometry it has been pushed into.
- There is no interaction or collision between kinematic actors and static actors. However, it is possible to request contact information for these cases with the PxSceneDesc::kineKineFilteringMode and PxSceneDesc::staticKineFilteringMode.
++++++++++++++++++++++++++++++++++++++++++
Kinematic Surface Velocities
++++++++++++++++++++++++++++++++++++++++++
In addition to setting kinematic targets or the global pose of a kinematic actor, it is also possible to set a kinematic surface velocity using the method PxRigidDynamic::setKinematicSurfaceVelocity(). This method is only legal to call if the rigid body in question is set to be kinematic. This method sets a persistent velocity on the kinematic actor but flags the actor to bypass integration. This means that objects interacting with the kinematic actor through collisions will behave as if the kinematic actor is moving, although the actor's pose does not actually change. This mechanism can be used to create conveyor belts and rotating surfaces.
===============================
Active Actors
===============================
The active actors API provides an efficient way to reflect actor transform changes in a PhysX scene to an associated external object such as a render mesh.
When a scene's fetchResults() method is called an array of active *PxActor* is generated. Because only actors that have moved will be included in the list this approach is potentially much more efficient than, for example, analyzing each actor in the scene individually.
The example below shows how to use active actors to update a render object::
// update scene
scene.simulate(dt);
scene.fetchResults();
// retrieve array of actors that moved
PxU32 nbActiveActors;
PxActor** activeActors = scene.getActiveActors(nbActiveActors);
// update each render object with the new transform
for (PxU32 i=0; i < nbActiveActors; ++i)
{
MyRenderObject* renderObject = static_cast<MyRenderObject*>(activeActors[i]->userData);
renderObject->setTransform(activeActors[i]->getGlobalPose());
}
.. note::
PxSceneFlag::eENABLE_ACTIVE_ACTORS must be set on the scene for the active actors array to be generated.
.. note::
Since the target transform for kinematic rigid bodies is set by the user, kinematics can be excluded from the list by setting the flag PxSceneFlag::eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS.
===================================
Dominance
===================================
Dominance is a mechanism to enable dynamic bodies to dominate each-other. Dominance effectively imbues the dominant body in a pair with infinite mass. This is a form of local mass modification within the constraint solver and, as such, can override the mass of one of the bodies in a pair. Similar effects can be achieved through local mass modification in contact modification but dominance has the advantage of being handled automatically within the SDK so does not incur the additional memory and performance overhead of contact modification.
Each actor must be assigned a dominance group ID. This is a 5-bit value in the range [0, 31]. As such, you are restricted to at-most 32 dominance groups. By default, all bodies are placed in dominance group 0. An actor can be assigned to a dominance group using the following method on PxActor::
virtual void setDominanceGroup(PxDominanceGroup dominanceGroup) = 0;
Dominance is defined by 2 real numbers in the following struct::
struct PxDominanceGroupPair
{
PxDominanceGroupPair(PxReal a, PxReal b)
: dominance0(a), dominance1(b) {}
PxReal dominance0;
PxReal dominance1;
};
And dominance between two dominance groups can be configured using the following method on PxScene::
virtual void setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2,
const PxDominanceGroupPair& dominance) = 0;
The user can define 3 different states for a given PxDominanceGroupPair:
* 1 : 1. This indicates that both bodies have equal dominance. This is the default behavior.
* 1 : 0. This indicates that body B dominates body A.
* 0 : 1. This indicates that body A dominates body B.
Any values other than 0 and 1 are not valid in a PxDominanceGroupPair. Assigning 0 to both sides of the PxDominanceGroupPair is also invalid. These values can be considered to be scales applied to the bodies' respective inverse mass and inverse inertia. A dominance value of 0 would therefore equate to an infinite mass body.
The following example sets two actors, actorA and actorB, into different dominance groups and configures the dominance group to make actorA dominate actorB::
PxRigidDynamic* actorA = mPhysics->createRigidDynamic(PxTransform(PxIdentity));
PxRigidDynamic* actorB = mPhysics->createRigidDynamic(PxTransform(PxIdentity));
actorA->setDominanceGroup(1);
actorB->setDominanceGroup(2);
mScene->setDominanceGroupPair(1, 2, PxDominanceGroupPair(0.f, 1.f));
Dominance values will not affect joints. Local mass modification on joints must be performed using the following methods on PxJoint::
virtual void setInvMassScale0(PxReal invMassScale) = 0;
virtual void setInvMassScale1(PxReal invMassScale) = 0;
virtual void setInvInertiaScale0(PxReal invInertiaScale) = 0;
virtual void setInvInertiaScale1(PxReal invInertiaScale) = 0;
As previously mentioned, dominance does not permit values other than 0 or 1 and any dominance values are applied uniformly to both the inverse mass and inverse inertia. Joints and contacts through contact modification permit defining separate inverse mass and inverse inertia scales, which accept any values within the range [0, PX_MAX_REAL] so can be used to achieve a wider range of effects than dominance can.
Dominance can produce some very peculiar results if misused. For example, given bodies A, B and C configured in the following way:
* Body A dominates body B
* Body B dominance body C
* Body C dominates body A
In this situation, body A cannot push body C directly. However, it can push body C if it pushes body B into body C.
===============================
Solver Iterations
===============================
When the motion of a rigid body is constrained either by contacts or joints, the constraint solver comes into play. The solver satisfies the constraints on the bodies by iterating over all the constraints restricting the motion of the body a certain number of times. The more iterations, the more accurate the results become. The solver iteration count defaults to 4 position iterations and 1 velocity iteration. Those counts may be set individually for each body using the following function::
void PxRigidDynamic::setSolverIterationCounts(PxU32 minPositionIters, PxU32 minVelocityIters);
Typically it is only necessary to significantly increase these values for objects with lots of joints and a small tolerance for joint error. If you find a need to use a setting higher than 30, you may wish to reconsider the configuration of your simulation.
The solver groups contacts into friction patches; friction patches are groups of contacts which share the same materials and have similar contact normals. However, the solver permits a maximum of 32 friction patches per contact manager (pair of shapes). If more than 32 friction patches are produced, which may be due to very complex collision geometry or very large contact offsets, the solver will ignore the remaining friction patches. A warning will be issues in checked/debug builds when this happens.
===============================
Immediate Mode
===============================
In addition to simulation using a *PxScene*, *PhysX* offers a low-level simulation API called "immediate mode". This provides an API to access the low-level contact generation and constraint solver. This approach currently only supports CPU rigid bodies and reduced coordinate articulations. It does not support maximal coordinate articulations.
The immediate mode API is defined in *PxImmediateMode.h* and there are two Snippets demonstrating its usage: "SnippetImmediateMode" and "SnippetImmediateArticulation". The first one does not use articulations and shows how to use the API for rigid bodies and joints that still belong to a *PxScene*. This can be used e.g. to simulate a specific actor of a scene with a higher frequency than the rest of the scene. The second snippet is a "pure" immediate mode example where all involved actors, joints and articulations exist without the need for *PxScene*, *PxActor* or *PxArticulation* objects.
The immediate mode API provides a function to perform contact generation::
PX_C_EXPORT PX_PHYSX_CORE_API bool PxGenerateContacts(const PxGeometry* const * geom0, const PxGeometry* const * geom1, const PxTransform* pose0, const PxTransform* pose1, PxCache* contactCache, const PxU32 nbPairs, PxContactRecorder& contactRecorder, const PxReal contactDistance, const PxReal meshContactMargin, const PxReal toleranceLength, PxCacheAllocator& allocator);
This function takes a set of pairs of *PxGeometry* objects located at specific poses and performs collision detection between the pairs. If the pair of geometries collide, contacts are generated, which are reported to *contactRecorder*. In addition, information may be cached in *contactCache* to accelerate future queries between these pairs of geometries. Any memory required for this cached information will be allocated using *allocator*.
In addition, the immediate mode provides APIs for the constraint solver. These include functions to create bodies used by the solver::
PX_C_EXPORT PX_PHYSX_CORE_API void PxConstructSolverBodies(const PxRigidBodyData* inRigidData, PxSolverBodyData* outSolverBodyData, const PxU32 nbBodies, const PxVec3& gravity, const PxReal dt);
PX_C_EXPORT PX_PHYSX_CORE_API void PxConstructStaticSolverBody(const PxTransform& globalPose, PxSolverBodyData& solverBodyData);
In addition to constructing the bodies, *PxConstructSolverBodies* also integrates the provided gravitational acceleration into the bodies velocities.
The following function is optional and is used to batch constraints::
PX_C_EXPORT PX_PHYSX_CORE_API PxU32 PxBatchConstraints( const PxSolverConstraintDesc* solverConstraintDescs, const PxU32 nbConstraints, PxSolverBody* solverBodies, const PxU32 nbBodies,
PxConstraintBatchHeader* outBatchHeaders, PxSolverConstraintDesc* outOrderedConstraintDescs,
Dy::ArticulationV** articulations=NULL, const PxU32 nbArticulations=0);
Batching constraints reorders the provided constraints and produces batchHeaders, which can be used by the solver to accelerate constraint solving by grouping together independent constraints and solving them in parallel using multiple lanes in SIMD registers. This process is entirely optional and can bypassed if not desired. Note that this will change the order in which constraints are processed, which can change the outcome of the solver.
The following methods is provided to create contact constraints::
PX_C_EXPORT PX_PHYSX_CORE_API bool PxCreateContactConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxSolverContactDesc* contactDescs,
PxConstraintAllocator& allocator, const PxReal invDt, const PxReal bounceThreshold, const PxReal frictionOffsetThreshold, const PxReal correlationDistance);
This method can be provided with the contacts produced by *PxGenerateContacts* or by contacts produced by application-specific contact generation approaches.
The following methods are provided to create joint constraints::
PX_C_EXPORT PX_PHYSX_CORE_API bool PxCreateJointConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator,
const PxReal dt, const PxReal invDt);
PX_C_EXPORT PX_PHYSX_CORE_API bool PxCreateJointConstraintsWithShaders(PxConstraintBatchHeader* batchHeaders, const PxU32 nbBatchHeaders, PxConstraint** constraints, PxSolverConstraintPrepDesc* jointDescs,
PxConstraintAllocator& allocator, const PxReal dt, const PxReal invDt);
PX_C_EXPORT PX_PHYSX_CORE_API bool PxCreateJointConstraintsWithImmediateShaders(PxConstraintBatchHeader* batchHeaders, const PxU32 nbBatchHeaders, immConstraint* constraints, PxSolverConstraintPrepDesc* jointDescs,
PxConstraintAllocator& allocator, const PxReal dt, const PxReal invDt);
The methods provide a mechanism for the application to define joint rows or for the application to make use of *PhysX PxConstraint* objects, which create the constraint rows.
The following method solves the constraints::
PX_C_EXPORT PX_PHYSX_CORE_API void PxSolveConstraints(const PxConstraintBatchHeader* batchHeaders, const PxU32 nbBatchHeaders, const PxSolverConstraintDesc* solverConstraintDescs,
const PxSolverBody* solverBodies, PxVec3* linearMotionVelocity, PxVec3* angularMotionVelocity, const PxU32 nbSolverBodies, const PxU32 nbPositionIterations, const PxU32 nbVelocityIterations,
const float dt=0.0f, const float invDt=0.0f, const PxU32 nbSolverArticulations=0, Dy::ArticulationV** solverArticulations=NULL);
This method performs all required position and velocity iterations and updates the objects' delta velocities and motion velocities, which are stored in *PxSolverBody* and *linear/angularMotionVelocity* respectively.
The following method is provided to integrate the bodies' final poses and update the bodies' velocities to reflect the motion produced by the constraint solver::
PX_C_EXPORT PX_PHYSX_CORE_API void PxIntegrateSolverBodies(PxSolverBodyData* solverBodyData, PxSolverBody* solverBody, const PxVec3* linearMotionVelocity, const PxVec3* angularMotionState, const PxU32 nbBodiesToIntegrate,
const PxReal dt);
The above methods are the ones needed for simulating regular rigid bodies and joints in immediate mode. See SnippetImmediateMode for an example.
Additional functions are provided to simulate reduced coordinate articulations. First, register articulation-related solver functions with *PxRegisterImmediateArticulations*::
PX_C_EXPORT PX_PHYSX_CORE_API void PxRegisterImmediateArticulations();
This is the counterpart of *PxRegisterArticulationsReducedCoordinate* for immediate mode. You only need to call it once at the start of your program. Then create a low-level reduced coordinate articulations with the following function::
PX_C_EXPORT PX_PHYSX_CORE_API Dy::ArticulationV* PxCreateFeatherstoneArticulation(const PxFeatherstoneArticulationData& data);
Once the articulation is created, add articulation links to it with the following function::
PX_C_EXPORT PX_PHYSX_CORE_API Dy::ArticulationLinkHandle PxAddArticulationLink(Dy::ArticulationV* articulation, const PxFeatherstoneArticulationLinkData& data, bool isLastLink=false);
The number of links per articulation is currently limited to 64, just as with PxScene-level articulations. After all links have been added, the articulation is ready to be simulated.
Note that for articulations the current API is not as "immediate" as for rigid bodies, since the returned object is still a thin "retained mode" wrapper around low-level structures. This is done to make articulations easier to use: the low-level structures currently contain data for both reduced coordinate and maximal coordinate articulations, and intimate knowledge of PhysX's internals is needed to distinguish between the two. The thin wrapper makes things more accessible. On the other hand, the data is not directly owned by the user, and the following function must be called to eventually release it at the end of your program::
PX_C_EXPORT PX_PHYSX_CORE_API void PxReleaseArticulation(Dy::ArticulationV* articulation);
Meanwhile there are a number of data accessor functions available::
PX_C_EXPORT PX_PHYSX_CORE_API Dy::ArticulationV* PxGetLinkArticulation(const Dy::ArticulationLinkHandle link);
PX_C_EXPORT PX_PHYSX_CORE_API PxU32 PxGetLinkIndex(const Dy::ArticulationLinkHandle link);
PX_C_EXPORT PX_PHYSX_CORE_API bool PxGetLinkData(const Dy::ArticulationLinkHandle link, PxLinkData& data);
PX_C_EXPORT PX_PHYSX_CORE_API PxU32 PxGetAllLinkData(const Dy::ArticulationV* articulation, PxLinkData* data);
PX_C_EXPORT PX_PHYSX_CORE_API bool PxGetMutableLinkData(const Dy::ArticulationLinkHandle link, PxMutableLinkData& data);
PX_C_EXPORT PX_PHYSX_CORE_API bool PxSetMutableLinkData(Dy::ArticulationLinkHandle link, const PxMutableLinkData& data);
PX_C_EXPORT PX_PHYSX_CORE_API bool PxGetJointData(const Dy::ArticulationLinkHandle link, PxFeatherstoneArticulationJointData& data);
PX_C_EXPORT PX_PHYSX_CORE_API bool PxSetJointData(Dy::ArticulationLinkHandle link, const PxFeatherstoneArticulationJointData& data);
Some of them are here to update the data at runtime, say for articulation drives. Some of them are needed to setup the articulation data for aforementioned immediate mode functions like *PxSolveConstraints*, which have been updated in *PhysX 4.1* to take additional articulation-related parameters (but which should otherwise be used the same way as for immediate mode rigid bodies).
The only new articulation-specific functions are otherwise::
PX_C_EXPORT PX_PHYSX_CORE_API void PxComputeUnconstrainedVelocities(Dy::ArticulationV* articulation, const PxVec3& gravity, const PxReal dt);
PX_C_EXPORT PX_PHYSX_CORE_API void PxUpdateArticulationBodies(Dy::ArticulationV* articulation, const PxReal dt);
Use the first one at the start of the simulation loop to compute unconstrained velocities for each immediate mode articulations. Use the second one at the end of the simulation loop to update the articulation bodies/links after *PxIntegrateSolverBodies* has finished. Please refer to SnippetImmediateArticulation for examples.
======================
Enhanced Determinism
======================
PhysX provides limited deterministic simulation. Specifically, the results of the simulation will be identical between runs if simulating the exact same scene (same actors inserted in the same order) using the same time-stepping scheme and same PhysX release running on the same platform. The simulation behavior is not influenced by the number of worker threads that are used.
However, the results of the simulation can change if actors are inserted in a different order. In addition, the overall behavior of the simulation can change if additional actors are added or if some actors are removed from the scene. This means that the simulation of a particular collection of actors can change depending on whether other actors are present in the scene or not, irrespective of whether these actors actually interact with the collection of actors. This behavioral property is usually tolerable but there are circumstances in which it is not acceptable.
To overcome this issue, PhysX provides a flag: PxSceneFlag::eENABLE_ENHANCED_DETERMINISM, which provides additional levels of determinism. Specifically, provided the application inserts the actors in a deterministic order, with this flag raised, the simulation of an island will be identical regardless of any other islands in the scene. However, this mode sacrifices some performance to ensure this additional determinism.
=======================
Axis locking
=======================
It is possible to restrict motion along or around specific world-space axes in PhysX using PxRigidDynamicLockFlag. For example, the below code snippet demonstrates how to restrict a PxRigidDynamic to two dimensional simulation. In this case, we permit the PxRigidDynamic to rotate only around the Z-axis and to translate only along the X- and Y- axes::
PxRigidDynamic* dyn = physics.createRigidDynamic(PxTransform(PxVec3(0.f, 2.5f, 0.f)));
...
//Lock the motion
dyn->setRigidDynamicLockFlags(PxRigidDynamicLockFlag::eLOCK_LINEAR_Z | PxRigidDynamicLockFlag::eLOCK_ANGULAR_X | PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y);
It is legal to restrict movement or rotation around any combination of the 6 degrees of freedom.
.. _TemporalGaussSeidel:
=======================
Temporal Gauss-Seidel
=======================
PhysX provides a default Projected Gauss-Seidel-style (PGS) solver and an optional Temporal Gauss-Seidel (TGS) solver.
Temporal gauss-seidel offers several advantages over the PGS-style solver:
* Dramatically improved convergence
* Improved handling of high-mass ratios
* Minimizes energy introduced when correcting penetrations (depending on solver convergence)
* Improved joint drive accuracy
TGS is generally a little slower than PGS. This is partially due to the increased complexity of the constraint solver and also partially due to TGS solving friction constraints every iteration, whereas PGS solves friction constraints only in the final 3 position iterations by default.
Enabling the TGS solver requires setting PxSceneDesc::solverType to PxSolverType::eTGS. This is an immutable scene property that must be set before the scene is constructed.
The TGS solver is available when using both CPU and GPU simulation.

View File

@ -0,0 +1,73 @@
.. _RigidBodyOverview:
------------------------
Rigid Body Overview
------------------------
=========================
Introduction
=========================
This chapter will introduce the fundamentals of simulating rigid body dynamics using the NVIDIA PhysX engine.
============================
Rigid Body Object Model
============================
PhysX uses a hierarchical rigid body object/actor model, which looks like this:
.. image:: ../images/ObjectModel.png
===================== ============================= ===============================
Class Extends Functionality
===================== ============================= ===============================
*PxBase* N/A Reflection/querying object types.
*PxActor* PxBase Actor name, actor flags, dominance, clients, aggregates, query world bounds.
*PxRigidActor* PxActor Shapes and transforms.
*PxRigidBody* PxRigidActor Mass, inertia, velocities, body flags.
*PxRigidStatic* PxRigidActor Interface for static body in the scene. This kind of body has implicit infinite mass/inertia.
*PxRigidDynamic* PxRigidBody Interface for dynamic rigid body in the scene. Introduces support for kinematic targets and object sleeping.
*PxArticulationLink* PxRigidBody Interface for a dynamic rigid body link in a PxArticulation. Introduces support for querying the articulation and adjacent links.
*PxArticulation* PxBase Defines interface for a PxArticulation. Effectively a contained referencing multiple PxArticualtionLink rigid bodies.
===================== ============================= ===============================
The following diagram shows the relationship between the main types involved in the rigid body pipeline:
.. image:: ../images/RigidBodyOverview.PNG
===============================
The Simulation Loop
===============================
Now use the method PxScene::simulate() to advance the world forward in time. Here is simplified code from the samples' fixed stepper class::
mAccumulator = 0.0f;
mStepSize = 1.0f / 60.0f;
virtual bool advance(PxReal dt)
{
mAccumulator += dt;
if(mAccumulator < mStepSize)
return false;
mAccumulator -= mStepSize;
mScene->simulate(mStepSize);
return true;
}
This is called from the sample framework whenever the app is done with processing events and is starting to idle. It accumulates elapsed real time until it is greater than a sixtieth of a second, and then calls simulate(), which moves all objects in the scene forward by that interval. This is probably the simplest of very many different ways to deal with time when stepping the simulation forward.
To allow the simulation to finish and return the results, simply call::
mScene->fetchResults(true);
True indicates that the simulation should block until it is finished, so that on return the results are guaranteed to be available. When fetchResults completes, any simulation event callback functions that you defined will also be called. See the chapter :ref:`callbacks`.
It is possible to read and write from the scene during simulation. The samples take advantage of this to perform rendering work in parallel with physics. Until fetchResults() returns, the results of the current simulation step are not available. So running rendering in parallel with simulation renders the actors as they were when simulate() was called. After fetchResults() returns, all these functions will return the new, post-simulate state. See the chapter :ref:`Threading` for more details about reading and writing while the simulation is running.
For the human eye to perceive animated motion as smooth, use at least twenty discrete frames per second, with each frame corresponding to a physics time step. To have smooth, realistic simulation of more complex physical scenes, use at least fifty frames per second.
.. note:: If you are making a real-time interactive simulation, you may be tempted to take different sized time steps which correspond to the amount of real time that has elapsed since the last simulation frame. Be very careful if you do this, rather than taking constant-sized time steps: The simulation code is sensitive to both very small and large time steps, and also to too much variation between time steps. In these cases it will likely produce jittery simulation.
See :ref:`simulation_memory` for details of how memory is used in simulation.

View File

@ -0,0 +1,589 @@
.. _SceneQueries:
------------------------
Scene Queries
------------------------
=================
Introduction
=================
PhysX provides methods in PxScene to perform collision queries against actors and attached shapes in the scene. There are three types of queries: raycasts, sweeps and overlaps, and each can return either a single result, or multiple results. Broadly speaking, each query traverses a culling structure containing the scene objects, performs a precise test using the GeometryQuery functions (see :ref:`GeometryQueries`), and accumulates the results. Filtering may occur before or after precise testing.
The scene uses two different query structures, one for *PxRigidStatic* actors, and the other for *PxRigidBody* actors (*PxRigidDynamic* and *PxArticulationLink*.) The two structures may be configured to use different culling implementations depending on the desired speed/space characteristics (see :ref:`PxPruningStructureType`.)
=================
Basic queries
=================
++++++++++
Raycasts
++++++++++
A *PxScene::raycast()* query intersects a user-defined ray with the whole scene.
The simplest use case for a raycast() query is to find the closest hit along a given ray as follows::
PxScene* scene;
PxVec3 origin = ...; // [in] Ray origin
PxVec3 unitDir = ...; // [in] Normalized ray direction
PxReal maxDistance = ...; // [in] Raycast max distance
PxRaycastBuffer hit; // [out] Raycast results
// Raycast against all static & dynamic objects (no filtering)
// The main result from this call is the closest hit, stored in the 'hit.block' structure
bool status = scene->raycast(origin, unitDir, maxDistance, hit);
if (status)
applyDamage(hit.block.position, hit.block.normal);
In this code snippet a PxRaycastBuffer object is used to receive results from the raycast query.
A call to raycast() returns true if there was a hit. hit.hadBlock is also set to true if there was a hit.
The distance for raycasts has to be in the [0, inf) range.
Raycasts results include position, normal, hit distance, shape and actor, and a face index with UV coordinates for triangle meshes and heightfields. Before using query results check PxHitFlag::ePOSITION, eNORMAL, eDISTANCE, eUV flags first, as in some cases they are not set.
++++++++++++
Sweeps
++++++++++++
A *PxScene::sweep()* query is geometrically similar to a raycast(): a PxGeometry shape is swept from a specified initial pose in a direction unitDir with specified maximum length, to find the points of impacts of the geometry with scene objects. The maximum distance for sweeps has to be in the [0, inf) range, and will be clamped by to PX_MAX_SWEEP_DISTANCE, defined in file PxScene.h.
Allowed shapes are box, sphere, capsule and convex.
A PxSweepBuffer object is used to receive results from sweep() queries::
PxSweepBuffer hit; // [out] Sweep results
PxGeometry sweepShape = ...; // [in] swept shape
PxTransform initialPose = ...; // [in] initial shape pose (at distance=0)
PxVec3 sweepDirection = ...; // [in] normalized sweep direction
bool status = scene->sweep(sweepShape, initialPose, sweepDirection, sweepDistance, hit);
Sweeps results include position, normal, hit distance, shape and actor, and a face index for triangle meshes and heightfields.
+++++++++++
Overlaps
+++++++++++
*PxScene::overlap()* query searches a region enclosed by a specified shape for any overlapping objects in the scene.
The region is specified as a transformed box, sphere, capsule or convex geometry.
A PxOverlapBuffer object is used to receive results from overlap() queries::
PxOverlapBuffer hit; // [out] Overlap results
PxGeometry overlapShape = ...; // [in] shape to test for overlaps
PxTransform shapePose = ...; // [in] initial shape pose (at distance=0)
PxOverlapBuffer hit;
bool status = scene->overlap(overlapShape, shapePose, hit);
Overlaps results only include actor/shape and faceIndex since there is no single point of intersection.
===========================
Touching and blocking hits
===========================
For queries with multiple results we distinguish between *touching* and *blocking* hits. The choice of whether a hit is touching or blocking is made by the user-implemented filtering logic. Intuitively a blocking hit prevents further progress of a raycast or a sweep along its path, and a touching hit is recorded but allows the ray or sweep to continue. So a multiple-hit query will return the closest blocking hit if one exists, together with any touching hits that are closer. If there are no blocking hits, all touching hits will be returned.
See the :ref:`QueryFiltering` section for details.
================
Query modes
================
++++++++++++
Closest hit
++++++++++++
The default mode of operation for all three query types is "closest hit". The query looks for all blocking hits, picks the one with the minimum distance and reports it in the PxHitBuffer::block member.
* For overlap() queries an arbitrary blocking hit is chosen as the reported blocking hit (distance is treated as zero for all overlap() hits).
+++++++++
Any hit
+++++++++
All three query types can operate in "any hit" mode. This is a performance hint to the query system indicating that there is no need to look
for the closest hit - any hit encountered will do. This mode is most often used for boolean blocking/non-blocking queries.
Performance improvement may be a factor of 3 or more, depending on scenario.
To activate this mode use PxQueryFlag::eANY_HIT filter data flag and set it in PxQueryFilterData object, for instance::
PxQueryFilterData fd;
fd.flags |= PxQueryFlag::eANY_HIT; // note the OR with the default value
bool status = scene->raycast(origin, unitDir, maxDistance, hit,
PxHitFlags(PxHitFlag::eDEFAULT), fdAny);
+++++++++++++++
Multiple hits
+++++++++++++++
All three query types (raycast, overlap, sweep) can also report multiple hits with objects in the scene.
* To activate this mode for raycasts use the PxRaycastBuffer constructor with user provided buffer for touching hits.
* In this mode all hits default to 'touching' type and are recorded in the PxRaycastBuffer::touches array.
For instance::
PxScene* scene;
PxVec3 origin = ...; // [in] Ray origin
PxVec3 unitDir = ...; // [in] Normalized ray direction
PxReal maxDistance = ...; // [in] Raycast max distance
const PxU32 bufferSize = 256; // [in] size of 'hitBuffer'
PxRaycastHit hitBuffer[bufferSize]; // [out] User provided buffer for results
PxRaycastBuffer buf(hitBuffer, bufferSize); // [out] Blocking and touching hits stored here
// Raycast against all static & dynamic objects (no filtering)
// The main result from this call are all hits along the ray, stored in 'hitBuffer'
scene->raycast(origin, unitDir, maxDistance, buf);
for (PxU32 i = 0; i < buf.nbTouches; i++)
animateLeaves(buf.touches[i]);
The same mechanism is used for overlaps (use PxOverlapBuffer with PxOverlapHit[]) and sweeps (PxSweepBuffer with PxSweepHit[]).
++++++++++++++++++++++++++++++++
Multiple hits with blocking hit
++++++++++++++++++++++++++++++++
In the snippet for multiple hits above we only expected touching hits. If a blocking hit was encountered along with touching hits, it will be reported in PxHitBuffer::block member, and the touch buffer will contain only touching hits which are closer. This combination is useful in scenarios such as bullets going through windows (breaking them on their way) or leaves of a tree (making them rustle) until they hit a blocking object (a concrete wall)::
// same initialization code as in the snippet for multiple hits
bool hadBlockingHit = scene->raycast(origin, unitDir, maxDistance, buf);
if (hadBlockingHit)
drawWallDecal(buf.block);
for (PxU32 i = 0; i < buf.nbTouches; i++)
{
assert(buf.touches[i].distance <= buf.block.distance);
animateLeaves(buf.touches[i]);
}
* By default, hits are assumed to be touching when a touch buffer is provided, and the filter callback should return PxQueryHitType::eBLOCK
to denote that a hit is blocking. See :ref:`QueryFiltering` for details.
* For overlap() queries all touching hits will be recorded even if a blocking hit was encountered and PxQueryFlag::eNO_BLOCK flag is set.
.. _QueryFiltering:
=================
Filtering
=================
Filtering controls how shapes are excluded from scene query results and how results are reported.
All three query types support the following filtering parameters:
- a *PxQueryFilterData* structure, containing both *PxQueryFlags* and *PxFilterData*
- an optional *PxQueryFilterCallback*
+++++++++++++++++++++++++++++++++++++++++++
PxQueryFlag::eSTATIC, PxQueryFlag::eDYNAMIC
+++++++++++++++++++++++++++++++++++++++++++
*PxQueryFlag::eSTATIC* and *PxQueryFlag::eDYNAMIC* flags control whether the query should include shapes from the static and/or dynamic query structures.
This is the most efficient way to filter out all static/dynamic shapes.
For example an explosion effect that applies forces to all dynamics in a region could use a spherical *overlap* query,
and only the *PxQueryFlag::eDYNAMIC* flag to exclude all statics since forces cannot be applied to static objects.
By default both statics and dynamics are included in query results.
For instance::
PxScene* scene;
PxVec3 origin = ...; // [in] Ray origin
PxVec3 unitDir = ...; // [in] Normalized ray direction
PxReal maxDistance = ...; // [in] Raycast max distance
PxRaycastBuffer hit; // [out] Raycast results
// [in] Define filter for static objects only
PxQueryFilterData filterData(PxQueryFlag::eSTATIC);
// Raycast against static objects only
// The main result from this call is the boolean 'status'
bool status = scene->raycast(origin, unitDir, maxDistance, hit, PxHitFlag::eDEFAULT, filterData);
+++++++++++++++++++++++++++++++++++++++++++++++++
PxQueryFlag::ePREFILTER, PxQueryFlag::ePOSTFILTER
+++++++++++++++++++++++++++++++++++++++++++++++++
Scene queries are performed in three phases: broad phase, midphase and narrow phase.
* Broad phase traverses the global scene spatial partitioning structure to find the candidates for mid and narrow phases.
* midphase traverses the triangle mesh and heightfield internal culling structures, to find a smaller subset of the triangles
in a mesh reported by the broad phase.
* Narrow phase performs exact intersection tests (ray test for raycast() queries, and exact sweep shape tests
or overlap tests for sweep() and overlap() queries).
To implement custom filtering in queries, set the *PxQueryFlag::ePREFILTER* and/or *PxQueryFlag::ePOSTFILTER* flags and subclass *PxQueryFilterCallback* with the required filtering logic.
* Pre-filtering happens before midphase and narrow phase and allows shapes to be efficiently discarded before the potentially
expensive exact collision test. These tests are more expensive for triangle meshes, heightfields, convexes and most sweeps than raycast
and overlap tests involving only simple shapes (such as spheres, capsules and boxes.)
* Post-filtering happens after the narrow phase test and can therefore use the results of the test (such as PxRaycastHit.position)
to determine whether a hit should be discarded or not. These results can be accessed via the *hit* input argument to the post-filtering callback (PxQueryFilterCallback::postFilter). Use e.g. static_cast<PxRaycastHit&>(hit), access data specific to a raycast query, and similarly for overlaps (PxOverlapHit) and sweeps (PxSweepHit.)
The implementation of a filtering callback returns a *PxQueryHitType* result.
* *eNONE* indicates that the hit should be discarded.
* *eBLOCK* indicates that the hit is blocking.
* *eTOUCH* indicates that the hit is touching.
Whenever a raycast(), sweep() or overlap() query was called with non-zero PxHitCallback::nbTouches and PxHitCallback::touches parameters, eTOUCH type hits that are no further (touchDistance <= blockDistance) than the closest eBLOCK type hit, will be reported. For example, to record all hits from a raycast query, always return eTOUCH.
.. note:: Returning eTOUCH from a filter callback requires the hit buffer query parameter to have a non-zero ::touches array, otherwise PhysX will generate an error in checked builds and discard any touching hits.
.. note:: eBLOCK should not be returned from user filters for overlap(). Doing so will result in undefined behavior, and a warning will be issued. If the PxQueryFlag::eNO_BLOCK flag is set, the eBLOCK will instead be automatically converted to an eTOUCH and the warning suppressed.
+++++++++++++++++++++
PxQueryFlag::eANY_HIT
+++++++++++++++++++++
Use this flag to force the query to report the first encountered hit (which may not be the closest) as a blocking hit.
Performance may be more than three times faster, depending on the scenario. Best gains can be expected for long raycasts/sweeps
with a nearby intersecting object, or overlaps with multiple intersecting objects.
* Also see PxHitFlag::eMESH_ANY
++++++++++++++++++++++
PxQueryFlag::eNO_BLOCK
++++++++++++++++++++++
Use this flag when you want to override the eBLOCK value returned from filters to eTOUCH or in cases when no blocking hits are
expected (in this case this flag serves as a performance hint.) All hits will then be reported as touching regardless of the filter callback return value.
The hit callback/buffer object provided to the query is required to have a non-zero
*PxHitBuffer::touches* buffer when this flag is used. Significant performance gains should only be expected for scenarios where the touching hit buffer overflows.
.. note:: this flag overrides the return value from pre and post-filter functions, so hits that were previously returned as blocking will instead be returned as touching.
+++++++++++++++++++++++++++++++++++++
PxFilterData fixed function filtering
+++++++++++++++++++++++++++++++++++++
A fast, fixed-function filter is provided by *PxFilterData*, a 4*32-bit bitmask used by the built-in filtering equation.
Each shape has a bitmask (set via PxShape::setQueryFilterData()), and the query also has a bitmask.
The query data is used differently by batched and unbatched queries (see below for batched queries). For unbatched queries, the following rules are applied:
* If the query's bitmask is all zeroes, custom filtering and intersection testing proceed as normal.
* Otherwise, if the bitwise-AND value of the query's bitmask and the shape's bitmask is zero, the shape is skipped
Or in other words::
PxU32 keep = (query.word0 & object.word0)
| (query.word1 & object.word1)
| (query.word2 & object.word2)
| (query.word3 & object.word3);
This hardcoded equation can provide simple filtering while avoiding the function call overhead of the filtering callback. For example, to emulate the behavior of PhysX 2 active groups, define the groups as follows::
enum ActiveGroup
{
GROUP1 = (1<<0),
GROUP2 = (1<<1),
GROUP3 = (1<<2),
GROUP4 = (1<<3),
...
};
When shapes are created, they can be assigned to the a group, for example GROUP1::
PxShape* shape; // Previously created shape
PxFilterData filterData;
filterData.word0 = GROUP1;
shape->setQueryFilterData(filterData);
Or to multiple groups, for example GROUP1 and GROUP3::
PxShape* shape; // Previously created shape
PxFilterData filterData;
filterData.word0 = GROUP1|GROUP3;
shape->setQueryFilterData(filterData);
When performing a scene query, select which groups are active for the query - for example GROUP2 and GROUP3 - as follows::
PxScene* scene;
PxVec3 origin = ...; // [in] Ray origin
PxVec3 unitDir = ...; // [in] Normalized ray direction
PxReal maxDistance = ...; // [in] Raycast max distance
PxRaycastBuffer hit; // [out] Raycast results
// [in] Define what parts of PxRaycastHit we're interested in
const PxHitFlags outputFlags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL;
// [in] Raycast against GROUP2 and GROUP3
PxQueryFilterData filterData = PxQueryFilterData();
filterData.data.word0 = GROUP2|GROUP3;
bool status = scene->raycast(origin, unitDir, maxDistance, hit, outputFlags, filterData);
================================================
User defined hit callbacks for unbounded results
================================================
Queries can sometimes return a very large number of results (for example, queries with very large objects or in areas with high object density), and
it can be prohibitively expensive to reserve a sufficiently large memory buffer. The classes PxRaycastCallback, PxSweepCallback and PxOverlapCallback
provide efficient callback based solutions for such scenarios. For instance a *raycast* query with a PxRaycastCallback callback will return all touch hits
via multiple virtual PxHitCallback::processTouches() callbacks::
struct UserCallback : PxRaycastCallback
{
UserData data;
virtual PxAgain processTouches(const PxRaycastHit* buffer, PxU32 nbHits)
// This callback can be issued multiple times and can be used
// to process an unbounded number of touching hits.
// Each reported touching hit in buffer is guaranteed to be closer than
// the final block hit after the query has fully executed.
{
for (PxU32 i = 0; i < nbHits; i++)
animateLeaves(buffer[i], data);
}
virtual void finalizeQuery()
{
drawWallDecal(this->block, data);
}
};
PxScene* scene;
PxVec3 origin = ...; // [in] Ray origin
PxVec3 unitDir = ...; // [in] Normalized ray direction
PxReal maxDistance = ...; // [in] Raycast max distance
UserCallback cb; cb.data = ...;
scene->raycast(origin, unitDir, maxDistance, cb); // see UserCallback::processTouches
In this code snippet the raycast() query will potentially invoke processTouches multiple times, with all touching hits already
clipped to the globally nearest blocking hit.
* Note that the query can be up to twice as expensive in case all eTOUCH results do not fit in the provided touches buffer
and a blocking hit was also found.
* Also see PxQueryFlag::eNO_BLOCK
.. _Batched Queries:
=================
Batched queries
=================
PhysX supports batching of scene queries via the *PxBatchQuery* interface.
Using this API may simplify multi-threaded implementations.
The batched query feature has been deprecated in PhysX version 3.4.
* *PxBatchQuery* interface facilitates batching and execution of multiple queries together. PxBatchQuery buffers raycast, overlap and sweep queries until PxBatchQuery::execute() is called.
* Use PxScene::createBatchQuery(const PxBatchQueryDesc& desc) to create a PxBatchQuery object.
* The hardcoded filtering equation is not used for batched queries. Instead it is replaced with two filter shaders, respectively running before (*PxBatchQueryPreFilterShader*) and after (*PxBatchQueryPostFilterShader*) the exact per-shape collision test. See *PxBatchQueryDesc::preFilterShader* and *PxBatchQueryDesc::postFilterShader*.
* BatchQueryFilterData::filterShaderData will be copied and passed to the filter shader via the constantBlock parameter.
* Results are written to user-defined buffers *PxBatchQueryMemory* in *PxBatchQueryDesc*,
in the same order queries were queued in a *PxBatchQuery* object.
* The results and hits buffers for the each query type used (raycast, overlap, sweep) are specified separately.
* These buffers can be changed before each batch query execute call.
The SDK will produce a warning for batched queries with NULL results or hits buffers for the corresponding query type
(raycast, overlap or sweep).
.. _single_object_caching:
=======================
Single Object Caching
=======================
A special case mechanism for accelerating scene queries is single-object caching, using *PxQueryCache*.
* This cache can provide additional speedups and memory savings for *raycast* and *sweep* queries in any operation mode.
* The cache object defines which shape should be tested first. For queries with high temporal
coherence, this can provide significant performance gains. A good strategy to capture that coherence is simply to fill the cache
object of a given query with the eBLOCK result (last blocking shape) from the previous frame.
* Note that it is likely incorrect to use a past touching hit (recorded with eTOUCH flag) for caching since it will be interpreted
as blocking and override any filtering.
For example there is a good chance that an AI visibility query will return the same line-of-sight blocking shape for several frames.
Using a *raycast* query with a properly filled *PxQueryCache* object will allow PhysX to test a single shape - before traversing
the internal spatial partitioning structures, and in case of a "cache hit" the traversal can be bypassed entirely. For instance::
PxScene* scene;
PxVec3 origin = ...; // [in] Ray origin
PxVec3 unitDir = ...; // [in] Normalized ray direction
PxReal maxDistance = ...; // [in] Raycast max distance
PxRaycastBuffer hit; // [out] Raycast results
// Per-raycast persistent cache, valid from one frame to the next
static PxQueryCache persistentCache;
// Define cache for current frame:
// - if there was a hit in the previous frame, use the cache.
// - otherwise do not (PhysX requires given cache has a valid shape pointer)
const PxQueryCache* cache = persistentCache.shape ? &persistentCache : NULL;
// Perform a raycast query using the cache
const bool status = scene->raycast(origin, unitDir, maxDistance, hit,
PxHitFlags(PxHitFlag::eDEFAULT),
PxQueryFilterData(), NULL, cache);
if(status)
{
// We hit a shape. Cache it for next frame.
persistentCache.shape = hit.block.shape;
persistentCache.faceIndex = hit.block.faceIndex;
}
else
{
// We did not hit anything. Reset the cache for next frame.
persistentCache = PxQueryCache();
}
Caching can also be useful in queries looking for the closest blocking hit or when using the eANY_HIT flag.
In this case, testing the previously closest object first can allow PhysX to shorten the query distance very early,
leading to fewer total narrow phase collision tests and early out from the traversal.
.. note:: PhysX does not detect stale pointers, so the application is responsible for cached object validity when shapes are deleted.
.. note:: Overlaps do not support single hit blocking caches.
.. _PxPruningStructureType:
====================================
PxPruningStructureType
====================================
PhysX SDK offers different pruning structures which are used to accelerate the scene queries. This paragraph describes the differences between them.
++++++++++++
Generalities
++++++++++++
The Scene Query system uses two different acceleration structures, a hierarchical grid and an AABB tree.
The grid builds quickly, in O(n) time, with queries executing in between O(1) and O(N) time depending on how uniformly the objects are
distributed in space, with pathological worst case performance of O(N) when all objects are clustered in the same grid cell.
The tree builds in O(n log(n)) time, but queries with a single result typically run in O(log(n)) time. Queries returning multiple results
will traverse more of the tree, the worst case being a query returning all of the objects in the scene in O(n) time.
The tree is vulnerable to degeneration when the same topology is maintained too long as object positions change,
and in pathological cases query performance may degrade to O(n) time.
Acceleration structures must be continually modified in accordance with objects being added or removed, or object AABB updates
due to changes in position or geometry. To minimize the cost, modifications are deferred for as long as possible.
Thus adding or removing objects or updating AABBs occurs in amortized constant time, with the cost of modifications deferred
until the changes 'commit'. This happens on the next subsequent query or the next fetchResults() or the next fetchQueries().
To force an immediate commit, call the PxScene::flushQueryUpdates() function.
The exact details of the commit process depend on the values of staticStructure and dynamicStructure specified in PxSceneDesc.
To avoid automatic resizing triggered by insertions into internal scene query data structures, reserve the space in advance.
See *PxSceneDesc::maxNbStaticShapes* and *PxSceneDesc::maxNbDynamicShapes*.
+++++++++++++++++++++++++++++++++++++
PxPruningStructureType::eNONE
+++++++++++++++++++++++++++++++++++++
The acceleration structure is similar to a hierarchical grid. Committing changes requires a full rebuild. This is a good choice if you expect
to rarely or never update the objects in this structure.
+++++++++++++++++++++++++++++++++++++++++++++++++
PxPruningStructureType::eSTATIC_AABB_TREE
+++++++++++++++++++++++++++++++++++++++++++++++++
The acceleration structure is a tree. Committing changes requires a full rebuild. It is not generally recommended, but can be a good choice
for staticStructure if the static actors in your scene are created on initialization, and not modified thereafter.
If you frequently add or remove static geometry, the default eDYNAMIC_AABB_TREE setting is usually a better choice, although it has a higher
memory footprint than that of eSTATIC_AABB_TREE.
++++++++++++++++++++++++++++++++++++++++++++++++++
PxPruningStructureType::eDYNAMIC_AABB_TREE
++++++++++++++++++++++++++++++++++++++++++++++++++
In this case, both the tree and the grid are used, and each query searches both the tree and the grid.
The tree is initially built by the first commit. Once a tree is built, committing changes proceeds as follows::
* the tree is refitted in accordance with updates and removals of object it contains.
* added objects are inserted into the grid. Such additions, or removals of objects currently in the grid, or changes to AABBs
of objects in the grid, cause it to be rebuilt.
In addition, a new tree is incrementally built during fetchResults(), over a number of frames controlled by PxScene's
dynamicTreeRebuildRateHint attribute. When the build starts, it includes all of the objects in the current tree and grid.
When it finishes, some frames later, the new tree is refitted in accordance with any AABB changes or removals since the build started,
and then replaces the current tree. Any objects that were added since the start of the build remain in the grid.
To force a full immediate rebuild, call PxScene::forceDynamicTreeRebuild(). This can be useful in cases such as the following:
* a slow rebuilt rate is typically desirable, but occasionally a large number of object additions creates high occupancy in the grid,
especially if the additions are localized so as to put pressure on just a few of the grid cells.
* you are moving many objects across large distances, since refitting may significantly degrade the quality of the current tree
====================================
PxSceneQueryUpdateMode
====================================
It is possible to define what scene query related work is done druing PxScene::fetchResults.
By default fetchResults will sync changed bounds during simulation and update the scene query bounds in pruners, this work is mandatory.
Other work can be optional, based on the PxSceneQueryUpdateMode:
* eCOMMIT_ENABLED_BUILD_ENABLED does allow to execute the new AABB tree build step during fetchResults, additionally the pruner commit is called where any changes are applied. During commit PhysX refits the dynamic scene query tree and if a new tree was built and the build finished the tree is swapped with current AABB tree.
* eCOMMIT_DISABLED_BUILD_ENABLED does allow to execute the new AABB tree build step during fetchResults. Pruner commit is not called, this means that refit will then occur during the first scene query following fetchResults, or may be forced by the method PxScene::flushSceneQueryUpdates().
* eCOMMIT_DISABLED_BUILD_DISABLED no further scene query work is executed. The scene queries update needs to be called manually, see PxScene::sceneQueriesUpdate. It is recommended to call PxScene::sceneQueriesUpdate right after fetchResults as the pruning structures are not updated.
==================
PxPruningStructure
==================
Provides access to precomputed pruning structure used to accelerate scene queries against newly added actors.
A pruning structure can be provided to PxScene::addActors. The actors scene query shapes will then be directly merged into the scenes AABB tree, without the need of an AABB tree recompute::
// Create pruning structure from given actors.
PxPruningStructure* ps = PxPhysics::createPruningStructure(&actors[0], (PxU32)actors.size());
// Add actors into a scene together with the precomputed pruning structure.
PxScene::addActors(*ps);
ps->release();
A PxPruningStructure object can be serialized into a collection together with its actors.
For usage of PxPruningStructure please refer to the snippet SnippetPrunerSerialization.
A typical use case for PxPruningStructure is a large world scenario where blocks of closely positioned actors get streamed in.
++++++++++++++++++++++++
Merge process
++++++++++++++++++++++++
The merge process into the scene query acceleration structure differs based on *PxPruningStructureType*:
* eSTATIC_AABB_TREE - the pruning structure is merged directly into scene's AABBtree. This might unbalance the tree and it is recommended to recompute the static tree at some point.
* eDYNAMIC_AABB_TREE - the pruning structure is merged into a temporary pruning structures until the scene's new optimized AABB tree is computed.
==================
PxBVHStructure
==================
Modifies the default behavior of scene queries. By default scene queries are shape centric, which means each shape is represented as an object in a scene query pruner. PxBVHStructure does tell the SDK to set actor centric behavior for that given actor.
How does that work. If PxBVHStructure is provided during PxScene::addActor, that actors bounds are stored in a pruning structure rather then each shape contained in the actor. When a scene query against such an actor is done it does first query the actors bounds and then makes a local query against the shapes using the provided BVH structure data (note that the PxBVHStructure data are copied to the SDK and are not required during runtime).
++++++++++++++++++++++++++++++++++++++++++++++++
How to create PxBVHStructure
++++++++++++++++++++++++++++++++++++++++++++++++
PxBVHStructure can be cooked through our PxCooking library. It takes the provided PxBounds3 and computes a BVH. This is stored within the PxBVHStructure and be used later.
Example::
// get the bounds from the actor, this can be done through a helper function in PhysX extensions
PxU32 numBounds = 0;
PxBounds3* bounds = PxRigidActorExt::getRigidActorShapeLocalBoundsList(*body, numBounds);
// setup the PxBVHStructureDesc, it does contain only the PxBounds3 data
PxBVHStructureDesc bvhDesc;
bvhDesc.bounds.count = numBounds;
bvhDesc.bounds.data = bounds;
bvhDesc.bounds.stride = sizeof(PxBounds3);
// cook the bvh structure
PxBVHStructure* bvh = gCooking->createBVHStructure(bvhDesc, gPhysics->getPhysicsInsertionCallback());
// release the memory allocated within extensions, the bounds are not required anymore
gAllocator.deallocate(bounds);
// add the actor to the scene and provide the bvh structure
gScene->addActor(*body, bvh);
// bvh can be released at this point, the precomputed BVH structure was copied to the SDK pruners.
bvh->release();
For usage of PxBVHStructure please refer to the snippet SnippetPxBVHStructure.

View File

@ -0,0 +1,867 @@
.. _serialization:
---------------------------------------------
Serialization
---------------------------------------------
====================================================
Introduction
====================================================
PhysX 3 features two approaches to serialization:
* API-level serialization to RepX (an XML format)
* Binary serialization
API-level serialization uses a human readable XML format - RepX - that directly corresponds to the PhysX API. It is therefore suitable for manual inspection and modification for debugging purposes. It offers platform independence and further supports loading data that was serialized with a previous PhysX SDK version. API-level serialization is not expected to be used in performance critical situations.
The binary serialization approach on the other hand supports instantiation of PhysX objects directly from memory without copying data. This in-place deserialization method is well suited for performance critical real time situations. However, this approach is also less flexible as the binary format is specific to a given platform and PhysX SDK version. PhysX provides functionality to convert binary serialized data from authoring platforms to run-time platforms to ease the asset management.
.. note:: *cooking* also generates a binary output stream. The primary purpose of cooking, however, is to translate from a user format to a format suitable for the SDK runtime, and so it is not considered a serialization mechanism. Loading a cooked mesh from a stream involves allocation and endian conversion. As a consequence, it is much less efficient than PhysX' binary serialization mechanism. See :ref:`RigidBodyCollisionShapes` for more details about cooking.
The following documentation will discuss how to use both serialization approaches. It will show how to build collections of PhysX objects and how these collections are serialized and deserialized. Further it will show how dependencies to other PhysX objects or application side objects can be re-established when deserializing.
PhysX also supports extending serialization to custom types, such as specialized joints. This is described in more detail in Section :ref:`extendedSerialization`.
====================================================
First Code
====================================================
The following code creates and serializes a rigid dynamic using both RepX and binary formats::
// Create a material, a shape and a rigid dynamic
PxSphereGeometry geometry(1.0f);
PxMaterial* material = PxGetPhysics().createMaterial(0.0f, 0.0f, 0.0f);
PxShape* shape = PxGetPhysics().createShape(geometry, *material);
PxTransform t = PxTransform(PxIdentity);
PxRigidDynamic* dynamic = PxCreateDynamic(PxGetPhysics(), t, geometry, *material, 1.0f);
PxSerializationRegistry* registry = PxSerialization::createSerializationRegistry(PxGetPhysics());
// Create a collection and all objects for serialization
PxCollection* collection = PxCreateCollection();
collection->add(*dynamic);
PxSerialization::complete(*collection, *registry);
// Serialize either to binary or RepX
PxDefaultFileOutputStream outStream("serialized.dat");
// Binary
PxSerialization::serializeCollectionToBinary(outStream, *collection, *registry);
//~Binary
// RepX
PxSerialization::serializeCollectionToXml(outStream, *collection, *registry);
//~RepX
Most operations related to serialization require an instance of *PxSerializationRegistry*, which provides information on how to serialize PhysX types. In order to serialize a PhysX object, it needs to be added to a *PxCollection*. If an object has dependencies on other PhysX objects, they need to be serialized as well. *PxSerialization::complete* adds all the required objects to the collection.
The following code deserializes the rigid dynamic and adds it to a scene for simulation::
PxSerializationRegistry* registry = PxSerialization::createSerializationRegistry(PxGetPhysics());
// Binary
// Open file and get file size
FILE* fp = fopen("serialized.dat", "rb");
fseek(fp, 0, SEEK_END);
unsigned fileSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
// Allocate aligned memory, load data and deserialize
void* memory = malloc(fileSize+PX_SERIAL_FILE_ALIGN);
void* memory128 = (void*)((size_t(memory) + PX_SERIAL_FILE_ALIGN)&~(PX_SERIAL_FILE_ALIGN-1));
fread(memory128, 1, fileSize, fp);
fclose(fp);
PxCollection* collection = PxSerialization::createCollectionFromBinary(memory128, *registry);
//~Binary
// RepX
// Load file and deserialize collection - needs cooking library
PxDefaultFileInputData inputData("serialized.dat");
PxCollection* collection = PxSerialization::createCollectionFromXml(inputData, *cooking,
*registry);
//~RepX
scene->addCollection(*collection);
When deserializing a binary serialized collection, the data first needs to be copied to a memory block that is aligned to 128 bytes. The memory block may not be deallocated before the objects have been released: it needs to persist for the entire lifetime of the objects. This does not apply to RepX deserialization, as the memory for the corresponding PhysX objects is allocated within PhysX. Finally the objects of the resulting collection can be added to the scene with *PxScene::addCollection*.
====================================================
In-depth Discussion
====================================================
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Collections
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
The serialization system makes use of a class *PxCollection*, which manages references to objects deriving from *PxBase*. Each collection represents a set of objects. Collections maintain a mapping between IDs of type *PxSerialObjectId* and objects in the collection. IDs may be defined by the application. One caveat here is that the IDs must be unique within a collection, but do not have to be unique across different collections. If the latter is required by the application, it is the application's responsibility to ensure it.
Here is an example of how to iterate over a collection, for instance to ensure that the objects intended for serialization have all been added to the collection. When doing so PhysX' dynamic typing mechanism can be used to classify the objects::
PxCollection* collection;
PxU32 size = collection->getNbObjects();
for(PxU32 i=0; i<size; i++)
{
PxBase* object = collection->getObject(i);
if(!object->is<PxActor>())
continue;
switch((PxConcreteType)object->getConcreteType())
{
case PxConcreteType::eRIGID_DYNAMIC:
...
}
}
.. note:: In order to simplify releasing object within a collection, PhysXExtensions contains a function to remove and release all objects from a collection: *PxCollectionExt::releaseObjects*.
.. note:: Releasing an object within a collection invalidates the mapping from indices to objects.
A collection is said to be *complete* if no contained objects depend on an object outside of the collection. For example, an actor, a shape with a box geometry, and the material of the shape would together form a complete collection. The same collection without the material would be incomplete.
.. figure:: ../images/Serialization_Complete.png
:scale: 100
:align: center
Figure 1: Left: Complete Collection, Right: Incomplete Collection
For a formal definition please refer to :ref:`Complete`.
Both complete and incomplete collections can be serialized, but when deserializing an incomplete collection, references to objects which were not serialized will need to be resolved. The following two sections describe how PhysX collections can be serialized and deserialized using the binary format or RepX. The first section shows how to deal with complete collections, and the second section shows how to deal with incomplete collections.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Serializing Complete Collections
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
This code snippet shows how to prepare a collection of PhysX objects for serialization (e.g. an actor, its shapes, and the materials and meshes they reference.)::
PxPhysics* physics; // The physics SDK object
PxRigidDynamic* dynamic = PxCreateDynamic(...); // Create a rigid dynamic
//Create a serialization registry
PxSerializationRegistry* registry = PxSerialization::createSerializationRegistry(*physics);
PxCollection* collection = PxCreateCollection(); // Create a collection
collection->add(*dynamic); // Add it to the collection
PxSerialization::complete(*collection, *registry); // Adds all objects required to
// recreate the dynamic after
// deserialization
Instead of using *PxSerialization::complete* it is possible to manually add the objects required for serialization. All objects the *PxRigidDynamic* references would need to be added and then all objects referenced by the newly added objects would need to be added as well and so forth. See definitions: :ref:`Requires`, :ref:`Complete`.
By default *PxSerialization::complete* follows references from joints to their actors, but not from actors to their joints. The *followJoint* parameter can be used to change the behavior of *PxSerialization::complete* to add the joints attached to each actor. This will cause entire actor-joint chains to be added to the collection.
When all the necessary objects have been added to a collection, create an implementation of the PxOutputStream interface, then serialize the collection::
PxColletion* collection; // Complete collection without orphans
PxSerializationRegistry* registry; // Registry for serializable types
PxOutputStream& outStream = ...; // Implemented by the application
// Serialize
// Binary
PxSerialization::serializeCollectionToBinary(outStream, *collection, *registry);
//~Binary
// RepX
PxSerialization::serializeCollectionToXml(outStream, *collection, *registry);
//~RepX
// Collection and registry can be released if they are no longer required.
// Note that releasing the collection will not release the contained objects!
collection->release();
registry->release();
.. note:: Serialization of objects in a scene that is simultaneously being simulated is not supported and leads to undefined behavior.
The following code shows how to deserialize a collection from a memory block or XML::
PxSerializationRegistry* registry; // Registry for serializable types
PxCooking* cooking; // Cooking library needed for
// instantiating objects by RepX
// Deserialize
// Binary
void* memory128 = ...; // A 128-byte aligned buffer previously
// loaded from disk by the user
PxCollection* collection = PxSerialization::createCollectionFromBinary(memory128, *registry);
//~Binary
// RepX
PxInputData& inputData = ...; // Implemented by the application
PxCollection* collection = PxSerialization::createCollectionFromXml(inputData, *cooking,
*registry);
//~RepX
To add all the objects to the scene and release the collection and registry::
PxScene* scene; // The scene object
scene->addCollection(*collection);
collection->release();
registry->release();
See :ref:`Serializable` for the exact set of conditions a collection must satisfy in order to be serialized. These conditions can be checked with *PxSerialization::isSerializable(...)*.
.. _SerializingPartialObjectgraphs:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Serializing Incomplete Collections
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Another common use case is where a collection of actors and joints - say, a rag doll - will be deserialized multiple times, with each instance sharing the same materials and meshes. To achieve this, serialize two collections:
* a collection A of the materials and meshes that will be deserialized just once
* a collection B of actors and joints which will be copied and deserialized multiple times
Collection B is *incomplete*, since it contains references to objects in A. When serializing B, the serialized format will remember each reference to an object in A using that object's ID (if it doesn't have an ID, then serialization will fail.) As long as an object of the right type with a matching ID is supplied when deserializing collection B, the reference can be resolved. Although collection B is incomplete, it is also said to be *complete relative to* collection A. For a formal definition of complete please refer to :ref:`Complete`.
.. figure:: ../images/Serialization_Dependency.png
:scale: 100
:align: center
Figure 2: Left: Collection *A* with Sharable Objects, Right: Collection *B* depending on *A*
Concretely, to serialize and deserialize an incomplete collection:
* At serialization time, provide IDs for all objects in collection A that are referenced by objects in collection B.
* When deserializing, provide a collection with matching IDs for all the objects in A that were referenced by objects in B.
Here are examples of how the application can provide identities (*PxSerialObjectId*) to express requirements of one collection to another. This can be done explicitly when adding the object with::
PxCollection* collection;
PxTriangleMesh* triMesh;
PxSerialObjectId triMeshId = 1; // PX_SERIAL_OBJECT_ID_INVALID
// is a reserved value
collection->add(*triMesh, triMeshId);
Or set the ID after adding the object::
collection->add(*triMesh);
collection->addId(*triMesh, triMeshId);
There is a helper function to generate IDs for all objects in a collection that do not have IDs yet::
PxSerialObjectId baseId = 1; // PX_SERIAL_OBJECT_ID_INVALID is
// a reserved value
PxSerialization::createSerialObjectIds(*collection, baseId); // Assigns incremental ID values
// to the collection objects
Already used ID values will be skipped by *createSerialObjectIds*, as well as objects that already have IDs.
After providing correct IDs, all required objects have been added to the collection to be serialized, but without adding the objects that are intended to be referenced. The *complete* function in *PxSerialization* supports completing a collection relative to another collection::
PxSerializationRegistry* registry; // Registry for serializable types
PxCollection* collectionB; // Collection to be completed
PxCollection* collectionA; // The collection, collectionB
// will depend on
PxSerialization::complete(*collectionB, *registry, collectionA); // Completes collectionB, but
// ignores objects in collectionA
// (and also their requirements)
Serialization example::
PxConvexMesh** convexes; // An array of mNbConvexes convexes
PxRigidDynamic** actors; // An array of mNbConvexes actors referencing the convexes
PxSerializationRegistry* registry; // Registry for serializable types
PxOutputStream& convexStream; // Output stream for the convex collection
PxOutputStream& actorStream; // Output stream for the actor collection
PxCollection* convexCollection = PxCreateCollection();
PxCollection* actorCollection = PxCreateCollection();
// Add convexes to collection
for(PxU32 i=0;i<mNbConvexes;i++)
convexCollection->add(*convexes[i]);
// Create IDs for the convexes, starting with 1
PxSerialization::createSerialObjectIds(*convexCollection, PxSerialObjectId(1));
// Serialize the convexes along with their IDs
// Binary
PxSerialization::serializeCollectionToBinary(convexStream, *convexCollection, *registry);
//~Binary
// RepX
PxSerialization::serializeCollectionToXml(convexStream, *convexCollection, *registry);
//~RepX
// Add actors to other collection
for(PxU32 i=0;i<mNbActors;i++)
actorCollection->add(*actors[i]);
// Add all required objects except the convexes
PxSerialization::complete(*actorCollection, *registry, convexCollection);
// Serialize the actors with references to convexCollection
// Binary
PxSerialization::serializeCollectionToBinary(actorStream, *actorCollection, *registry,
convexCollection);
//~Binary
// RepX
PxSerialization::serializeCollectionToXml(actorStream, *actorCollection, *registry, NULL,
convexCollection);
//~RepX
// Release collections and registry
convexCollection->release();
actorCollection->release();
registry->release();
Deserialization example::
PxPhysics* physics; // The physics SDK object
PxSerializationRegistry* registry // Registry for serializable types
PxCooking* cooking; // Cooking lib needed for instantiating objects (RepX)
PxScene* scene; // The scene into which the objects will be inserted
// Deserialize convexes along with their IDs (no external dependencies)
// Binary
void* convexMemory128; // Aligned memory containing serialized convexes
PxCollection* convexCollection =
PxSerialization::createCollectionFromBinary(convexMemory128, *registry, NULL);
//~Binary
// RepX
PxInputData& convexInputData = ...; // Implemented by the application
PxCollection* convexCollection =
PxSerialization::createCollectionFromXml(convexInputData, *cooking, *registry, NULL);
//~RepX
// Deserialize actors referencing the convexCollection
// Binary
void* actorMemory128; // Aligned memory containing serialized actors
PxCollection* actorCollection =
PxSerialization::createCollectionFromBinary(actorMemory128, *registry, convexCollection);
//~Binary
// RepX
PxInputData& actorInputData = ...; // Implemented by the application
PxCollection* actorCollection =
PxSerialization::createCollectionFromXml(actorInputData, *cooking, *registry,
convexCollection);
//~RepX
// Release convex collection
convexCollection->release();
// Add actors to scene and release collection and registry
scene->addCollection(*actorCollection);
actorCollection->release();
registry->release();
The next example shows how to deal with situations where the serialized objects require objects that are not serialized and deserialized but created by other means::
PxSerializationRegistry* registry; // Registry for serializable types
PxMaterial** materials; // Created procedurally by application
PxRigidDynamic** actors; // An array of mNbConvexes actors referencing the convexes
PxOutputStream& actorStream; // Output stream for the actor collection
// Add materials with IDs to collection
PxCollection* materialCollection = PxCreateCollection();
for(PxU32 i=0;i<mNbMaterials;i++)
materialCollection->add(*materials[i], PxSerialObjectId(i+1));
// Create actor collection, complete and serialize
PxCollection* actorCollection = PxCreateCollection();
for(PxU32 i=0;i<mNbActors;i++)
actorCollection->add(*actors[i]);
PxSerialization::complete(*actorCollection, *registry, materialCollection);
// Binary
PxSerialization::serializeCollectionToBinary(actorStream, *actorCollection, *registry,
materialCollection);
//~Binary
// RepX
PxSerialization::serializeCollectionToXml(actorStream, *actorCollection, *registry, NULL,
materialCollection);
//~RepX
actorCollection->release();
materialCollection->release(); // Note that materialCollection was not serialized
registry->release();
Deserialization::
PxScene* scene; // The scene into which the objects will be inserted
PxSerializationRegistry* registry; // Registry for serializable types
PxCooking* cooking; // Cooking library needed for instantiating objects(RepX)
PxMaterial** materials; // Created procedurally by application
// recreate material collection with consistent IDs, no deserialization
PxCollection* materialCollection = PxCreateCollection();
for(PxU32 i=0;i<mNbMaterials;i++)
materialCollection->add(*materials[i], PxSerialObjectId(i+1));
// Deserialize actors with reference material collection
// Binary
void* actorMemory128; // aligned memory containing serialized actors
PxCollection* actorCollection =
PxSerialization::createCollectionFromBinary(actorMemory128, *registry,
materialCollection);
//~Binary
// RepX
PxInputData& actorInputData = ...; // Implemented by the application
PxCollection* actorCollection =
PxSerialization::createCollectionFromXml(actorInputData, *cooking, *registry,
materialCollection);
//~RepX
materialCollection->release();
scene->addCollection(*actorCollection);
actorCollection->release();
registry->release();
.. _DeserializeReferenceCounting:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Reference Counting of Deserialized Objects
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
This section assumes the background in :ref:`BasicReferenceCounting`.
Objects that are created by deserialization are always created with a reference that the application needs to give up by explicitly calling *release()*. The information whether the application gave up a reference to an object is **not** preserved on serialization.
See :ref:`RigidBodyCollisionShapes` for a discussion of the method *PxRigidActorExt::createExclusiveShape*, which automatically releases the initial reference to the shape, leaving only the actor's reference. Again, the information that this reference has been released is not preserved by serialization.
Example for shapes::
PxOutputStream& outStream; // Output stream for the collection
PxSerializationRegistry* registry; // Registry for serializable types
PxRigidActor* actor; // Any actor
// Creating shapes in different ways implies different rules for releasing
// Shape is automatically released when actor gets released
PxShape* shapeA = PxRigidActorExt::createExclusiveShape(*actor, ...);
// Shape is either created as "shared" or "exclusive" and needs to be released by
// the application
PxShape* shapeB = PxGetPhysics().createShape(...);
actor->attachShape(*shapeB);
// Create collection with actor and shapes and serialize
PxCollection* collection = PxCreateCollection();
collection->add(*actor);
collection->add(*shapeA);
collection->add(*shapeB);
PxSerialization::serializeCollectionToBinary(outStream, *collection, *registry, NULL);
collection->release();
// Releasing actors and shapes
actor->release(); // Releases actor and shapeA (automatically)
shapeB->release(); // Releases shapeB (necessary since shapeB was created through PxPhysics)
// Deserialize collection
...
void* memory128 = ...; // Aligned memory for serialized data
collection = PxSerialization::createCollectionFromBinary(memory128, *registry, NULL);
// Release actors and release ALL shapes (necessary since shape creation history is
// not preserved across serialization
for(PxU32 i = 0; i < collection->getNbObjects(); i++)
{
switch ( collection->getObject(i).getConcreteType() )
{
case PxConcreteType::eRIGID_DYNAMIC:
case PxConcreteType::eRIGID_STATIC:
static_cast<PxActor&>(collection->getObject(i)).release(); // Doesn't release
break; // any shapes
case PxConcreteType::eSHAPE:
static_cast<PxShape&>(collection->getObject(i)).release(); // All shapes need to be
break; // released explicitly
}
}
.. note:: There is a PhysXExtensions function to release all objects within a collection: *PxCollectionExt::releaseObjects*.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Reconnecting PhysX and Application-Objects
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Here is an example of how to fix up references with application objects by querying the IDs of a collection::
PxPhysics* physics; // The physics SDK object
PxCooking* cooking; // Cooking library needed for instantiating objects(RepX)
PxSerializationRegistry* registry; // Registry for serializable types
// Deserialize objects along with IDs
// Binary
void* memory128; // Aligned memory containing serialized objects
PxCollection* collection =
PxSerialization::createCollectionFromBinary(memory128, *registry, NULL);
//~Binary
// RepX
PxInputData& inputData = ...; // Implemented by the application
PxCollection* collection =
PxSerialization::createCollectionFromXml(actorInputData, *cooking, *registry,
materialCollection);
//~RepX
// Receive a list of all deserialized IDs
#define MAX_IDS 100
PxSerialObjectId idBuffer[MAX_IDS];
PxU32 numIds = collection->getIds(idBuffer, MAX_IDS);
// iterate over the list to patch up application objects
for (PxU32 i = 0; i < numIds; i++)
{
PxActor* actor = collection->find(idBuffer[i])->is<PxActor>();
if (actor)
{
// this assumes that findAppObjectFromId is able to locate
// the corresponding application object from a PxSerialObjectId
actor->userData = findAppObjectFromId(idBuffer[i]);
}
}
Alternatively *PxCollection::getObjects(...)* and *PxCollection::getId(PxBase& object)* can be used to achieve the same.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Serializing Everything
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
PhysX provides two utility functions for serializing the entirety of the PhysX runtime: *PxCollectionExt::createCollection(PxPhysics& sdk)* and *PxCollectionExt::createCollection(PxScene& scene)*::
PxPhysics* physics; // The physics SDK object
PxScene* scene; // The physics scene
PxSerializationRegistry* registry; // Registry for serializable types
PxOutputStream& outStream; // The user stream doing the actual write to disk
// 1) Create a collection from the set of all objects in the physics SDK that are shareable across
// multiple scenes.
PxCollection* everythingCollection = PxCollectionExt::createCollection(*physics);
// 2) Create a collection from all objects in the scene and add it
// to everythingCollection.
PxCollection* collectionScene = PxCollectionExt::createCollection(*scene);
everythingCollection->add(*collectionScene);
collectionScene->release();
// 3) Complete collection
PxSerialization::complete(*everythingCollection, *registry);
// 4) serialize collection and release it
// Binary
PxSerialization::serializeCollectionToBinary(outStream, *everythingCollection, *registry);
//~Binary
// RepX
PxSerialization::serializeCollectionToXml(outStream, *everythingCollection, *registry);
//~RepX
everythingCollection->release();
registry->release();
Deserialization is as previously::
PxScene* scene; // The physics scene
PxCooking* cooking; // Cooking library needed for instantiating objects by RepX
PxSerializationRegistry* registry; // Registry for serializable types
// Binary
void* memory128 = ...; // a 128-byte aligned buffer previously loaded from disk
// by the user
PxCollection* everythingCollection =
PxSerialization::createCollectionFromBinary(memory128, *registry);
//~Binary
// RepX
PxInputData& inputData = ...; // Implemented by the application
PxCollection* everythingCollection =
PxSerialization::createCollectionFromXml(inputData, *cooking, *registry);
//~RepX
scene->addCollection(*everythingCollection);
everythingCollection->release();
registry->release();
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Serializability
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
This section contains various definitions to describe serializability of a collection. Whether a collection can be successfully serialized and deserialized, optionally given an external references collection, can be queried by calling *PxSerialization::isSerializable(...)*
.. _Requires:
....................................................................................
Requires
....................................................................................
An object **A** requires another object **B** if **A** maintains a reference to **B** that needs to be re-established for successfully deserializing **A**. This implies that **B** needs to be deserialized before **A**.
Here is the table of the relationship **requires** of all PhysX objects:
+-----------------+-------------------------------------------------------------------------------------------------+
|**joints** |require their **actors** and **constraint** |
+-----------------+-------------------------------------------------------------------------------------------------+
|**rigid actors** |require their **shapes** |
+-----------------+-------------------------------------------------------------------------------------------------+
|**shapes** |require their **materials** and **mesh** (triangle mesh, convex mesh or height field), if any |
+-----------------+-------------------------------------------------------------------------------------------------+
|**articulations**|require their **links** and **joints** |
+-----------------+-------------------------------------------------------------------------------------------------+
|**aggregates** |require their **actors** |
+-----------------+-------------------------------------------------------------------------------------------------+
.. _Subordinate:
....................................................................................
Subordinate
....................................................................................
Subordinates are objects that cannot be instantiated without being owned by other objects. An articulation link, for example, can only be instantiated as part of its articulation.
The following three types are **subordinates**:
+----------------------------------+
|**articulation links** |
+----------------------------------+
|**articulation joint** |
+----------------------------------+
|**constraints** |
+----------------------------------+
.. _Complete:
....................................................................................
Complete
....................................................................................
Definition of a complete set:
A set of objects **C** is **complete** if every object **required** by **C** is in **C**.
Definition of a set that is complete relative to another set:
A set of objects **C** is **complete** relative to a set **D** if every object **required** by **C** is in **C** or in **D**. This means that **C** can be deserialized given **D**.
.. _Serializable:
....................................................................................
Serializable
....................................................................................
Here is the complete set of requirements on a collection **C** with dependencies to **D** such that **C** can be serialized:
* **C** is complete relative to **D**. ("no dangling references")
* Every object in **D** required by an object in **C** has a valid ID. ("no unnamed references")
* Every subordinate object in **C** is required by another object in **C**. ("no orphans")
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Binary Serialization Specifics
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
The following sections describe specific properties of the binary serialization system.
....................................................................................
Memory Management
....................................................................................
Management of memory blocks containing deserialized objects is left to users. It is the user's responsibility to:
* allocate the memory block. Note that it must be properly aligned, to a *PX_SERIAL_FILE_ALIGN* (128) bytes boundary.
* fill the block with serialized data, typically by loading it from disk.
* deallocate the memory block when the objects within have been released by PhysX.
Although the user owns the memory block, the PhysX runtime owns any deserialized objects it contains. Concretely, calling release() on an object that was created by deserialization will cause its destructor to run, but will not deallocate its memory. If the block is deallocated before the destructors have run for all the objects it contains, the PhysX runtime will likely crash. For more information about how deserialized objects need to be released see :ref:`DeserializeReferenceCounting`.
.. _Retargeting:
....................................................................................
Versioning
....................................................................................
The binary serialized data is typically specific to the version of the SDK it was produced with. However, a SDK version can load the data of older SDK versions if the binary format didn't change. This is usually the case with bugfix releases. The global unique identifier PX_BINARY_SERIAL_VERSION is used to version the binary data and is updated for every release with changed format.
....................................................................................
Retargeting to other Platforms
....................................................................................
Binary serialized data is platform-specific, and when serialized it always targets the platform on which it was created. The binary converter in the extensions library retargets data from one platform to another. Typically assets are serialized on an authoring platform (Windows, Mac OS X and Linux). The serialized data can then be retargeted, for example, to a console or any other runtime platform.
The converter requires meta-data for the source and target platforms, which contains information about the binary layout of objects for that platform. To obtain metadata, use the function provided in the extensions library for each platform::
void PxSerialization::dumpBinaryMetaData(PxOutputStream& stream, PxSerializationRegistry& sr);
On each target platform, run it once and keep generated data around. Alternatively a set of pre-built binary metadata is included with the PhysX SDK at [path to installed PhysX SDK]/Tools/BinaryMetaData.
.. figure:: ../images/Serialization_Retargeting.png
:scale: 100
:align: center
Figure 3: Schema of Retargeting
Assuming that the extensions library has been initialized, conversion takes place as follows::
PxSerializationRegistry* registry; // Registry for serializable types
PxInputStream& srcMetadata; // metadata for the 'from' platform
// (e.g. PxDefaultFileInputData)
PxInputStream& dstMetadata; // metadata for the 'to' platform
PxInputStream& srcAsset; // stream containing source asset
PxU32 srcAssetSize; // size of the source asset
PxOutputStream& dstAsset; // output stream for retargeted asset
PxBinaryConverter* converter = PxSerialization::createBinaryConverter();
converter->setMetaData(srcMetadata, dstMetadata);
converter->convert(srcAsset, srcAssetSize, dstAsset);
....................................................................................
The Convert Tool
....................................................................................
The convert tool is at [path to installed PhysX SDK]/Snippets/SnippetConvert. It illustrates how to convert PhysX 3 serialized binary files from
one platform to another. It only compiles and runs on authoring platforms (Windows, MacOs and Linux).
SnippetConvert is a simple command-line tool supporting the following options::
--srcMetadata=<filename> Defines source metadata file
--dstMetadata=<filename> Defines target metadata file
--srcBinFile=<filename> Source binary file to convert
--dstBinFile=<filename> Outputs target binary file
--generateExampleFile=<filename> Generates an example file
--verbose Enables verbose mode
....................................................................................
Object Names
....................................................................................
Some SDK objects, such as shapes and actors, can be given names using the *PxShape::setName()* and *PxActor::setName()* functions. By default these names are not serialized. The 'exportNames' parameter of the *PxSerialization::serializeCollectionToBinary()* can be set to true in order to serialize the names along with the objects.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
API-level Serialization (RepX) Specifics
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
RepX stands for Representation X and is the ASCII-XML serialization format for PhysX 3. As opposed to binary serialization, the RepX XML serialization is not intended to be used in performance critical or memory constrained situations. The following sections describe specifics of the RepX XML serialization system.
....................................................................................
Upgrading RepX Data
....................................................................................
Upgrading RepX data from an older PhysX version to a newer one is easy. It happens implicitly when deserializing old RepX data with a newer PhysX SDK and re-serializing the resulting PxCollection.
Example for upgrading a RepX stream::
PxPhysics* physics; // The physics SDK object (e.g.
// PhxsX 3.3)
PxCooking* cooking; // Cooking library needed for
// instantiating objects
PxSerializationRegistry* registry; // Registry for serializable types
PxDefaultFileInputData inputData(pathTo30RepXFile); //load an older 3.x RepX file
PxCollection* collection =
PxSerialization::createCollectionFromXml(inputData, *cooking, *registry);
PxDefaultFileOutputStream outStream(pathToNewRepXFile);
PxSerialization::serializeCollectionToXml(outStream, *collection, *registry);
....................................................................................
Object Names
....................................................................................
As opposed to binary serialization, the object names that can be specified with the *PxShape::setName()* and *PxActor::setName()* functions, are always included in the serialized format. On deserialization with *PxSerialization::createCollectionFromXml(...)* the names can be recovered by setting the *PxStringTable* parameter.
If *PxStringTable* parameter is set, the names will live within the memory which is allocated by the string table. The string table must not be released unless it can be guaranteed that the names will not be accessed any more.
.. _CachingCookedGeometryData:
....................................................................................
Caching Cooked Geometry Data
....................................................................................
In order to facilitate faster instantiation of XML data, it is possible to configure the XML serialization to store the cooked triangle and convex mesh data along with the plain data. The cooked data caching can be enabled by passing a *PxCooking* instance into *PxSerialization::serializeCollectionToXml(...)*. The cached cooked data is ignored when its format is incompatible with the current SDK version.
====================================================
Common Use Cases
====================================================
API-level RepX serialization should be used whenever compatibility and human readability are important. The PhysX plug-ins for the DCC tools 3ds Max and Maya use RepX to export PhysX objects. The resulting RepX files can then be deserialized and loaded into the PhysX runtime. This is useful for rapid prototyping or for generally loading PhysX assets if performance is not of a big concern. For quick loading of assets it is better to convert RepX data into binary serialized data. RepX is also useful for reproducing situations with unwanted behavior without the need to provide the whole application. For this, the application may be connected to the PhysX Visual Debugger (PVD), which records the scene of interest. A representative frame can then be saved in RepX format from within PVD (see :ref:`PVD`).
Binary serialization should be used in performance and memory constrained situations. The main target use-case is streaming in chunks of a large game level that can't be loaded into memory at once. Creating and loading save games is another application that could be optimized by using binary serialization. PhysX objects in binary format can also be sent over the network to enable efficient game state synchronization.
====================================================
Snippet Discussion
====================================================
The following snippets illustrate common operations such as managing collections, serialization, deserialization and re-targeting of binary data.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SnippetSerialization
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SnippetSerialization shows binary and XML serialization of a scene with a number of jointed rigid bodies representing a chain. This is done in a way that allows the instantiation of multiple chains while sharing the shape and the material across all chains. The snippet shows how to create and populate collections, specify IDs to enable resolving dependencies, serialize collections, deserialize collections and add actors to the scene for simulation.
The snippet also shows how to allocate a data block aligned to 128 bytes and demonstrates how to copy binary serialized data into it. It further demonstrates that the data blocks containing the binary deserialized collections must be maintained until the corresponding objects are not needed anymore and have been released.
.. figure:: ../images/Serialization_Snippet.png
:scale: 100
:align: center
Figure 4: SnippetSerialization
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SnippetConvert
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SnippetConvert illustrates how binary serialized data can be re-targeted from an authoring platform to a runtime platform such as a console. The snippet is a simple command line tool that can load a binary serialized data file along with meta data files for both source and destination platforms and then output a converted binary data file. See the snippet's source documentation for more details on usage.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SnippetLoadCollection
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SnippetLoadCollection shows how to deserialize serialized collections from either binary or XML format. The snippet is a command line tool that can connect to the PhysX Visual Debugger application and display the content of serialized collection files. See the snippet's source documentation for more details.
====================================================
Best practices / Troubleshooting
====================================================
* Concurrent simulation and serialization is not supported and leads to undefined behavior.
* If releasing PhysX objects leads to crashes or errors it is possible that the application is releasing some objects twice. The following two reasons should be considered: 1.) A potential source of error is to release PhysX objects without updating collections referencing these objects. 2.) Shapes that where created through an actor have their application reference automatically released on creation. If such a shape is serialized and deserialized the creation history will be lost. It might be convenient to use the extension function *PxCollectionExt::releaseObjects* because it deals with the different cases as required. See :ref:`DeserializeReferenceCounting`.
* If accessing binary deserialized PhysX objects, including accesses during simulation, causes crashes it might be due to the premature release of the memory block that holds the deserialized objects.
* If binary files are too large and/or too slow to load it might be that shared assets have been serialized multiple times. An example of a shared asset might be a mesh that is referenced by multiple shapes. The solution is to separate shared PhysX objects into a separate collection. See :ref:`SerializingPartialObjectgraphs`.
* If loading PhysX objects from RepX files is too slow two things should be considered: 1.) Could binary serialization be used instead? Even for debugging it might make sense to convert RepX files into binary serialized data by re-serializing them with the binary approach. 2.) Meshes tend to load very slowly from text files. RepX serialization offers an option to cache cooked mesh data by in-lining binary data into the RepX file. If such a cache is present and valid, the loading can become significantly faster. See :ref:`CachingCookedGeometryData`.
.. _PVD:
====================================================
PVD
====================================================
The PhysX Remote Debugger provides the functionality to export single frames of PhysX scenes as RepX files. The resulting files can be used to playback a snapshot of the PhysX state. In many cases this is sufficient to isolate an issue. The option can be found in the menu of PVD:
[Menu > File > Export Current Frame To RepX]
.. figure:: ../images/Serialization_PVD.png
:scale: 100
:align: center
Figure 5: RepX Functionality in PVD

View File

@ -0,0 +1,197 @@
.. _Simulation:
------------------------
Simulation
------------------------
.. _callbacks:
===============================
Callback Sequence
===============================
The simplest type of simulation callbacks are the events. Using callbacks the application can simply listen for events and react as required, provided the callbacks obey the rule that SDK state changes are forbidden. This restriction may be a bit surprising given that the SDK permits writes to an inactive back-buffer while the simulation is running. Event callbacks, however, are not called from within the simulation thread, but rather from inside fetchResults(). The key point here is that fetchResults() processes the buffered writes, meaning that writing to the SDK from an event callback can be a particularly fragile affair. To avoid this fragility it is necessary to impose the rule that SDK state changes are not permitted from an event callback.
Inside fetchResults(), among other things, the buffers are swapped. More specifically, this means that properties of each object's internal simulation state are copied to the API-visible state. Some event callbacks happen before this swap, and some after. The events that happen before are:
* onTrigger
* onContact
* onConstraintBreak
When these events are received in the callback, the shapes, actors, etc. will still be in the state they were in immediately before the simulation started. This is preferable, because these events were detected early on during the simulation, before objects were integrated (moved) forward. For example, a pair of shapes that get an onContact() to report that they are in contact will still be in contact when the call is made, even though they may have bounced apart again after fetchResults() returns.
On the other hand, these events are sent after the swap:
* onSleep
* onWake
Sleep information is updated after objects have been integrated, so it makes sense to send these events after the swap.
To 'listen' to any of these events it is necessary to first subclass PxSimulationEventCallback so that the various virtual functions may be implemented as desired. An instance of this subclass can then be registered per scene with either PxScene::setSimulationEventCallback or PxSceneDesc::simulationEventCallback. Following these steps alone will ensure that constraint break events are successfully reported. One further step is required to report sleep and wake events: to avoid the expense of reporting all sleep and wake events, actors identified as worthy of sleep/wake notification require the flag PxActorFlag::eSEND_SLEEP_NOTIFIES to be raised. Finally, to receive onContact and onTrigger events it is necessary to set a flag in the filter shader callback for all pairs of interacting objects for which events are required. More details of the filter shader callback can be found in Section :ref:`collisionFiltering`.
.. _simulation_memory:
===============================
Simulation memory
===============================
PhysX relies on the application for all memory allocation. The primary interface is via the PxAllocatorCallback interface required to initialize the SDK::
class PxAllocatorCallback
{
public:
virtual ~PxAllocatorCallback() {}
virtual void* allocate(size_t size, const char* typeName, const char* filename,
int line) = 0;
virtual void deallocate(void* ptr) = 0;
};
After the self-explanatory function argument describing the size of the allocation, the next three function arguments are an identifier name, which identifies the type of allocation, and the __FILE__ and __LINE__ location inside the SDK code where the allocation was made. More details of these function arguments can be found in the PhysXAPI documentation.
.. note:: An important change since 2.x: The SDK now requires that the memory that is returned be 16-byte aligned. On many platforms malloc() returns memory that is 16-byte aligned, but on Windows the system function _aligned_malloc() provides this capability.
.. note:: On some platforms PhysX uses system library calls to determine the correct type name, and the system function that returns the type name may call the system memory allocator. If you are instrumenting system memory allocations, you may observe this behavior. To prevent PhysX requesting type names, disable allocation names using the method PxFoundation::setReportAllocationNames().
Minimizing dynamic allocation is an important aspect of performance tuning. PhysX provides several mechanisms to control and analyze memory usage. These shall be discussed in turn.
++++++++++++++++++++++++++++++++++++++++++
Scene Limits
++++++++++++++++++++++++++++++++++++++++++
The number of allocations for tracking objects can be minimized by presizing the capacities of scene data structures, using either PxSceneDesc::limits before creating the scene or the function PxScene::setLimits(). It is useful to note that these limits do not represent hard limits, meaning that PhysX will automatically perform further allocations if the number of objects exceeds the scene limits.
++++++++++++++++++++++++++++++++++++++++++
16K Data Blocks
++++++++++++++++++++++++++++++++++++++++++
Much of the memory PhysX uses for simulation is held in a pool of blocks, each 16K in size. The initial number of blocks allocated to the pool can be controlled by setting PxSceneDesc::nbContactDataBlocks, while the maximum number of blocks that can ever be in the pool is governed by PxSceneDesc::maxNbContactDataBlocks. If PhysX internally needs more blocks than nbContactDataBlocks then it will automatically allocate further blocks to the pool until the number of blocks reaches maxNbContactDataBlocks. If PhysX subsequently needs more blocks than the maximum number of blocks then it will simply start dropping contacts and joint constraints. When this happens warnings are passed to the error stream in the PX_CHECKED configuration.
To help tune nbContactDataBlocks and maxNbContactDataBlocks it can be useful to query the number of blocks currently allocated to the pool using the function PxScene::getNbContactDataBlocksUsed(). It can also be useful to query the maximum number of blocks that can ever be allocated to the pool with PxScene::getMaxNbContactDataBlocksUsed.
Unused blocks can be reclaimed using PxScene::flushSimulation(). When this function is called any allocated blocks not required by the current scene state will be deleted so that they may be reused by the application. Additionally, a number of other memory resources are freed by shrinking them to the minimum size required by the scene configuration.
++++++++++++++++++++++++++++++++++++++++++
Scratch Buffer
++++++++++++++++++++++++++++++++++++++++++
A scratch memory block may be passed as a function argument to the function PxScene::simulate. As far as possible, PhysX will internally allocate temporary buffers from the scratch memory block, thereby reducing the need to perform temporary allocations from PxAllocatorCallback. The block may be reused by the application after the PxScene::fetchResults() call, which marks the end of simulation. One restriction on the scratch memory block is that it must be a multiple of 16K, and it must be 16-byte aligned.
++++++++++++++++++++++++++++++++++++++++++
In-place Serialization
++++++++++++++++++++++++++++++++++++++++++
PhysX objects cab be stored in memory owned by the application using PhysX' binary deserialization mechanism. See :ref:`serialization` for details.
++++++++++++++++++++++++++++++++++++++++++
PVD Integration
++++++++++++++++++++++++++++++++++++++++++
Detailed information about memory allocation can be recorded and displayed in the PhysX Visual Debugger. This memory profiling feature can be configured by setting the trackOutstandingAllocations flag when calling PxCreatePhysics(), and raising the flag PxVisualDebuggerConnectionFlag::eMEMORY when connecting to the debugger with PxVisualDebuggerExt::createConnection().
===============================
Completion Tasks
===============================
A completion task is a task that executes immediately after PxScene::simulate has exited. If PhysX has been configured to use worker threads then PxScene::simulate will start simulation tasks on the worker threads and will likely exit before the worker threads have completed the work necessary to complete the scene update. As a consequence, a typical completion task would first need to call PxScene::fetchResults(true) to ensure that fetchResults blocks until all worker threads started during simulate() have completed their work. After calling fetchResults(true), the completion task can perform any other post-physics work deemed necessary by the application:
scene.fetchResults(true);
game.updateA();
game.updateB();
...
game.updateZ();
The completion task is specified as a function argument in PxScene::simulate. More details can be found in the PhysAPI documentation.
=================================
Synchronizing with Other Threads
=================================
An important consideration for substepping is that simulate() and fetchResults() are classed as write calls on the scene, and it is therefore illegal to read from or write to a scene while those functions are running. For the simulate() function it is important to make the distinction between running and ongoing. In this context, it is illegal to read or write to a scene before simulate() exits. It is perfectly legal, however, to read or write to a scene after simulate() has exited but before the worker threads that started during the simulate() call have completed their work.
.. note:: PhysX does not lock its scene graph, but it will report an error in checked build if it detects that multiple threads make concurrent calls to the same scene, unless they are all read calls.
===============================
Substepping
===============================
For reasons of fidelity simulation or better stability it is often desired that the simulation frequency of PhysX be higher than the update rate of the application. The simplest way to do this is just to call simulate() and fetchResults() multiple times::
for(PxU32 i=0; i<substepCount; i++)
{
... pre-simulation work (update controllers, etc) ...
scene->simulate(substepSize);
scene->fetchResults(true);
... post simulation work (process physics events, etc) ...
}
Sub-stepping can also be integrated with the completion task feature of the simulate() function. To illustrate this, consider the situation where the scene is simulated until the graphics component signals that it has completed updating the render state of the scene. Here, the completion task will naturally run after simulate() has exited. Its first job will be to block with fetchResults(true) to ensure that it waits until both simulate() and fetchResults() have completed their sequential work. When the completion task is able to proceed its next work item will be to query the graphics component to check if another simulate() is required or if it can exit. In the case that another simulate() step is required it will clearly need to pass a completion task to simulate(). A tricky point here is that a completion task cannot submit itself as the next completion task because it would cause an illegal recursion. A solution to this problem might to be to have two completion tasks where each stores a reference to the other. Each completion task can then pass its partner to simulate()::
scene.fetchResults(true);
if(!graphics.isComplete())
{
scene.simulate(otherCompletionTask);
}
===============================
Split sim
===============================
As an alternative to simulate(), you can split the simulation into two different phases, collide() and advance(). For some properties, called write-through properties, modifications during the collide() phase will be seen immediately by the subsequent advance() phase. This allows collide() to begin before the data required by advance() is available and to run in parallel with application side logic that generates inputs to advance(). This is particularly useful for animation logic generating kinematic targets, and for controllers applying forces to bodies. The write-through properties are listed below::
addForce()/addTorque()/clearForce()/clearTorque()
setAngularVelocity()/setLinearVelocity()
setKinematicTarget()
wakeUp()
setWakeCounter()
When using the split sim, a physics simulation loop would look like this::
scene.collide(dt)
scene.fetchCollision()
scene.advance()
scene.fetchResults()
Any other sequence of API calls is illegal. The SDK will issue error messages. The users can interleave the physics-dependent application logic between collide() and fetchCollision::
scene.collide(dt)
physics-dependent game logic(anmimation, rendering)
scene.fetchCollision()
fetchCollision() will wait until collide() has finished before it updates the write-through properties in the SDK. Once fetchCollision() has completed, any state modification performed on the objects in the executing scene will be buffered and will not be reflected until the simulation and a call to fetchResults() has completed. The solver will take the write-through properties into account when computing the new sets of velocities and poses for the actors being simulated.
===============================
Split fetchResults
===============================
The fetchResults() method is available in both a standard and split format. The split format offers some advantages over the standard fetchResult() method because it permits the user to parallelize processing of contact reports, which can be expensive when simulating complex scenes.
A simplistic way to use split fetchResults would look something like this::
gSharedIndex = 0;
gScene->simulate(1.0f / 60.0f);
//Call fetchResultsStart. Get the set of pair headers
const PxContactPairHeader* pairHeader;
PxU32 nbContactPairs;
gScene->fetchResultsStart(pairHeader, nbContactPairs, true);
//Set up continuation task to be run after callbacks have been processed in parallel
callbackFinishTask.setContinuation(*gScene->getTaskManager(), NULL);
callbackFinishTask.reset();
//process the callbacks
gScene->processCallbacks(&callbackFinishTask);
callbackFinishTask.removeReference();
callbackFinishTask.wait();
gScene->fetchResultsFinish();
The user is free to use their own task/threading system to process the callbacks. However, the PhysX scene provides a utility function that processes the callbacks using multiple threads, which is used in this code snippet. This method takes a continuation task that will be run when the tasks processing callbacks have completed. In this example, the completion task raises an event that can be waited upon to notify the main thread that callback processing has completed.
This feature is demonstrated in SnippetSplitFetchResults. In order to make use of this approach, contact notification callbacks must be thread-safe. Furthermore, for this approach to be beneficial, contact notification callbacks need to be doing a significant amount of work to benefit from multi-threading them

View File

@ -0,0 +1,177 @@
.. _Startup:
-----------------------
Startup and Shutdown
-----------------------
===============================
Introduction
===============================
The first step in using the PhysX SDK in a program is the initialization of some global objects. These objects can be released when PhysX is no longer needed to free resources. This chapter describes how to do this.
===============================
Foundation and Physics
===============================
First, in some startup code, create a *PxFoundation* object::
static PxDefaultErrorCallback gDefaultErrorCallback;
static PxDefaultAllocator gDefaultAllocatorCallback;
mFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gDefaultAllocatorCallback,
gDefaultErrorCallback);
if(!mFoundation)
fatalError("PxCreateFoundation failed!");
Every PhysX module requires a PxFoundation instance to be available. The required parameters are a version ID, an allocator callback and an error callback. *PX_PHYSICS_VERSION*, is a macro predefined in our headers to enable PhysX to check for a version mismatch between the headers and the corresponding SDK DLLs.
Usually, the allocator callback and error callback are specific to the application, but PhysX provides default implementations that make it easy to get started. See :ref:`MemoryManagement` and :ref:`ErrorReporting` for more details of these callbacks. (The actual sample code supports an advanced memory allocator that tracks allocations instead of the default, but we have omitted that detail here.)
Now create the top-level *PxPhysics* object::
bool recordMemoryAllocations = true;
mPvd = PxCreatePvd(*gFoundation);
PxPvdTransport* transport = PxDefaultPvdSocketTransportCreate(PVD_HOST, 5425, 10);
mPvd->connect(*transport,PxPvdInstrumentationFlag::eALL);
mPhysics = PxCreatePhysics(PX_PHYSICS_VERSION, *mFoundation,
PxTolerancesScale(), recordMemoryAllocations, mPvd);
if(!mPhysics)
fatalError("PxCreatePhysics failed!");
Again, the version ID has to be passed in. The PxTolerancesScale parameter makes it easier to author content at different scales and still have PhysX work as expected, but to get started simply pass a default object of this type. The recordMemoryAllocations parameter specifies whether to perform memory profiling. The optional PVD instance enables the debugging and profiling with the PhysX Visual Debugger.
===============================
Cooking
===============================
The PhysX cooking library provides utilities for creating, converting, and serializing bulk data. Depending on your application, you may wish to link to the cooking library in order to process such data at runtime. Alternatively you may be able to process all such data in advance and just load it into memory as required. Initialize the cooking library as follows::
mCooking = PxCreateCooking(PX_PHYSICS_VERSION, *mFoundation, PxCookingParams(scale));
if (!mCooking)
fatalError("PxCreateCooking failed!");
The PxCookingParams struct configures the cooking library to target different platforms, use non-default tolerances or produce optional outputs. It is important to use consistent PxTolerancesScale values everywhere in your application (see :ref:`PxToleranceScale` for more details).
The cooking library generates data through a streaming interface. In the samples, implementations of streams are provided in the PxToolkit library to read and write from files and memory buffers. Heightfield or Trianglemesh cooked meshes can be directly inserted into PxPhysics without serialization using the PxPhysicsInsertionCallback. The default callback must be used and can be obtained using the PxPhysics::getPhysicsInsertionCallback().
===============================
Extensions
===============================
The extensions library contains many functions that may be useful to a large class of users, but which some users may prefer to omit from their application either for code size reasons or to avoid use of certain subsystems, such as those pertaining to networking. Initializing the extensions library requires the PxPhysics object::
if (!PxInitExtensions(*mPhysics, mPvd))
fatalError("PxInitExtensions failed!");
.. _optionalSDKComponents:
===============================
Optional SDK Components
===============================
When linking PhysX as a static library on memory constrained platforms, it is possible to avoid linking the code of some PhysX features that are not always used in order to save memory. Currently the optional features are:
* Articulations
* Height Fields
If your application requires a subset of this functionality, it is recommended that you call PxCreateBasePhysics as opposed to PxCreatePhysics and then manually register the components you require. Below is an example that registers some of the options::
physx::PxPhysics* customCreatePhysics(physx::PxU32 version,
physx::PxFoundation& foundation,
const physx::PxTolerancesScale& scale,
bool trackOutstandingAllocations
physx::PxPvd* pvd)
{
physx::PxPhysics* physics = PxCreateBasePhysics(version, foundation, scale,
trackOutstandingAllocations, pvd);
if(!physics)
return NULL;
PxRegisterArticulations(*physics);
PxRegisterHeightFields(*physics);
return physics;
}
Note that this will only save memory when linking PhysX as a static library, as we rely on the linker to strip out the unused code.
===============================
Delay-Loading DLLs
===============================
The PhysXCommon DLL and PhysXFoundation DLL are marked as delay-loaded inside of the PhysX, PhysXCooking and PhysXCommon projects. So it is possible to have delay-loaded PhysXFoundation, PhysXCommon, PhysX and PhysXCooking DLLs.
+++++++++++++++++++++++++++++++++++++++++++++++++++
PhysXCommon DLL and PhysXFoundation DLL load
+++++++++++++++++++++++++++++++++++++++++++++++++++
* If delay load hook is specified the PhysXCommon name or PhysXFoundation name provided by user is used
* If delay load hook is not specified, the corresponding PhysXCommon DLL or PhysXFoundation DLL is used
+++++++++++++++++++++++++++++++
PxDelayLoadHook
+++++++++++++++++++++++++++++++
The PxDelayLoadHook class supports loading of the PhysXCommon, PhysXFoundation DLLs with different names and paths. This can be achieved by providing different DLL paths to the PhysX SDK through a custom subclass of PxDelayLoadHook, see the following example::
class SnippetDelayLoadHook: public PxDelayLoadHook
{
virtual const char* getPhysXCommonDllName() const
{ return "customPath\\PhysXCommon_x64_Renamed.dll"; }
virtual const char* getPhysXFoundationDllName() const
{ return "customPath\\PhysXFoundation_x64_Renamed.dll"; }
} gDelayLoadHook;
Now the hook must be set for PhysX, PhysXCooking, PhysXCommon::
PxSetPhysXDelayLoadHook(&gDelayLoadHook);
PxSetPhysXCookingDelayLoadHook(&gDelayLoadHook);
PxSetPhysXCommonDelayLoadHook(&gDelayLoadHook);
For more information please see the SnippetDelayLoadHook.
+++++++++++++++++++++++++++++++
PxGpuLoadHook
+++++++++++++++++++++++++++++++
The PxGpuLoadHook class supports loading of the PhysXGpu DLL with a different name or path. This can be achieved by providing a different DLL path to the PhysX SDK through a custom subclass of PxGpuLoadHook, see the following example::
class SnippetGpuLoadHook: public PxGpuLoadHook
{
virtual const char* getPhysXGpuDllName() const
{ return "customPath\\PhysXGpu_x64_Renamed.dll"; }
} gGpuLoadHook;
Now the hook must be set for PhysX::
PxSetPhysXGpuLoadHook(&gGpuLoadHook);
For more information please see the SnippetDelayLoadHook.
+++++++++++++++++++++++++++++++
PhysXCommon Secure Load
+++++++++++++++++++++++++++++++
All PhysX DLLs distributed by NVIDIA are signed. The PhysXCommon DLL signature is checked, when it is loaded by PhysX or PhysXCooking. If signature test fails the application is terminated.
===============================
Shutting Down
===============================
To dispose of any PhysX object, call its release() method. This will destroy the object, and all contained objects. The precise behavior depends on the object type being released, so refer to the reference guide for details. To shut down the extensions library, call the function *PxCloseExtensions()*. To shut down physics, call release() on the PxPhysics object, and this will clean up all of the physics objects::
mPhysics->release();
Do not forget to release the foundation object as well, but only after all other PhysX modules have been released::
mFoundation->release();

View File

@ -0,0 +1,25 @@
.. _statistics:
---------------------
Simulation Statistics
---------------------
=========
Interface
=========
In this chapter we will have a quick look at the statistics information that PhysX collects every simulation step. Usually, this information can be explored in the PhysX Visual Debugger but we do offer a PhysX API method as well to allow applications to access the data directly. After a simulation step and a call to *PxScene::fetchResults()*, the simulation statistics for the processed step can be retrieved through the *PxScene::getSimulationStatistics()* interface. The method copies the data to a user provided *PxSimulationStatistics* structure. For details about the individual members please refer to the API documentation.
.. note:: Do not fetch the simulation statistics while the simulation is running.
=====
Usage
=====
The provided simulation statistics is mainly meant to help investigate performance issues. It provides a quantitative summary of the work done, i.e., the number of objects or combination of objects which have been processed in the current simulation step. For example, if you encounter performance spikes in certain frames, then the simulation statistics might give some insight into possible causes. For instance:
* Has a large amount of volumes been added or removed from the broadphase in one single step? You could try to distribute the addition/removal of objects over a couple of simulation steps.
* Are there suddenly many more collision pairs processed than expected? This could be caused by a badly configured collision pair filter or maybe some *PxPairFlags* have been accidentally raised.
* etc.
Please keep in mind that the simulation statistics are currently less a measurement of what the scene contains but rather what got processed. So it is only partially helpful to detect whether objects have been configured and arranged properly.

View File

@ -0,0 +1,376 @@
.. _Threading:
---------
Threading
---------
============
Introduction
============
This chapter explains how to use PhysX in multithreaded applications. There are three main aspects to using PhysX with multiple threads:
* how to make read and write calls into the PhysX API from multiple threads without causing race conditions.
* how to use multiple threads to accelerate simulation processing.
* how to perform asynchronous simulation, and read and write to the API while simulation is being processed.
.. _DataAccessRules:
=================================
Data Access from Multiple Threads
=================================
For efficiency reasons, PhysX does not internally lock access to its data structures by the application, so be careful when calling the API from multiple application threads. The rules are as follows:
* API interface methods marked 'const' are read calls, other API interface methods are write calls.
* API read calls may be made simultaneously from multiple threads.
* Objects in different scenes may be safely accessed by different threads.
* Different objects outside a scene may be safely accessed from different threads. Be aware that accessing an object may indirectly cause access to another object via a persistent reference (such as joints and actors referencing one another, an actor referencing a shape, or a shape referencing a mesh.)
Access patterns which do not conform to the above rules may result in data corruption, deadlocks, or crashes. Note in particular that it is not legal to perform a write operation on an object in a scene concurrently with a read operation to an object in the same scene. The checked build contains code which tracks access by application threads to objects within a scene, to try and detect problems at the point when the illegal API call is made.
+++++++++++++++++++++++++
Scene Locking
+++++++++++++++++++++++++
Each PxScene object provides a multiple reader, single writer lock that can be used to control access to the scene by multiple threads. This is useful for situations where the PhysX scene is
shared between more than one independent subsystem. The scene lock provides a way for these systems to coordinate with each other without creating direct dependencies.
It is not mandatory to use the lock. If all access to the scene is from a single thread, using the lock adds unnecessary overhead. Even if you are accessing the scene from multiple threads, you may be able to synchronize the threads using a simpler or more efficient application-specific mechanism that guarantees your application meets the above conditions. However, using the scene lock has two potential benefits:
* If the *PxSceneFlag::eREQUIRE_RW_LOCK* is set, the checked build will issue a warning for any API call made without first acquiring the lock, or if a write call is made when the lock has only been acquired for read,
* The APEX SDK uses the scene lock to ensure that it shares the scene safely with your application.
There are four methods for for acquiring / releasing the lock::
void PxScene::lockRead(const char* file=NULL, PxU32 line=0);
void PxScene::unlockRead();
void PxScene::lockWrite(const char* file=NULL, PxU32 line=0);
void PxScene::unlockWrite();
Additionally there is an RAII helper class to manage these locks, see PxSceneLock.h.
+++++++++++++++++
Locking Semantics
+++++++++++++++++
There are precise rules regarding the usage of the scene lock:
* Multiple threads may read at the same time.
* Only one thread may write at a time, no thread may write if any threads are reading.
* If a thread holds a write lock then it may call both read and write API methods.
* Re-entrant read locks are supported, meaning a *lockRead()* on a thread that has already acquired a read lock is permitted. Each *lockRead()* must have a paired *unlockRead()*.
* Re-entrant write locks are supported, meaning a *lockWrite()* on a thread that has already acquired a write lock is permitted. Each *lockWrite()* must have a paired *unlockWrite()*.
* Calling *lockRead()* by a thread that has already acquired the write lock is permitted and the thread will continue to have read and write access. Each lock*() must have an associated unlock*() that occurs in reverse order.
* Lock upgrading is *not* supported - a *lockWrite()* by a thread that has already acquired a read lock is *not* permitted. Attempting this in checked builds will result in an error, in release builds it will lead to deadlock.
* Writers are favored - if a thread attempts a *lockWrite()* while the read lock is acquired it will be blocked until all readers leave. If new readers arrive while the writer thread is blocked they will be put to sleep and the writer will have first chance to access the scene. This prevents writers being starved in the presence of multiple readers.
* If multiple writers are queued then the first writer will receive priority, subsequent writers will be granted access according to OS scheduling.
Note: *PxScene::release()* automatically attempts to acquire the write lock, it is not necessary to acquire it manually before calling release().
++++++++++++++++++++++
Locking Best Practices
++++++++++++++++++++++
It is often useful to arrange your application to acquire the lock a single time to perform multiple operations. This minimizes the overhead of the lock, and in addition can prevent cases such as a sweep test in one thread seeing a rag doll that has been only partially inserted by another thread.
Clustering writes can also help reduce contention for the lock, as acquiring the lock for write will stall any other thread trying to perform a read access.
===============================
Asynchronous Simulation
===============================
PhysX simulation is asynchronous by default. Start simulation by calling::
scene->simulate(dt);
When this call returns, the simulation step has begun in a separate thread. While simulation is running, you can still make calls into the API. Where those calls affect simulation state, the results will be buffered and reconciled with the simulation results when the simulation step completes.
To wait until simulation completes, call::
scene->fetchResults(true);
The boolean parameter to fetchResults denotes whether the call should wait for simulation to complete, or return immediately with the current completion status. See the API documentation for more detail.
It is important to distinguish two time slots for data access:
1) After the call to *PxScene::fetchResults()* has returned and before the next *PxScene::simulate()* call (see figure below, blue area "1").
2) After the call to *PxScene::simulate()* has returned and before the corresponding *PxScene::fetchResults()* call (see figure below, green area "2").
.. image:: ../images/timeSlots.png
:width: 800 px
In the first time slot, the simulation is not running and there are no restrictions for reading or writing object properties. Changes to the position of an object, for example, are applied instantaneously and the next scene query or simulation step will take the new state into account.
In the second time slot the simulation is running and in the process, reading and changing the state of objects. Concurrent access from the user might corrupt the state of the objects or lead to data races or inconsistent views in the simulation code. Hence the simulation code's view of the objects is protected from API writes, and any attributes the simulation updates are buffered to allow API reads. The consequences will be discussed in detail in the next section.
Note that *simulate()* and *fetchResults()* are write calls on the scene, and as such it is illegal to access any object in the scene while these functions are running.
.. _DoubleBuffering:
++++++++++++++++
Double Buffering
++++++++++++++++
While a simulation is running, PhysX supports read and write access to objects in the scene (with some exceptions, see further below). This includes adding/removing them to/from a scene.
From the user perspective, API changes are reflected immediately. For example, if the velocity of a rigid body is set and then queried, the new velocity will be returned. Similarly, if an object is created while the simulation is running, it can be accessed/modified as any other object. However, these changes are buffered so that the simulation code sees the object state as it was when *PxScene::simulate()* was called. For instance, changes to the filter data of an object while the simulation is running are ignored for collision pair generation of the running step, and will only affect for the next simulation step.
When *PxScene::fetchResults()* is called, any buffered changes are flushed: changes made by the simulation are reflected in API view of the objects, and API changes are made visible to the simulation code for the next step. User changes take precedence: for example, a user change to the position of an object while the simulation is running will overwrite the position which resulted from the simulation.
The delayed application of updates does not affect scene queries, which always take into account the latest changes.
++++++++++++++++++++++++++++++++
Events involving removed objects
++++++++++++++++++++++++++++++++
Deleting objects or removing them from the scene while the simulation is in process will affect the simulation events sent out at *PxScene::fetchResults()*. The behavior is as follows:
* *PxSimulationEventCallback::onWake(), ::onSleep()* events will not get fired if an object is involved which got deleted/removed during the running simulation.
* *PxSimulationEventCallback::onContact(), ::onTrigger()* events will get fired if an object is involved which got deleted/removed during the running simulation. The deleted/removed object will be marked as such (see *PxContactPairHeaderFlag::eREMOVED_ACTOR_0*, *PxContactPairFlag::eREMOVED_SHAPE_0, PxTriggerPairFlag::eREMOVED_SHAPE_TRIGGER*). Furthermore, if *PxPairFlag::eNOTIFY_TOUCH_LOST, ::eNOTIFY_THRESHOLD_FORCE_LOST* events were requested for the pair containing the deleted/removed object, then these events will be created.
+++++++++++++++++++++
Memory Considerations
+++++++++++++++++++++
The buffers to store the object changes while the simulation is running are created on demand. If memory usage concerns outweigh the advantage of reading/writing objects in parallel with simulation, do not write to objects while the simulation is running.
.. _taskman:
========================
Multithreaded Simulation
========================
PhysX includes a task system for managing CPU and GPU compute resources. Tasks are created with dependencies so
that they are resolved in a given order, when ready they are then submitted to a
user-implemented dispatcher for execution.
Middleware products typically do not want to create CPU threads for their
own use. This is especially true on consoles where execution threads
can have significant overhead. In the task model, the computational
work is broken into jobs that are submitted to the application's thread pool as
they become ready to run.
The following classes comprise the CPU task management.
+++++++++++++++++++
TaskManager
+++++++++++++++++++
A TaskManager manages inter-task dependencies and dispatches ready tasks
to their respective dispatcher. There is a dispatcher for CPU tasks and
GPU tasks assigned to the TaskManager.
TaskManagers are owned and created by the SDK. Each PxScene will allocate
its own TaskManager instance which users can configure with dispatchers
through either the PxSceneDesc or directly through the TaskManager interface.
+++++++++++++++++++
CpuDispatcher
+++++++++++++++++++
The CpuDispatcher is an abstract class the SDK uses for interfacing with the
application's thread pool. Typically, there will be one single
CpuDispatcher for the entire application, since there is rarely a need
for more than one thread pool. A CpuDispatcher instance may be shared by
more than one TaskManager, for example if multiple scenes are being used.
PhysX includes a default CpuDispatcher implementation, but we prefer
applications to implement this class themselves so PhysX and APEX can
efficiently share CPU resources with the application.
.. note::
The TaskManager will call CpuDispatcher::submitTask() from either the
context of API calls (aka: scene::simulate()) or from other running
tasks, so the function must be thread-safe.
An implementation of the CpuDispatcher interface must call the following two methods on each submitted task for it to be run correctly::
baseTask->run(); // optionally call runProfiled() to wrap with PVD profiling events
baseTask->release();
The PxExtensions library has default implementations for all dispatcher types, the following code snippets are taken from SampleBase and show how the default dispatchers are created.
*mNbThreads* which is passed to *PxDefaultCpuDispatcherCreate* defines how many worker threads the CPU dispatcher will have.::
PxSceneDesc sceneDesc(mPhysics->getTolerancesScale());
[...]
// create CPU dispatcher which mNbThreads worker threads
mCpuDispatcher = PxDefaultCpuDispatcherCreate(mNbThreads);
if(!mCpuDispatcher)
fatalError("PxDefaultCpuDispatcherCreate failed!");
sceneDesc.cpuDispatcher = mCpuDispatcher;
[...]
mScene = mPhysics->createScene(sceneDesc);
.. note::
Best performance is usually achieved if the number of threads is less than or equal to the available hardware threads of the platform you are running on,
creating more worker threads than hardware threads will often lead to worse performance. For platforms with a single execution core, the CPU dispatcher
can be created with zero worker threads (PxDefaultCpuDispatcherCreate(0)). In this case all work will be executed on the thread that calls PxScene::simulate(),
which can be more efficient than using multiple threads.
.. note::
CudaContextManagerDesc support appGUID now. It only works on release build. If your application employs PhysX modules that use CUDA you need to use a GUID so that patches for
new architectures can be released for your application. You can obtain a GUID for your application from NVIDIA. The application should log the failure into a file which can be
sent to NVIDIA for support.
+++++++++++++++++++++++++++++++++++++++
CpuDispatcher Implementation Guidelines
+++++++++++++++++++++++++++++++++++++++
After the scene's TaskManager has found a ready-to-run task and submitted it to the
appropriate dispatcher it is up to the dispatcher implementation to decide how and
when the task will be run.
Often in game scenarios the rigid body simulation is time critical and the goal is to
reduce the latency from simulate() to the completion of fetchResults(). The lowest
possible latency will be achieved when the PhysX tasks have exclusive access to CPU resources
during the update. In reality, PhysX will have to share compute resources with other application tasks.
Below are some guidelines to help ensure a balance between throughput and latency when mixing
the PhysX update with other work.
- Avoid interleaving long running tasks with PhysX tasks, this will help reduce latency.
- Avoid assigning worker threads to the same execution core as higher priority threads. If a PhysX task is context switched during execution the rest of the rigid body pipeline may be stalled, increasing latency.
- PhysX occasionally submits tasks and then immediately waits for them to complete, because of this, executing tasks in LIFO (stack) order may perform better than FIFO (queue) order.
- PhysX is not a perfectly parallel SDK, so interleaving small to medium granularity tasks will generally result in higher overall throughput.
- If your thread pool has per-thread job-queues then queuing tasks on the thread they were submitted may result in more optimal CPU cache coherence, however this is not required.
For more details see the default CpuDispatcher implementation that comes as part of the
PxExtensions package. It uses worker threads that each have their own task queue and steal
tasks from the back of other worker's queues (LIFO order) to improve workload distribution.
+++++++++++++++++++
BaseTask
+++++++++++++++++++
BaseTask is the abstract base class for all task types. All task
run() functions will be executed on application threads, so they
need to be careful with their stack usage, use a little stack as
possible, and they should never block for any reason.
+++++++++++++++++++
Task
+++++++++++++++++++
The Task class is the standard task type. Tasks must be submitted to the
TaskManager each simulation step for them to be executed. Tasks may be
named at submission time, this allows them to be discoverable. Tasks
will be given a reference count of 1 when they are submitted, and the
TaskManager::startSimulation() function decrements the reference count
of all tasks and dispatches all Tasks whose reference count reaches zero.
Before TaskManager::startSimulation() is called, Tasks can set
dependencies on each other to control the order in which they are dispatched.
Once simulation has started, it is still possible to submit new tasks and
add dependencies, but it is up to the programmer to avoid race hazards.
You cannot add dependencies to tasks that have already been dispatched,
and newly submitted Tasks must have their reference count decremented
before that Task will be allowed to execute.
Synchronization points can also be defined using Task names. The
TaskManager will assign the name a TaskID with no Task implementation.
When all of the named TaskID's dependencies are met, it will decrement
the reference count of all Tasks with that name.
APEX uses the Task class almost exclusively to manage CPU
resources. The ApexScene defines a number of named Tasks that the
modules use to schedule their own Tasks (ex: start after LOD
calculations are complete, finish before the PhysX scene is stepped).
+++++++++++++++++++
LightCpuTask
+++++++++++++++++++
LightCpuTask is another subclass of BaseTask that is explicitly
scheduled by the programmer. LightCpuTasks have a reference count of 1
when they are initialized, so their reference count must be decremented
before they are dispatched. LightCpuTasks increment their continuation
task reference count when they are initialized, and decrement the
reference count when they are released (after completing their run() function)
PhysX 3.x uses LightCpuTasks almost exclusively to manage CPU
resources. For example, each stage of the simulation update may consist
of multiple parallel tasks, when each of these tasks has finished execution
it will decrement the reference count on the next task in the update chain.
This will then be automatically dispatched for execution when its reference
count reaches zero.
.. note::
Even when using LightCpuTasks exclusively to manage CPU resources, the
TaskManager startSimulation() and stopSimulation() calls must be made
each simulation step to keep the GpuDispatcher synchronized.
The following code snippets show how the crabs' A.I. in SampleSubmarine is run as a CPU Task. By doing so the Crab A.I. is run as a background Task in parallel with the PhysX simulation update.
For a CPU task that does not need handling of multiple continuations *LightCpuTask* can be subclassed. A *LightCpuTask* subclass requires that the getName and a run method be defined::
class Crab: public ClassType, public physx::PxLightCpuTask, public SampleAllocateable
{
public:
Crab(SampleSubmarine& sample, const PxVec3& crabPos, RenderMaterial* material);
~Crab();
[...]
// Implements LightCpuTask
virtual const char* getName() const { return "Crab AI Task"; }
virtual void run();
[...]
}
After PxScene::simulate() has been called, and the simulation started, the application calls removeReference() on each Crab task, this in turn causes it to be submitted to the CpuDispatcher for update. Note that it is also possible to submit tasks to the dispatcher directly (without manipulating reference counts) as follows::
PxLightCpuTask& task = &mCrab;
mCpuDispatcher->submitTask(task);
Once queued for execution by the CpuDispatcher, one of the thread pool's worker threads will eventually call the task's run method. In this example the Crab task will perform raycasts against the scene and update its internal state machine::
void Crab::run()
{
// run as a separate task/thread
scanForObstacles();
updateState();
}
It is safe to perform API read calls, such as scene queries, from multiple threads while simulate() is running. However, care must be taken not to overlap API read and write calls from multiple threads. In this case the SDK will issue an error, see :ref:`Threading` for more information.
An example for explicit reference count modification and task dependency setup::
// assume all tasks have a refcount of 1 and are submitted to the task manager
// 3 task chains a0-a2, b0-b2, c0-c2
// b0 shall start after a1
// the a and c chain have no dependencies and shall run in parallel
//
// a0-a1-a2
// \
// b0-b1-b2
// c0-c1-c2
// setup the 3 chains
for(PxU32 i = 0; i < 2; i++)
{
a[i].setContinuation(&a[i+1]);
b[i].setContinuation(&b[i+1]);
c[i].setContinuation(&c[i+1]);
}
// b0 shall start after a1
b[0].startAfter(a[1].getTaskID());
// setup is done, now start all task by decrementing their refcount by 1
// tasks with refcount == 0 will be submitted to the dispatcher (a0 & c0 will start).
for(PxU32 i = 0; i < 3; i++)
{
a[i].removeReference();
b[i].removeReference();
c[i].removeReference();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,177 @@
.. _physxvisualdebugger:
----------------------------
PhysX Visual Debugger (PVD)
----------------------------
===============================
PVD
===============================
.. figure:: ../images/PvdScreenshot.png
:align: center
The PhysX Visual Debugger (PVD) provides a graphical view of the PhysX scene and includes various tools to inspect and visualize variables of every PhysX object. Additionally it can also record and visualize memory and timing data.
PVD can be downloaded from: https://developer.nvidia.com/physx-visual-debugger
Questions regarding the usage of the GUI should all be answered by its detailed built-in help.
===============================
Basic Setup (SDK Side)
===============================
PVD integration is enabled in the debug, checked and profiling configurations of the SDK. In order to reduce memory footprint and code size, it is not enabled in the release configuration.
The SDK outputs the PVD debugging data in form of a stream. PVD supports reading the stream either from a TCP/IP network socket or from a file.
+++++++++++++++++++
Network Setup
+++++++++++++++++++
Streaming to TCP/IP is supported on almost all platforms, and is usually the most convenient way to collect PVD data. In this mode the stream can be watched in real-time, depending on network speed and scene complexity. In network mode PVD acts as a TCP/IP server and must therefore be launched before the SDK tries to connect to it. The default listening port is 5425::
use namespace physx;
PxPvd* pvd = PxCreatePvd(*foundation);
PxPvdTransport* transport = PxDefaultPvdSocketTransportCreate(PVD_HOST, 5425, 10);
pvd->connect(*transport,PxPvdInstrumentationFlag::eALL);
PxPhysics* physics = PxCreatePhysics(PX_PHYSICS_VERSION, *gFoundation, PxTolerancesScale(), true, pvd);
//After releasing PxPhysics, release the PVD
physics->release();
pvd->release();
transport->release();
+++++++++++++++++++
File Setup
+++++++++++++++++++
Streaming to file is an alternative to network streams. This is the recommended fall-back in case your platform or system setup does not support a network connection to PVD. File streams are often faster than network sockets and therefore a good alternative if performance is more important than real-time viewing.
Streams stored as files can be loaded by drag&drop or over the File->Load menu in PVD::
use namespace physx;
PxPvd* pvd = PxCreatePvd(*foundation);
PxPvdTransport* transport = PxDefaultPvdFileTransportCreate(filename);
pvd->connect(*transport,PxPvdInstrumentationFlag::eALL);
PxPhysics* physics = PxCreatePhysics(PX_PHYSICS_VERSION, *gFoundation, PxTolerancesScale(),true, pvd);
//After releasing PxPhysics, release the PVD
physics->release();
pvd->release();
transport->release();
===============================
Advanced Setup
===============================
+++++++++++++++++++
Connection Flags
+++++++++++++++++++
To optimize the stream size we provide flags to enable specific features. This has both influence on PVD's and the SDK's performance:
* **PxPvdInstrumentationFlag::eDEBUG**: Transfer all debug data to visualize and inspect objects. This flag has usually the biggest impact on the stream's size.
* **PxPvdInstrumentationFlag::ePROFILE**: Transfer timing information of various profiling zones in our SDK.
* **PxPvdInstrumentationFlag::eMEMORY**: Transfer memory usage data of our SDK.
Setup to transfer only profiling data over network::
pvd->connect(*transport, PxPvdInstrumentationFlag::ePROFILE);
+++++++++++++++++++++++++++++++++++++++
Visualizing Externals and Extended Data
+++++++++++++++++++++++++++++++++++++++
Joints are implemented as an extension to the SDK constraints and therefore need special handling to get transmitted to PVD.
Both joint and contact data can increase the stream size significantly. Visualizing it in PVD is therefore disabled by default. To enable them use following API calls::
mScene->getScenePvdClient()->setScenePvdFlags(PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS | PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES | PxPvdSceneFlag::eTRANSMIT_CONTACTS);
or set the flags separately::
mScene->getScenePvdClient()->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS, true);
+++++++++++++++++++++++++++++++++++++++
Visualizing SceneQuery
+++++++++++++++++++++++++++++++++++++++
Visualizing SceneQuery in PVD is disabled by default since queries and hits data can increase the stream size significantly. To enable it use following API calls::
mScene->getScenePvdClient()->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES, true);
+++++++++++++++++++
Custom PvdClient
+++++++++++++++++++
Implement the PvdClient interface if your application needs to react upon connection or disconnection from PVD, or if you plan to send custom PVD events from your application.
It is recommended to toggle the contact and constraint visualization in the onPvdConnected/onPvdDisconnected callbacks to avoid potential memory and compute overhead in the SDK::
// derive from PvdClient
struct MyPvdClient : public physx::pvdsdk::PvdClient
{
virtual void onPvdConnected()
{
// 1. create a PvdDataStream
// 2. send your custom PVD class descriptions from here
// this then allows PVD to correctly identify and represent
// custom data that is sent from your application to a PxVisualDebuggerConnection.
// example in JointConnectionHandler
// 3. do something when successfully connected
// e.g. enable contact and constraint visualization
}
virtual void onPvdDisconnected()
{
// handle disconnection, release PvdDataStream
// e.g. disable contact and constraint visualization
}
//impleament other methods
...
};
// register custom handler
MyPvdClient myPvdClient;
pvd->addClient(myPvdClient);
+++++++++++++++++++
PVD Error Stream
+++++++++++++++++++
PhysX SDK sends all its own error messages to PVD if PVD is connected.
In addition, you can call Ps::Foundation::error() or Ps::Foundation::getErrorHandler()::reportError() to report your error message. These functions will send error messages to PVD automatically.
The messages will be listed in ErrorStream view of PVD.
+++++++++++++++++++
Custom profiling
+++++++++++++++++++
When using PxPvdInstrumentationFlag::ePROFILE, PVD internally calls PxSetProfilerCallback() to set itself up as the current profiler. This happens during the PxPvd::connect() call, and it overrides the potentially already existing profiler callback. That is, if users call PxSetProfilerCallback() with their own user profiler callback, and then initialize PVD with PxPvdInstrumentationFlag::ePROFILE, then the user profiler callback is lost. Similarly, initializing PVD first then calling PxSetProfilerCallback() will make PVD's profiling results vanish.
In case both PVD's internal profiling and a user's custom profiling are needed at the same time, it is recommended to initialize PVD first, then call PxSetProfilerCallback() with your own profiler. In your implementation, call the PVD profiling functions manually, before or after performing your own profiling operations::
struct UserProfilerCallback : public PxProfilerCallback
{
PxPvd* mPvd;
virtual void* zoneStart(const char* eventName, bool detached, uint64_t contextId)
{
// Do custom profiling here
// Then re-route to PVD implementation
return mPvd->zoneStart(eventName, detached, contextId);
}
virtual void zoneEnd(void* profilerData, const char* eventName, bool detached, uint64_t contextId)
{
// Do custom profiling here
// Then re-route to PVD implementation
mPvd->zoneEnd(profilerData, eventName, detached, contextId);
}
};
This is illustrated in SnippetCustomProfiler.