天天看點

PhysX3 User Guide

http://www.cnblogs.com/mumuliang/archive/2011/06/02/2068453.html

1 The SDK Object   首先用全局函數 PxCreatePhysics建立一個PxPhysics對象。

PhysX3 User Guide

#include  " PxPhysicsAPI.h "

bool recordMemoryAllocations =  true;

static PxDefaultErrorCallback gDefaultErrorCallback;

static PxDefaultAllocator gDefaultAllocatorCallback;

mSDK = PxCreatePhysics(PX_PHYSICS_VERSION, gDefaultAllocatorCallback, gDefaultErrorCallback, PxTolerancesScale(), recordMemoryAllocations );

if(!mSDK)

        fatalError( " PxCreatePhysics failed! ");

PhysX3 User Guide

這部分和SDK 2.x稍微有些不同,必須設定allocator callback和error callback. physx3的extensions提供了一組預設的 PxDefaultAllocator和 PxDefaultErrorCallback。

倒數第二個參數,PxTolerancesScale, 是建立場景中的物體的預設Tolerances。

最後一個bool,true表示可以 memory usage debugging 。

 2 The Allocator Callback     The allocator callback是需要自己實作的動态記憶體管理的接口類, PxAllocatorCallback。physx實作了一個, PxDefaultAllocator。

PhysX3 User Guide

#include <malloc.h>

class PxDefaultAllocator :  public PxAllocatorCallback

{

         void* allocate(size_t size,  const  char*,  const  char*,  int)

        {

                 return _aligned_malloc(size,  16);

        }

         void deallocate( void* ptr)

        {

                _aligned_free(ptr);

        }

};

PhysX3 User Guide

Note 與2.x不同的是,3.要求配置設定的記憶體是16位對齊(We now require that the memory that is returned be 16-byte aligned! )。windows上可以用 _aligned_malloc. 控制台系統(console systems)上的malloc()傳回的已經是16位對其的空間,是以不許特别處理。

If you want to track the SDK’s use of dynamic memory, you can put your own custom instrumentation code into the allocate and deallocate functions.

If you are curious about the three unused parameters of allocate, you can refer to PxAllocatorCallback::allocate to find out more about them. Basically, we support a system of named allocations which let us identify types of memory allocation so that you can allocate them from special heaps. The last two parameters are the __FILE__ and __LINE__ inside the SDK code where the allocation was made.

 3 The Error Callback    the error callback也是要自己實作的一個接口 PxErrorCallback。這個接口類隻需要實作一個方法, reportError. physx3也已經實作了一個。

PhysX3 User Guide

class PxDefaultErrorCallback :  public PxErrorCallback

{

public:

        PxDefaultErrorCallback();

        ~PxDefaultErrorCallback();

         virtual  void reportError(PxErrorCode::Enum code,  const  char* message,  const  char* file,  int line);

};

void PxDefaultErrorCallback::reportError(PxErrorCode::Enum e,  const  char* message,  const  char* file,  int line)

{

         const  char* errorCode = NULL;

         switch (e)

        {

         case PxErrorCode::eINVALID_PARAMETER:

                errorCode =  " invalid parameter ";

                 break;

         case PxErrorCode::eINVALID_OPERATION:

                errorCode =  " invalid operation ";

                 break;

         case PxErrorCode::eOUT_OF_MEMORY:

                errorCode =  " out of memory ";

                 break;

         case PxErrorCode::eDEBUG_INFO:

                errorCode =  " info ";

                 break;

         case PxErrorCode::eDEBUG_WARNING:

                errorCode =  " warning ";

                 break;

         default:

                errorCode =  " unknown error ";

                 break;

        }

        printf( " %s (%d) : ", file, line);

        printf( " %s ", errorCode);

        printf( "  : %s\n ", message);

}

PhysX3 User Guide

 4 Cooking and Extensions   PhysX有倆可選庫供使用:extensions 和 cooking. 應該在建立了SDK(也就是PxPhysics)對象後要首先初始化他們。他倆都需要一個東東,SDK的foundation對象。 if (!PxInitExtensions(*mSDK))

        fatalError( " PxInitExtensions failed! ");

mCooking = PxCreateCooking(PX_PHYSICS_VERSION, &mSDK->getFoundation(), PxCookingParams());

if (!mCooking)

        fatalError( " PxCreateCooking failed! "); PxCreateCooking的第三個參數是可以設定的, 為了省事兒這兒也有預設值。

 5 The Scene   PxScene是PhysX的展現. 這個必須有。

PhysX3 User Guide

static PxDefaultSimulationFilterShader gDefaultFilterShader;

PxScene* mScene;

PxSceneDesc sceneDesc(mSDK->getTolerancesScale());

sceneDesc.gravity = PxVec3( 0.0f, - 9.81f,  0.0f);

customizeSceneDesc(sceneDesc);

if(!sceneDesc.cpuDispatcher)

{

        mCpuDispatcher = PxDefaultCpuDispatcherCreate(mNbThreads);

         if(!mCpuDispatcher)

                fatalError( " PxDefaultCpuDispatcherCreate failed! ");

        sceneDesc.cpuDispatcher = mCpuDispatcher;

}

if(!sceneDesc.filterShader)

        sceneDesc.filterShader  = &gDefaultFilterShader;

#ifdef PX_WINDOWS

if(!sceneDesc.gpuDispatcher && mCudaContextManager)

{

        sceneDesc.gpuDispatcher = mCudaContextManager->getGpuDispatcher();

}

#endif

mScene = mSDK->createScene(sceneDesc);

if (!mScene)

        fatalError( " createScene failed! ");

PhysX3 User Guide

PxScene的屬性在建立時設定,建立後不能修改,PxSceneDesc就是描述其屬性的結構體. 

PxDefaultCpuDispatcherCreate 傳回一個預設的 CpuDispatcher對象. 線程數俺們設成1。當然這個 CpuDispatcher也是可以自己去實作的。

PxDefaultSimulationFilterShader ,又一個physx3的預設對象,實作接口 PxSimulationFilterShader.

 6 Basic Actors  

空場景有了,接下來添加物體。所有物體都必須有材質,是以俺們先造個材質 PxMeterial。 PxMaterial*     mMaterial;

mMaterial = mSDK->createMaterial( 0.5f,  0.5f,  0.1f);      // static friction, dynamic friction, restitution

if(!mMaterial)

        fatalError( " createMaterial failed! "); 材質定義了物體的摩擦力和慣性系數(friction and restitution coefficients )。  

然後就可以用這個材質建立個最簡單的static geometry,地面。 

PhysX3 User Guide

PxReal d =  0.0f;

PxU32 axis =  1;

PxTransform pose;

if(axis ==  0)

        pose = PxTransform(PxVec3(d,  0.0f,  0.0f));

else  if(axis ==  1)

        pose = PxTransform(PxVec3( 0.0f, d,  0.0f),PxQuat(PxHalfPi, PxVec3( 0.0f,  0.0f,  1.0f)));

else  if(axis ==  2)

        pose = PxTransform(PxVec3( 0.0f,  0.0f, d), PxQuat(-PxHalfPi, PxVec3( 0.0f,  1.0f,  0.0f)));

PxRigidStatic* plane = mSDK->createRigidStatic(pose);

if (!plane)

        fatalError( " create plane failed! ");

PxShape* shape = plane->createShape(PxPlaneGeometry(), *mMaterial);

if (!shape)

        fatalError( " create shape failed! ");

mScene->addActor(*plane);

PhysX3 User Guide

axis的部分給出了分别設定x、y、z方向為豎直方向的做法。(Physx預設姿态?是,X正方向。)

第一個PxRigidStatic actor就醬(sdk調用 createRigidStatic())建立了, 然後往裡扔了一個平面shape, 這個shape使用了前邊兒建立的material。最後,把這個static actor添加到場景中。 

再使用extensions中的全局函數 PxCreateDynamic()建立一個盒子. 這個函數會把shape和actor一并建立,并根據傳入的密度density計算其品質mass和慣性inertia。

PhysX3 User Guide

PxReal density =  1.0f;

PxTransform(PxVec3( 0.0f,  5.0f,  0.0f), PxQuat::createIdentity());

PxVec3 dimensions( 1.0f,  1.0f,  1.0f);

PxBoxGeometry geometry(dimensions);

PxRigidDynamic *actor = PxCreateDynamic(*mSDK, transform, geometry, *mMaterial, density);

if (!actor)

        fatalError( " create actor failed! ");

mScene->addActor(*actor);

PhysX3 User Guide

這兒還是用的那個材質。最後表忘了mScene->addActor()。

 7 The Simulation Loop   場景和其中的物體都建立好了,要讓他們動起來,需要在仿真循環中往前推進一個時間段。blabla...

一個最簡單的仿真循環:

PhysX3 User Guide

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;

}

PhysX3 User Guide

mScene->simulate()的參數是往前推進的時間長度。這個函數極有可能是異步調用的。是以在return的時候可能還沒有算完。

假使這回想在某個圖形引擎中使用PhysX,抓取物體的位置和朝向 (positions and orientations) 以重新整理圖形引擎中對應的資料,可以使用PxShapeExt下的getGlobalPose。 PxTransform newPose = PxShapeExt::getGlobalPose(*mPhysicsShape);

圖形引擎run的時候PhysX的也在run,是以應該詢問simulate有否完成。 mScene->fetchResults( true);

true表示如果還沒完成就block直到完成。在調用fetchResults之前使用getGlobalPose() (或其他擷取狀态的函數) 擷取actor或其他對象的資料,得到的是上一次simulate完成後的資料。

fetchResults, 會調用所有已定義的event callback。參考 ref:callbacks章節。

 8 Shutting Down   PhysX不用了以後,可以手動release。凡是形如 PxCreate... 建立的對象都有release方法,用來銷毀相關的資料。關閉整個場景的話,也同(調用PxPhysicx的release)。 mSDK->release();

 9 PhysX Visual Debugger Support   PVD (PhysX Visual Debugger) 可直覺的檢視仿真場景中的情形。

PhysX一次隻能連接配接?一個debugger, PvdConnectionFactoryManager管理這些連接配接. 

有幾種方式連接配接debugger. 最簡單的方法是使用a network stream. extensions提供了一個功能函數 PxExtensionVisualDebugger::connect. 調用是要確定debugger已經運作. 

另一種可能更高效的方式把debug資訊寫到一個檔案,然後用debugger打開。 PvdConnectionFactoryManager 類提供了一個完成該功能的函數。

略 

PhysX3 User Guide 02 - Shape

Posted on 2011-06-02 16:07 mumuliang 閱讀( 1658) 評論( 4) 編輯 收藏 Shape是PxGeometry的展現。一個Shape包括:PxGeometry, PxMetarial,和shape相對它所屬的actor的pose(orientation and positionActor),肯定是這個PxActor建立的。多個shape可以湊成一組(成為一個Compound)在同一個Actor中。   和PhysX 2.x類似, 建立Shape是需定義一個PxGeometry. 也就是說每一個Shape都有它自己的PxGeometry類型. PhysX既有的PxGeometry如下.

1)for static and dynamic Actors:Spheres, Capsules, Boxes, ConvexMeshes

2)for static Actors only:  Planes, HeightFields, TriangleMeshes

 (although TriangleMeshes can be used as kinematics in scene queries)

建立 simulation object or Shape 的步驟:

  1. 用SDK對象建立一個Actor,  (要給出它的pose (orientation and position))。
  2. 建立Shape需要的Geometry
  3. Actor建立Shape,們。(别忘了材質和shape的相對pose)
  4. 更新mass、inertia 等屬性。 (PhysX Extensions中有相關函數)
  5. 向Scene添加Actor。

 1 Spheres  

在某位置以某初始化速度扔出一個球體。 首先,俺們需要一個dynamic的actor PxRigidDynamic* aSphereActor = thePhysics->createRigidDynamic(PxTransform(position));  

然後是一個shape。描述sphere的geomety隻需要半徑足夠了,第二個材質是必須要的。

PxShape* aSphereShape = aSphereActor->createShape(PxSphereGeometry(radius), aMaterial);  

Actor加完所有的shape以後,就計算mass和inertia。

PxRigidBodyExt::updateMassAndInertia(*aSphereActor, sphereDensity);   

用Shape設定初始速度,這是個linear velocity vector:

aSphereShape->setLinearVelocity(velocity);

将Actor加到場景中。 aScene->addActor(aSphereActor);

 2 Capsules  

和Sphere相比, Capsule還需要一個height.   注意,初始化Capsule設定的是half height。建立Shape和Actor的方式同Sphere,略。 PxTransform pose;

pose.q = PxQuat(PxHalfPi, PxVec( 0, 0, 1));

PxShape* aCapsuleShape = aCapsuleActor->createShape(PxCapsuleGeometry(radius, halfHeight), aMaterial, pose);  

capsule的height預設是y方向的,要讓Capsule沿X方向延伸,将其繞Z軸旋轉90度。

 3 Boxes  

描述一個Box的隻需要3個參數。 PxShape* aBoxShape = aBoxActor->createShape(PxBoxGeometry(a/ 2, b/ 2, c/ 2), aMaterial);  

a, b , c代表其邊長。

 4 Convex Meshes  

使用頂點vertex來描述Convex Mesh。建立一個金字塔形試試看. 首先定義其邊緣的頂點(extreme verteices)。 static  const PxVec3 convexVerts[] = {PxVec3( 0, 1, 0),PxVec3( 1, 0, 0),PxVec3(- 1, 0, 0),PxVec3( 0, 0, 1),PxVec3( 0, 0,- 1)};  

然後描述面上的點:

PxConvexMeshDesc convexDesc;

convexDesc.points.count =  5;

convexDesc.points.stride =  sizeof(PxVec3);

convexDesc.points.data = convexVerts;

convexDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX;  

有了這些點PhysX SDK就可以計算出其餘相關的資料。俺們把這個過程叫做cooking。接下來叫你如何用PhysX cook a Convex Mesh,and it is done through a Stream objects。

PxCooking* cooking = PxCreateCooking(PX_PHYSICS_VERSION, thePhysics->getFoundation(), PxCookingParams());

MemoryWriteBuffer buf;

bool status = cooking->cookConvexMesh(convexDesc, buf);

PxConvexMesh* convexMesh = thePhysics->createConvexMesh(MemoryReadBuffer(buf.data));

cooking->release();  

現在有了ConvexMesh,就可以用它建立一個Convex的Shape了。

PxShape* aConvexShape = aConvexActor->createShape(PxConvexMeshGeometry(convexMesh, PxMeshScale()), aMaterial, aTransform); PxConvexMeshGeometry的第二個參數用以控制ConvexMesh的縮放。  

 5 Planes  

Plane把空間分成以上和以下。凡是低于plane物體就會和它碰撞. 俺們常用它來建立地面或者模拟的世界的邊界。Plane這種Geometry根本不需要參數.隻要把它放到某處就可以了。它是static的。當然是static。 PxRigidStatic* aPlaneActor = thePhysics->createRigidStatic(pose);

PxShape* aPlaneShape = aPlaneActor->createShape(PxPlaneGeometry(), aMaterial);

 6 Heightfields  

如名, 地形就可以用方形網格采樣的高度值描述。 PxHeightFieldSample* samples = (PxHeightFieldSample*)alloc( sizeof(PxHeightFieldSample)*(numRows*numCols));  

每個樣本(方格子)都有一個16位的整數值和2個材質(一個方格劃分成2個三角面)。這是一種特殊的已經定義了的材質,PxHeightFieldMaterial::eHOLE. 如果你用float型描述高度(也木有關系?),there is the possibility to set the used scale later on with the Geometry. Since there are two ways to split a rectangle into two triangles, the split diagonal can be selected with the tesselation flag.

當然,還要知道每個方向有多少樣本高度。 PxHeightFieldDesc hfDesc;

hfDesc.format = PxHeightFieldFormat::eS16_TM;

hfDesc.nbColumns = numCols;

hfDesc.nbRows = numRows;

hfDesc.samples.data = samples;

hfDesc.samples.stride =  sizeof(PxHeightFieldSample);  

目前隻支援提及的格式。eS16_TM?

Heightfields不需要cooking, 但也有一個内部對象需要初始化: PxHeightField* aHeightField = thePhysics->createHeightField(hfDesc);  

接下來就可以用Geometry建立shape:

PxHeightFieldGeometry hfGeom(aHeightField, PxMeshGeometryFlags(), heightScale, rowScale, colScale);  

Heightfield也是static的.

PxRigidStatic* aHeightFieldActor = mSDK->createRigidStatic(pose);

PxShape* aHeightFieldShape = aHeightFieldActor->createShape(hfGeom, aMaterial);  

如果是 multi-material的heightfields, 需用另一個函數來create Shape

PxShape* aHeightFieldShape = aHeightFieldActor->createShape(hfGeom, aMaterialArray, nbMaterials);  

 7 Triangle Meshes  

也是要先有三角面的頂點vertices,然後像convexes一樣,需要cooking。

PhysX3 User Guide

PxTriangleMeshDesc meshDesc;

meshDesc.points.count = nbVerts;

meshDesc.triangles.count = nbFaces;

meshDesc.points.stride =  4* 3;

meshDesc.triangles.stride =  4* 3;

meshDesc.points.data = verts;

meshDesc.triangles.data = indices;

PxCooking* cooking = PxCreateCooking(PX_PHYSICS_VERSION, thePhysics->getFoundation(), PxCookingParams());

MemoryWriteBuffer buf;

bool status = cooking->cookTriangleMesh(meshDesc, buf);

PxTriangleMesh* triangleMesh = thePhysics->createTriangleMesh(MemoryReadBuffer(buf.data));

cooking->release();

PhysX3 User Guide

strides表示points和triangle都是3個一組的4位元組長的資料。   

Triangle meshe是static. 但是可以用作kinematics。(譬如水面?)

PxRigidStatic* aTriMeshActor = thePhysics->createRigidStatic(pose);

PxShape* aTriMeshShape = aTriMeshActor->createShape(PxTriangleMeshGeometry(triangleMesh), aMaterial);  

Triangle meshe也可以縮放. 

索引可能是32bit也可以是16bit,視mesh的三角數目而定。用PxTriangleMesh::has16BitTriangleIndices()可以檢視其具體情況.

 8 Compound Shapes 

Compound組合了多個Shape到一個Actor中. 要建立一個Compound Shap隻需多次調用PxRigidActor::createShape()即可。隻是不要忘記最後要計算mash和intertia什麼的。 在North Pole Sample中的雪人就是compound對象. compound到一起的shape之間有些是可以分離的。

例如雪人中,有些會被雪球砸中分開。這需要設定一個PxSimulationFilterShader的pairFlags. 然後在可分離的shape被擊中後在simulation filter function中做如下工作:

if (needsContactReport(filterData0, filterData1))

{

pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND;

}  

needsContactReport 是一個用來在每個shape的simulationFilterData中測試flags的工具函數helper function. 這些flags是一早在建立Shape時用setDetachable和setSnowball設定的。Contact notification is requested if one of the Shapes is detachable and the other is marked as a snowball. (迷惑 =“=)

That way, the pair will be passed to the PxSimulationEventCallback::onContact() function which is implemented in the Sample. All we want to do here is to remember which detachable Shapes were touched in this frame so far. After simulation, we can iterate over that list again and actually detach the according Shapes from their respective Actor. Note that the Shape has a local (in Actor space) pose and the Actor has a world pose. Furthermore, a PxShape cannot exist on its own, it is always bound to an Actor. That means, that when you detach a Shape you have to provide a new Actor for it to continue to exist. The new Actor will have the global pose of the old Shape (in the Compound Actor) and the new local pose is identity. There is a helper extension to calculate the global pose of a Shape. The Geometry and Material needed to create the copy of the Shape in the new Actor can be retreived from the detaching Shape.

PhysX3 User Guide

PxShape* shape = <theDetachingShapeYouKnow>

PxTransform pose = PxShapeExt::getGlobalPose(*shape);

PxRigidDynamic* newActor = mSDK->createRigidDynamic(pose);

PxMaterial* mat;

shape->getMaterials(&mat, 1);

PxConvexMeshGeometry convexGeom;

if(shape->getConvexMeshGeometry(convexGeom))

{

PxShape* newShape = newActor->createShape(convexGeom,*mat);

PxRigidBodyExt::updateMassAndInertia(*newActor, 1);

aScene->addActor(*newActor));

newActor->addForce(PxVec3( 0,. 1, 0),PxForceMode::eFORCE);

shape->release();

}

PhysX3 User Guide

Obviously, you need several PxGeometry types and a selection code if you don’t know in advance which kind of Shape you are going to detach. As usual the mass properties need to be set after adding Shapes to an Actor. And eventually you can release the old detaching Shape completely.

Note here that you can only change dynamic properties of an Actor after it has been added to a Scene. Kinematic properties like velocities can be set before that though.

 9 Mass Computation  

恩,看出來了,actor的mass是需要計算的。用它, PxRigidBodyExt::updateMassAndInertia(). 它會計算和設定

  1. actor的mass
  2. 重心,center of mass
  3. 最大靜摩擦力? the principal moments of inertia

To illustrate different mass properties we will look at the Wobbly Snowmen in the North Pole Sample. The principle of a roly-poly toy is the low center of mass, moving the object back in upright position after it has been tilted. Usually, most of the body is just an empty shell, and the bottom is filled with some heavy material. The Wobbly Snowmen come in different flavors, depending on how the mass properties are set: The first Snowman is basically mass-less. 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 example 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 is by no means physically correct, but we only approximate things here. The resulting Snowman feels a bit more filled. The third and fourth example use all basic 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 Snowmans mass properties are set up manually. After constructing the compound, we set the mass and center of mass. For the moments of inertia, which tell us ‘how much resistance there is to change the orientation about an axis’, we use rough values which create the desired behavior (of course, for real you would integrate the moments properly!). The Snowman shall wobble back and forth only, which is around the X axis in this case. The resulting tensor diagonal values will have a small value in X, and high values in Y and Z: There is small resistance to rotate about X, but a high resistance to rotate about Y and Z.

PhysX3 User Guide 03 - Joint

Posted on 2011-06-02 16:28 mumuliang 閱讀( 1798) 評論( 1) 編輯 收藏  1 Joint Creation   Joint提供了一種聯系倆Actor的途徑。典型的例子如門上的合頁,人物的肩膀。.

建立Joint的方法: PxRevoluteJointCreate(PxPhysics& physics,

                      PxRigidActor* actor0,  const PxTransform& localFrame0,

                      PxRigidActor* actor1,  const PxTransform& localFrame1);

必須有一個Actor可以動,是PxRigidDynamic,要不就是PxArticulationLink. 另一個Actor無所謂。

localFrame參數表示Actor的相對PxTransform,包括位置和姿态,position and orientation. 上面的例子是一個revolute joint,localFrame須指定原點和旋轉軸。

PhysX提供了6中Joint:

    1. 固定。不能相對運動。a fixed joint locks the orientations and origins rigidly together 
    2. 距離。Actor之間像栓了繩子。a distance joint keeps the origins within a certain distance range 
    3. 球體。原點重合,姿态自由。a spherical joint (sometimes known as as a ball-and-socket) keeps the origins together, but allows the orientations to vary freely. 
    4. 開阖。門的合頁那樣的。a revolute joint (often referred to as a hinge) keeps the origins and x-axes of the frames together, and allows free rotation around this common axis. 
    5. 截面。移門那樣的。a prismatic joint keeps the orientations identical, but allows the origin of one actor to slide freely along the line defined by the origin and x-axis of the other. This kind of joint models a sliding door. 
    6. D6。高度自定義的一類,可以高度自由也可以高度固定。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. 

應用程式可以添加向架構添加新的Joint擴充現有類型。

Note: PhysX中的角度是以弧度為機關。

 2 Beyond the Basics  

  Visualization   所有PhysX的标準joints都支援debug visualization.是以..

但 joint本身是不可見的. 為使其可見, 要将設定其constraint flag為visualization,并改變場景的visualization parameters: scene->setVisualizationParameter(PxVisualizationParameter::eJOINT_FRAMES,  1.0f);

scene->setVisualizationParameter(PxVisualizationParameter::eJOINT_LIMITS,  1.0f);

...

joint->setConstraintFlag(PxConstraintFlag::eVISUALIZATION)

  Force Reporting   Jonit連接配接的Actor每幀都要儲存連接配接它倆的force的資訊。通過設定reporting constraint flag啟用該功能,然後就可以用Joint對象的getForce方法獲得force資訊。 joint->setConstraintFlag(PxConstraintFlag::eREPORTING)

...

scene->fetchResults(...)

joint->getForce(force, torque);

The force is resolved at the origin of actor1’s joint frame.

  Breakage   PhysX的标準joint都是可以被掰開的breakable. 可以定義這個臨界force和torgue。 joint被掰開會引發一個simulate event(參看 PxSimulationEventCallback::onJointBreak), 這個joint盡管可能仍然存在與場景中,但也不再參與運算。

但joint預設是不可掰開的。開啟該屬性,方法如下,首先設定一個臨界force和torgue,然後設定breakable constraint flag為true。 joint->setBreakForce( 100.0f,  100.0f);

joint->setConstraintFlag(PxConstraintFlag::eBREAKABLE,  true);

  Projection   Under stressful conditions, PhysX’ dynamics solver may not be able to accurately enforce the constraints specified by the joint. For joints where this is problematic you can enable kinematic projection, which tries to bring violated constraints back into alignment. Projection is not a physical process and doesn’t preserve momentum or respect collision geometry. It should be avoided where practical, but it can be useful in improving simulation quality where joint separation results in unacceptable artifacts.(這段很迷糊)

在突發情況(有外力情況?)下,PhysX 的dynamics slover(dynamic物體的電腦?)可能會沒辦法正确操作joint定義的Actor之間限制關系。這顯然不太妙。你可以啟用kinematic projection(運動投影?),它會嘗試糾正。但Projection并非實體過程,它不會遵守動量守恒也不會管圖形是否碰撞。實際應用中應該避免使用projection,但它在改善什麼什麼效果時很有用。-_-b

projection也是預設不可用的。啟用方法如下,首先設定會引起joint被project的臨界linear tolerance和angular tolerance,然後開啟那啥constraint flag.

joint->setProjectionLinearTolerance( 0.1f);

joint->setConstraintFlag(PxConstraintFlag::ePROJECTION,  true);

Very small tolerance values for projection may result in jittering around the joint.

  Limits   joint限制相關的Actor如何旋轉或隻能沿某方向平移,同時可進一步設定這種可旋轉和可移動的範圍。例如revolute joint,合頁關節,預設是繞軸的任意角度的旋轉,如果需要,你可以開啟limit,然後定義個旋轉的最大角度和最小角度。

Limits可以看做collision碰撞的一種。stable limit需要定義最大最小距離,和一個tolerance。blabla..要說的是limit的模拟開銷是很昂貴的。

設定limit要配置limit的geometry,然後設定joint的limit flag為true。 revolute->setLimit(PxJointLimitPair(-PxPi/ 4, PxPi/ 4,  0.1f));  //  upper, lower, tolerance

revolute->setRevoluteJointFlag(PxRevoluteJointFlag::eLIMIT_ENABLED,  true);

Limit可硬可軟。硬就是讓joint的運動立刻停下來,如果這是limit被設定為non-zero restitution,會被彈回。軟的就是有阻尼效果。預設的limit是不會回彈的hard。

Note: Limits are not projected.

  Actuation   有些joint是被spring和motor驅動的。這種模拟的開銷比簡單用force驅動的要大,它要求有更多的 stable control ,更多細節參看D6和revolute joint的文檔。

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.

  3 Fixed Joint   image:: ../images/fixedJoint.png

The fixed joint has no additional characteristics.

  4 Spherical Joint   image:: ../images/sphericalJoint.png

A spherical joint constrains a point on each actor to be coincident.

The spherical joint supports a cone limit, which constrains the direction of the x-axis of actor1’s constraint frame. The axis of the limit cone is the x-axis of actor0’s constraint frame, and the allowed limit values are the maximum rotation around the 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.

  5 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

  6 Prismatic Joint   image:: ../images/prismaticJoint.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 primatic 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);

  7 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 paramters may be specified.

  8 D6 Joint     Locking and Unlocking Axes   D6是最複雜的标準Joint. 它預設表現的像fixed Joint,兩個物體像黏在一起一樣運動。但使用Joint的 setMotion方法,你可以設定允許旋轉和平移。 d6joint->setMotion(PxD6Axis::eX, PxD6Motion::eFREE);

如果設定了平移自由translational degrees of freedom,即允許actor1的原點沿着actor0的constraint frame定義的軸移動。

旋轉自由Rotational degrees of freedom分為兩種:扭twist (around the x-axis of actor0’s constraint frame);和擺 swing (around the y- and z- axes.) 通過控制twist和swing參數的開關,可以得到很多效果:

  1. 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. 
  2. 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 π radians (180 degrees) swing, so a swing limit should be used to avoid the singularity. 
  3. 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 π/2 radians (90 degrees), making the zero-twist joint a better behaved alternative for most use cases. 
  4. if all three angular degrees are unlocked, the result is equivalent to a spherical joint. 

有三種PhysX2的joint在physX3已經木有了,但你可以醬來實作他們。

  • 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);

  Limits   除了可以定義軸是自由還是鎖定( axis is free or locked), 還可以定義其受限範圍limit. D6有三種limit,并且可以組合使用。

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->setLinearLimit(PxJointLimit( 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 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 π radians (180 degrees). If the other swing degree is free, the maximum value of the limit is π/2 radians (90 degrees.)

  Drives   D6有1個linear drive model, 和2個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 也可以代替force用來引起加速。Acceleration drive 調整比force drive要容易些。

linear drive model 有如下參數: 

  • 目标位置,在actor0的constraint frame中定義。target position, specified in actor0’s constraint frame 
  • 目标速度,也定義在actor0的constraint frame中。target velocity, specified in actor0’s constraint frame 
  • 彈性?spring 
  • 阻尼。damping 
  • 能提供的最大力。forceLimit - the maximum force the drive can apply 
  • 加速标志。acceleration drive flag 

它會以設定的阻尼和倔強系數stiffness?往目标位置運動。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.

如果位置固定目标速度為0, a position drive will spring about that drive position with the specified springing/damping characteristics:

PhysX3 User Guide

//  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::createIdentity());

d6joint->setDriveVelocity(PxVec3::createZero());

PhysX3 User Guide

Angular drive和linear drive完全不同,angular drive木有一種簡單直接的方式用以描述它相對于奇點(singularities)是怎麼自由運動的, 是以D6 joint提供了兩種angular drive models——扭擺 和 球面插值SLERP (Spherical Linear Interpolation).

這倆angular drive model最大的差別在于他們使用四元數的方式。SLERP中直接使用四元數,扭擺模型裡面會吧四元數分解成blabla. 扭擺模型多數情況下的行為都是比較一緻的,但要注意180度的擺動,這時候actor并不會沿着兩個角度之間的最短弧線運動(球體表面的最短路徑). SLERP模型總是會讓actor沿着最短對角弧運動。

隻有有任何一個方向的角度不能改變, SLERP drive 參數就會被忽略。當所有方向上都是角度自由,and parameters are set for multiple angular drives, the SLERP parameters will be used.

The angular drive model has the following parameters:

  • 角速度,定義在actor0的constraint frame。An angular velocity target specified relative to actor0’s constraint frame 
  • 目标姿态。定義在同上。An orientation target specified relative to actor0’s constraint frame 
  • SLERP或扭擺drive的配置。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 - maximum torque applied when driving towards a velocity target (not used for an orientation drive) 
  • 那啥。acceleration drive flag 

Best results will be achieved when the drive target inputs are consistent with the joint freedom and limit constraints. 

PhysX3 User Guide 04 - Rigid Body Dynamics

Posted on 2011-06-03 16:06 mumuliang 閱讀( 1349) 評論( 0) 編輯 收藏 本章涉及了了不少模拟剛體物體必須了解的點。

  Applying Forces and Torques        

實體學上和物體互動的方式常是對其施以外力。在經典力學中,很多物體之間的互動都是采用力來求解。因為力遵守如下法則:

f = m*a (force = mass * acceleration) 力= 品質*加速度 0-0

力直接控制物體的加速度,間接影響其位置和速度。是以,如果你需要立刻獲得反應的話,使用力Force可能不太友善。力的好處是無需考慮場景中受力物體Body的情況,模拟程式會計算定義所有的限制條件。重力就是這麼幹的。

不幸的是,大量力會導緻共振,使速度越來越大,最後系統solver因為無法maintain the joint constraints而崩潰。不像現實世界,joint break就是了。(joint的breakable打開也不行麼?)

同一模拟幀内的力可以累加的,下一幀會全部置零。你可以設定力和力矩torque。 PxRigidBody和 PxRigidBodyExt中的相關函數如下. 

PhysX3 User Guide

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);

PhysX3 User Guide

PxForceMode 預設為 PxForceMode::eFORCE 。除此以外的可能值有: PxForceMode::eIMPULSEs, impulsive force. PxForceMode::eVELOCITY_CHANGE 會無視物體的品質...更多資訊檢視API手冊中的PxForceMode章節.

  Gravity        

重力是最常用的。如果需要在整個場景内都使用重力效果,使用 PxScene類的 setGravity()進行設定。

使用米和秒為機關的話,重力值為9.8,方向為場景的豎直向下。

某些效果可能要求一些dynamic actor暫時不受重力影響(真想不出是啥時候啥情況),你可以這麼幹:

PxActor::setActorFlag(PxActorFlag::eDISABLE_GRAVITY,  true);

Note: 改變重力效果的時候要嚴重當心。記得要手動恢複失重的物體,用 PxRigidDynamic::wakeUp()。因為系統如果要自動喚醒失重物體的話,勢必周遊場景中的所有物體,性能不佳。.

    Setting the Velocity        

如果是想立刻讓一個物體運動,給它初速度是最直接的。還有個更直接的辦法是設動量momentum,如果你不知道物體的品質的話,這種方法可能更友善, 隻是要知道速度的話還得要品質。

場景中設了初速度的物體, 你可能會遇到在第一幀結束以後速度就沒了的情況。這是因為如果這個初速度和其他什麼constraint沖突的話,就會被simulation以其他的設定重寫。例如,如果一個球靜止在桌子上,同時給了它一個向下的初速度,那麼這個初速度就會,變成0. 如果給鍊條上某鍊節一個初速度,這個鍊節上的初速度會減小,其他鍊節的初速度會增大,以保證所有鍊節仍然連接配接在一起.

初速度的設定方法簡單的說,如下:

void PxRigidBody::setLinearVelocity( const PxVec3& linVel,  bool autowake);

void PxRigidBody::setAngularVelocity( const PxVec3& angVel,  bool autowake);

   Kinematic Actors          

dynamic Actor受力force影響,也受沖量impulse影響。通常俺們用 PxRigidBody::addForce()或 PxRigidBody::addTorque()函數的時候,用這倆枚舉量 PxForceMode::eIMPULSE , PxForceMode::eFORCE 對其設定. 但是這些都是在以二手或者三手方式在控制actor,不夠靈活。例如當想要移動舞台和角色的時候,直接改變它們的Actor的位置Position是最直接的。kinematic Acotor就提供了這樣一種一手方式.

使用 PxRigidDynamic::moveKinematic()可以盡情操作kinematic Actor. 除了這個函數Kinematic Actor對不管是力,重力,碰撞啥的,都不起反應。這條指令先被暫存起來,當内部的simulate完成後,它會計算出一個速度,然後在本幀内以該速度向目标位置移動,本幀内的這一步移動完成後,速度會被歸零。是以需要在反複調用該函數才能完成沿着某路徑運動的效果。也就是在移動過程中,kinematic actor表現得像是品質為無窮大,是以總是馬上停下來.

建立kinematic actor的方法隻是在建立一個普通dynamic actor之後打開它的kinematic flag:   PxRigidDynamic* rigidDynamic;

rigidDynamic->setRigidDynamicFlag(PxRigidDynamicFlag::eKINEMATIC,  true);

隻要把這個kinematic flag關閉,actor就又變成一個普通的dynamic actor. 如果你向一堆dynamic中的一個kinematic增加mass時,其實mass并沒有增加,因為這個mass被用在了kinematic上,相當于滄海一粟,這一堆dynamic的總mass也不會變化。

附加說明:

  • 注意 PxRigidDynamic::moveKinematic() 和 PxRigidBody::setGlobalPose() 的差別. setGlobalPose()也會将Actor移動到指定位置,但不會引起其他對象的變化。尤其是,它根本不會阻擋别的物體運動,而是會穿牆而過。但這個setGlobalPose()還是會被用到, 如果僅僅是想把某個東西移動到某個位置時。. 
  • kinematic actor是會推開别人的,如果有人擋道的話,但是其他物體不會反作用于kinematic物體。也就是說kinematic actor的出現,讓其他dynamic actor表現為static actor或者kinematic actor. 譬如,一個dynamic豆腐會把沖過來的kinematic腦袋撞個坑?。
  • kinematic 和 static actor之間木有互相作用和碰撞。 

   Sleeping       

actor若持續一段時間木有動作, 可以看做将來它也不會再動作,除非再受到了外力。是以在下一次受到外力之前,它都不必參與simulate.這種狀态就是Sleeping. 查詢一個物體是不是睡了可以醬:

bool PxRigidDynamic::isSleeping();

actor的動能kinematic energy低于某個值的話就會進入睡眠狀态。這個門檻值可這樣設定:

void PxRigidDynamic::setSleepEnergyThreshold(PxReal threshold);

PxReal PxRigidDynamic::getSleepEnergyThreshold()  const;

一個沉睡物體的醒來是因為有其他醒着的物體touch了它,或者它的什麼屬性發生了變化,睡還是沒睡通常不應該是應用程式關心的問題,也不必。但如果非要人肉改變睡還是不睡,也不是沒有辦法:

void PxRigidDynamic::wakeUp(PxReal wakeCounterValue=PX_SLEEP_INTERVAL);

void PxRigidDynamic::putToSleep();

但是,也有一些特殊情況要求使用者必須手動喚醒Sleeping actor。哪些函數有這樣的要求,API參考文檔有詳情。

    Solver Accuracy        

求解器精确度。當剛體rigid body的運動受到關節jount或和其他物體的接觸contract的限制時,限制求解器constraint solver就會自動運作。求解器為了滿足所有的限制條件,會有限次周遊運動物體上的所有限制條件。這次數越大,求解的結果越精确越接近現實。預設是4個位置和1個速度的周遊(?!?!)。The solver iteration count defaults to 4 position iterations and 1 velocity iteration. 可以單獨設定每個剛體物體的周遊次數:

void PxRigidDynamic::setSolverIterationCounts(PxU32 minPositionIters, PxU32 minVelocityIters);

眼目前俺們隻發現了在一個關聯了太多Joint并且這些joint的tolerance都很小的物體上才有必要把這個值設的比較大。如果該值需要設定到為30以上才能滿足需求,基本上是整個模拟從根子上就有問題。

    Fast Rotation        

像鉛筆這樣的長條形狀的物體模拟起來比較有難度,因為它們繞短軸旋轉時會存儲大量能量,而後若繞長軸旋轉會變成一個很大的角速度。這樣會出問題,因為旋轉運動中的某些線性逼近米有辦法完成。是以PhysX SDK中限制了角速度的最大值。當然這個值也是可以修改的。

void PxRigidDynamic::setMaxAngularVelocity(PxReal maxAngVel);

PhysX3 User Guide 05 - Scene Queries

Posted on 2011-06-04 16:26 mumuliang 閱讀( 1030) 評論( 3) 編輯 收藏

有兩種方式查詢場景中的碰撞collision:批處理batched和非批處理non-batched。非批處理查詢又稱single shot queries,使用PxScene類的接口,是PxScene的一項正常功能。批處理查詢是使用PxBatchQuery對象,PS3上隻有批處理查詢一種方式。

    Raycast queries      

查詢中使用者定義的射線ray會與整個場景相交intersected with the whole scene。PhysX有三種射線:

  • raycastAny
  • raycastSingle
  • raycastMultiple

查詢碰撞collision時,傳回的最主要的資訊是一個布爾是非值,表示碰沒碰上,若需擷取更多資訊,比如射線是不是穿過了什麼之類的,就可以用raycastAny。至于交點的确切資訊、相交的是個啥形狀,這些都不是raycastAny關心的内容,它隻關心一件事,是不是有擊中發生(there was a hit)。人工智能中的視線查詢(line-of-sight queries)就是一個典型應用。

raycastSingle比raycastAny先進一些,通過它可以知道第一個被擊中的是個嘛。譬如說子彈擊中的情形。

raycastMultiple是最進階的。ray會hit到的所有物體都可以得到。好比說穿甲子彈。

注意:* solid物體(sphere, capsule, box, convex)是封閉的closed(即它們包括它們的邊界boundaries)

        * plane是封閉的半空間 closed halfspace

        * heightfield也是封閉的,并且是solid物體。 

射線投射在solid物體上,射線是有終點的。射線ray和solid物體的相交intersection會引發一個擊中報告hit report。

下表顯示了不同物體對射線是否會産生hit report報告的反應是不同的。比如隻要起點位于plane内部,即平面以下,無論射線終點在哪兒,也不會有plane被hit的report傳回。

PhysX3 User Guide

     Sweep Queries     

Queries中Shape會以一指定的方向掃sweep過去,并報告是否與場景Scene有碰撞collision。也有三種sweep方式:

  • sweepAny
  • sweepSingle
  • sweepMultiple

它們的差別也和3種ray之間的差別一樣。 

目前支援sweep的形狀Shape有box, sphere和capsule。

    Overlap Queries     

overlap query傳回的是和場景發生碰撞的物體所接觸到的其他物體。有兩種overlap

  • overapAny 
  • overlapMultiple

overlapAny 也叫placement API,特别适用于隻知道場景的體積是不是空的情況。

overlapMultiple并不會在第一次hit後停下來,而是會傳回其後所有被碰到的物體。

沒有overlapSingle,因為沒必要。overlap并沒固定一個具體的方向,是以也就不能得到某個方向上的最近或最遠,是以single木有存在的意義。(難道時間上也木有排序的意義麼?)

overlap接受box,sphere和capsule三種形狀。

    Filtering     

有幾種方法可以把場景中不需要的Shape過濾掉。query與filter有關的參數有:

  • 結構體PxSceneQueryFilterData,包括PxSceneQueryFilterFlags和PxFilterData
  • PxSceneQueryFilterCallback選項

這些靈活多變的filter機制允許使用者鎖心所欲的自定義過濾行為。俺們來看幾個例子:

首先PxSceneQueryFilterFlag::eSTATIC 和 PxSceneQueryFilterFlag::eDYNAMIC 标志提供了第一層過濾。它們表示query是針對場景中的static還是dynamic對象。如果想query全部static或dynamic對象時,這顯然比一個一個往filtering callback中加對象要友善。例如,爆炸效果可以球體的overlapMultiple來掃全場景中的dynamic對象(打開PxSceneQueryFilterFlag::eDYNAMIC标志),對static對象隻需要給他們加個外力什麼的就可以了。

第二個層次上的過濾由PxFilterData提供,它是一個128位的掩碼。query會拿shape的掩碼跟自己比較,通過才将shape納入本次query。shape通過的情況有兩種:1,query的掩碼為零;2,shape的掩碼和query的掩碼按位與的結果非零(其實非零即等于掩碼,掩碼通常是隻有1位為1)。

filter callback可以建立更靈活的過濾規則。filter callback需實作PxSceneQueryFilterCallback後傳給query。然後場景查詢可以在需要時調用其。調用時機可能是計算碰撞前也可能是其後,視實際情況。當然在計算之前就決定是不是要丢棄shape的開銷是要小一些,但有時候計算以後的結果是你的判斷條件之一。設定callback調用時機使用這倆filter标志:PxSceneQueryFilterFlag::ePREFILTER 或 PxSceneQueryFilterFlag::ePOSTFILTER .

filter callback會傳回一個PxSceneQueryHitType 。該值可能為:

  • eNONE,表示shape不許參與後面的query處理。
  • eBLOCK,表示shape是hit最終結束在的物體blocking hit,凡是比該shape更遠的shape都不必參與query。(從後文來看該shape并沒有被過濾掉,仍然是參與query的。)
  • eTOUCH,表示shape是一個touching hit,參與query處理,除非後續filter callback給它過濾掉了。 

eNONE和eBLOCK比較簡單。NONE就丢棄shape不參與query,BLOCK就将shape加入query。eTOUCH是新增加的值,為了處理像子彈穿過窗戶(打碎窗戶但繼續向前)這樣的情形。顯而易見eTOUCH隻對造成Multiple hits的query有效。

    Caching      

PxSceneQueryCache 可以用來加速某些情況下的query,尤其是raycastAny, raycastSingle 和 sweepSingle .它的工作方式是:在緩存中定義了一些shape,甚至三角面(三角面和shape都會被首先測試)。高度時序性的query會是以得到顯著的效率提升。解決這類時序問題的一個好方法是将上一幀的query結果放在query緩存對象中。

例如,人工智能的可見度query很可能連續幾幀都傳回同一個遮擋物blocking shape。那俺們就可以給到這個raycastAny(視線的query常用racycatAny,前面有講)一個恰當的PxSceneQueryCache,然後呢,query會在檢查其他shape之前就馬上發現它hit了緩存中的這個shape,然後query就傳回了。

再譬如,在緩存中存放上一次的closest hit,這會大大提高query查找closest hit的效率。

    Batched Queries     

批查詢使用的是PxPatchQuery對象的接口。 顧名思義,就是可以把多個Query組合在一起,然後一次執行(調用PxBatchQuery::execute())。批查詢基本上和非批查詢是一樣的。他倆的差別主要是:

  • PS3的SPUs隻能跑批查詢
  • 寫死的過濾方程(hardcoded filtering equation) 不能用于批查詢。取而代之的是兩個filter shader:PxBatchQueryPreFilterShader 和 PxBatchQueryPostFilterShader。

查詢結果先寫入PxPatchQueryDesc中使用者指定的緩沖區,後以同樣的順序輸出到PxPatchQuery對象。

PhysX3 User Guide 06 - Callbacks and Customization

Posted on 2011-06-04 18:49 mumuliang 閱讀( 1235) 評論( 1) 編輯 收藏 本章俺們要看看SDK提供了哪些函數用來監聽模拟事件和自定義部分模拟行為。回調函數需在使用者使用的繼承類中實作。這和第一章談到的自定義配置設定操作allocator和錯誤提示error notification所使用的機制是一樣的。

    Simulation Events        

事件是最簡單的模拟回調。程式可以隻監聽而不做反應。使用者在callback中添加的代碼隻有一個問題:你未必能如願修改SDK中的狀态!(only one restriction? -_-b)背景進行實體模拟時,也可以進行寫操作——這有點意外吧,因為SDK都是雙緩沖形式的,新狀态都是寫入了非活動狀态的後緩沖中。但是,event是在fetchResults()内部被調用的而不是在模拟線程simulation thread,這就有可能後緩沖中有些操作在模拟線程中已經做過了。将來的更新版本可能會基于單個事件在此問題上做一些改進,但目前還是隻有先把需要作為事件結果傳回的寫操作緩存起來,并且在fetchResult()傳回後執行。(糊裡糊塗的#v#)

fetchResults()内部,交換緩存(意味着物體的模拟結果API可見了)的動作并不是在最初或最後,而是在一系列操作的中間,也就是說事件回調被分成了發生在緩存交換之前和之後兩種情況。在之前的有

  • onTrigger 
  • onContactNotify 
  • onConstraintBreak 

收到這些事件event的時候 ,物體的形狀Shape、角色Actor等仍然保持模拟simulate之前的狀态。這樣是對的。因為這些事件的檢測本應在模拟之前。例如,一對引發了onContactNotify()的shape,即使在fetchResult()之後它們可能是被彈開了,但他們也的确那啥了。

位于交換緩存之後的事件有:

  • onSleep 
  • onWake 

Sleep information is updated after objects have been integrated, so that it makes sense to send these events after the swap.

監聽事件有兩步:1,寫一個繼承于 PxSimulationEventCallback的子類,定義需要的回調函數。對Sleep/Wake事件或是限制破壞事件constraint break event, 這是唯一的一個步驟。 

2,onContactNotify 和 onTrigger 事件,還需要在filter shader callback為需要接受該事件的物體設定一個标志。下一節collision filtering會細說。

這是SampleSubmarine工程中的使用contact notify function的例子:

PhysX3 User Guide

void SampleSubmarine::onContactNotify(PxContactPair& pair, PxU32 events)

{

         if(events & PxPairFlag::eNOTIFY_TOUCH_FOUND)

        {

                 if((pair.actors[ 0] == mSubmarineActor) || (pair.actors[ 1] == mSubmarineActor))

                {

                        PxActor* otherActor = (mSubmarineActor == pair.actors[ 0]) ? pair.actors[ 1] : pair.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);

                }

        }

}

PhysX3 User Guide

SampleSubmarine 是 PxContactNotifyCallback 的子類. onContactNotify 方法接收一個contract pair的事件掩碼. 上面的函數隻處理了eNOTIFY_TOUCH_FOUND事件。事實上它隻關心潛艇的這個事件. 然後它會假設第二個Actor是水雷(可能隻激活了潛艇和地雷的contact report). 然後它把這個地雷添加到一組下一次會爆炸的地雷裡面。

    Collision Filtering        

幾乎所有實際應用中,都需要設定不計算某些互相作用的物體,或者讓SDK以某種特殊的方式進行沖突檢測。在潛水艇的例程中,如上文說道的,需要在潛水艇touch到了一個水雷或水雷鍊的時候得到通知be notified,以便引爆它們。再有,鉗爪AI也需要知道它是不碰touch到了heightfield。

在了解例程是咋做的之前,有必要先了解一下SDK的filter系統能做啥。因為潛在的作用對的filtering操作發生在模拟引擎最deepest(深奧?難懂?深入?)的部分,并且會作用于所有互相靠近的對象對,是以它表現的尤其sensitive。最簡單的一種實作方法所有潛在的作用對都調用一個回調函數,在回調函數中,應用程式采用自定義的邏輯(例如查詢遊戲資料)來判斷是否發生了互相作用,不過這種方法在遊戲世界很大的情況下會很慢。特别是當沖突檢測是由一個遠端處理器(像是GPU或其他矢量處理器)在處理的時候,處理器不得不先挂起它的并行計算,中斷遊戲運作遊戲代碼的主處理器,并在再次運作遊戲代碼之前執行callback。即使是在CPU上,它這樣做的同時很可能是運作在多核心或超線程上,所有的序列化代碼都必須到位以確定能同時通路共享資料。使用可以在遠端處理器上執行的固定的函數邏輯是比較好的方法。2.x中就是這麼做的,但這個基于将過濾規則簡單分組的規則不夠靈活不足以滿足所有應用,是以3.0中,使用了shader(開發者使用矢量處理器支援的代碼實作任意filter規則,但是以無法通路記憶體中的資料)這種比2.x的固定函數filtering更靈活的方式,同時也支援filter shader調用CPU callback函數的機制(它能通路所有應用程式資料,以犧牲性能為代價)。詳情見PxSimulationFilterCallback。最好的是應用程式可以基于作用對設定要速度還是靈活度。

首先俺們看看shader system,SampleSubmarine中實作了一個filter shader 

PhysX3 User Guide

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 | PxPairFlag::eNOTIFY_TOUCH_PERSISTS;

                 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;

}

PhysX3 User Guide

SampleSubmarineFilterShader 的shader函數很簡單,實作了PxFiltering.h中的 PxSimulationFilterShader 原型。shader filter 函數, SampleSubmarineFilterShader ,可能不能引用任何記憶體,除了它的參數和本地棧變量(非new的局部變量),因為它可能是被編譯了運作在遠端處理器上。

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 the what SampleSubmarineFilterShader() returns.

The arguments of SampleSubmarineFilterShader() include PxFilterObjectAttributes and PxFilterData for the two shapes, and a constant block of memory. Note that the pointers to the two shapes 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 pointer 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 eRIGID_STATIC, eRIGID_DYNAMIC, or even ePARTICLE_SYSTEM. Additionally, it lets you find out if the object is kinematic, or a trigger.

Each PxShape shape in PhysX 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 shape.

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.

Finall, 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 it starts to overlap again (eSUPPRESS), or call the low performance but more flexible CPU callback if the shader can’t 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’s look at what the above shader does: //  let triggers through

if(PxFilterObjectIsTrigger(attributes0) || PxFilterObjectIsTrigger(attributes1))

{

        pairFlags = PxPairFlag::eTRIGGER_DEFAULT | PxPairFlag::eNOTIFY_TOUCH_PERSISTS;

         return PxFilterFlag::eDEFAULT;

}

This means that if either object is a trigger, then perform default trigger behavior (notify the application while touching each frame), and otherwise perform ‘default’ collision detection between them. The next lines are:

PhysX3 User Guide

//  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;

PhysX3 User Guide

This says that for all other objects, perform ‘default’ collision handling. In addition, if 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:

PhysX3 User Guide

struct FilterGroup

{

         enum Enum

        {

                eSUBMARINE              = ( 1 <<  0),

                eMINE_HEAD              = ( 1 <<  1),

                eMINE_LINK              = ( 1 <<  2),

                eCRAB                   = ( 1 <<  3),

                eHEIGHTFIELD    = ( 1 <<  4),

        };

};

PhysX3 User Guide

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:

PhysX3 User Guide

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 =  new PxShape*[numShapes];

        actor->getShapes(shapes, numShapes);

         for(PxU32 i =  0; i < numShapes; i++)

        {

                PxShape* shape = shapes[i];

                shape->setSimulationFilterData(filterData);

        }

        delete [] shapes;

}

PhysX3 User Guide

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 real game, but it shows the basic usage of the filter shader, and it will ensure that SampleSubmarine::onContactNotify() is called for all interesting pairs.

An alteriative group based filtering mechanism is provided with source in the extensions function PxDefaultSimulationFilterShader. And, again, if this shader based system is too inflexible for your needs in general, consider using the callback approach provided with PxSimulationFilterCallback.

    Contact Modification        

Sometimes users would like to have special contact behavior. Some examples to implement sticky contacts, give objects the appearance of floating or swimming inside eachother, 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, the user must derive from the class PxContactModifyCallback: class MyContactModification :  public PxContactModifyCallback

        {

        ...

         void onContactModify(PxContactModifyPair * const pairs, PxU32 count);

        };

And then implement the function onContactModify of PxContactModifyCallback:

PhysX3 User Guide

void MyContactModification::onContactModify(PxContactModifyPair * const pairs, PxU32 count)

{

         for(PxU32 i= 0; i<count; i++)

        {

                ...

        }

}

PhysX3 User Guide

Basically, 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, friction properties of the contacts cannot be modified. Perhaps we will make this possible in future releases. See PxContactPoint and PxContactPointAux for properties that can be modified.

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.

    Custom Constraints        

Constraints like contact filtering, also uses shaders, for the same reason: There is a requirement to inject performance sensitive custom code into the SDK. Constraint is a more general term for joints. While joints were native objects of the PhysX 2.x API, PhysX 3.0 only supports a fully customizeable constraint object in the core API, and all 2.x joint types are implemented using this mechanism as extensions. Let’s take a short look at how this works. Once the reader understands, he will be in a good position to create his own joint types. You should read the chapter on joints before you try to understand their workings, however.

When you call PxJointCreate(), the extensions library first fills out a PxConstraintDesc object, which is a bunch of parameters for constraint creation. Here is the code for a spherical joint:

PhysX3 User Guide

PxConstraintDesc nxDesc;

nxDesc.actor[ 0]                         = desc.actor[ 0];

nxDesc.actor[ 1]                         = desc.actor[ 1];

nxDesc.flags                            = desc.constraintFlags;

nxDesc.linearBreakImpulse       = desc.breakForce;

nxDesc.angularBreakImpulse      = desc.breakTorque;

nxDesc.solverPrep                       = SphericalJointSolverPrep;

nxDesc.project                          = SphericalJointProject;

nxDesc.visualize                        = SphericalJointVisualize;

nxDesc.dataSize                         =  sizeof(SphericalJointData);

nxDesc.connector                        = joint->getConnector();

PhysX3 User Guide

The first few settings are self explanatory ... like the actors to connect, when the joint should break, and so on. The next three are three callback functions – user defined shaders! (See the section on filter shaders to find out what shaders are, and the rules that apply to them.) They contain the code that mathematically defines the behavior of the joint. Every time the joint needs to be solved, the simulation will call these functions.

Finally, the ‘connector’ is a class of additional user defined joint specific functionality that are not called from the solver directly, and are not shaders.

Lastly, the filled out descriptor is used to create a the constraint object:

PxConstraint* constraint = physics.createConstraint(nxDesc);

繼續閱讀