Nvidia PhysX 学习文档9: Rigid Body Collision

                                                            


official site:https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/RigidBodyCollision.html#rigid-body-collision

红色代表需要弄懂的。


Rigid Body Collision



Introduction

This section will introduce the fundamentals of rigid body collision.



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.

Shape的作用是 描述actor的空间延申, 和碰撞属性。 使用shape的三个作用: (1) test intersection,应该是得到两个东东相交相碰的部分;(2) secne query test,例如ray cast;  (3) 定义trigger volume,当其他的shape于该volumes相交时,volumes会产生notification.

Shapes are reference counted, see Reference Countingshape可以被引用计数。

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:

每个shape都包含: 一个PxGeometry object, 一个feference to PxMaterial。在创建shape的时候必须指定两者。 下例创建按了一个shape,球形geometrym.

PxShape* shape = physics.createShape(PxSphereGeometry(1.0f), myMaterial, true);
myActor.attachShape(*shape);
shape->release(); //为何直接release了?

The method PxRigidActorExt::createExclusiveShape() is equivalent to the three lines above.

PxRigidActorExt::createExclusiveShape()函数等同于上面的三行代码。

Note

for reference counting behavior of deserialized(反序列化) shapes refer to Reference Counting of Deserialized Objects.

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.

createShape()函数的最后一个入参为true是代表 被创建的shape不会被其他actors shared。 用户可以使用shape sharing 来减少内存损耗,特别是当许多的actor由相同的geometry的时候。 但是,shared shape由很多强烈的限制用户不能更新一个shared shape的属性,当其被attached到一个actor时候(没有体会!)。

Optionally you may configure a shape by specifying shape flags of type PxShapeFlags. By default a shape is configured as
你可以设置shape的PxShapeFlags类型的flag,默认情况下shape的配置是:

  • 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.
当一个geometry 对象被指定给一个shape的时候,该object被复制到shape内部。 不是所有的geometry都可以被指定给shape,是否可以 取决于shape flag和 type of parent actor.

  • 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.
    如果一个shape是用来attach到一个dynamic actor的,那么, 该shape不能使用的geometry类型是: TriangleMesh, HeightField and Plane geometries。
  • TriangleMesh and HeightField geometries are not supported for trigger shapes.
    对于trigger shape, 不能使用的geometry类型是:TriangleMesh and HeightField.

See the following sections for more details.

Detach the shape from the actor as follows:

myActor.detachShape(*shape);


Simulation Shapes and Scene Query Shapes

---两种类型的shape

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.
一个shape可以被配置成 参与scene query或contact test, 或者两者都参与。默认情况下两者都参与。

The following pseudo-code configures a PxShape instance so that it no longer participates in shape pair intersection tests:

配置一个PxShape对象,使其不参与intersection test:

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:
配置一个PxShape对象,使其参与intersection test:

void enableShapeInContactTests(PxShape* shape)
{
    shape->setFlag(PxShapeFlag::eSIMULATION_SHAPE,true);
}

To disable a PxShape instance from scene query tests:

配置一个PxShape对象,使其不参与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:

配置一个PxShape对象,使其参与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).
如果一个actor不需要参与simulation,其shape仅仅被用来做scene query或手动移动, 这时,可以disable 这个actor的simulation以节省内存。具体参考文档中关于PxActorFlag::eDISABLE_SIMULATION。



Kinematic Triangle Meshes (Planes, Heighfields)

--前面有提到过TriangleMesh, HeightField and Plane类型的geometry不能给shape,当该shape是要被attached到dymatic actor时.

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.

可以创建一个kinematic RxRigidDynamic 类型的actor, 其shape对应triangle mesh, plane, heighfield, 如果shape有一个simulation shape flag, 那么, 该actor必须保持kinematic(也就是不能做dynamic actor呗)。     ---看上一篇文章, RxRigidDynamic 类既可以生成dynamic 对象,也可以kinematic对象。

To setup kinematic triangle mesh see following code:

下例展示了如何setup kinematic triangle mesh:

PxRigidDynamic* meshActor = getPhysics().createRigidDynamic(PxTransform(1.0f));
PxShape* meshShape;
if(meshActor)
{
    meshActor->setRigidDynamicFlag(PxRigidDynamicFlag::eKINEMATIC, true);//将上面创建的PxRigidDynamic对象设置成KINEMATIC

    PxTriangleMeshGeometry triGeom;
    triGeom.triangleMesh = triangleMesh;
    //shape包含geometry和material, 创建一个shape同时将该shape赋给上面创建的actor, 又因为上面的actor是kinemati类型的,故而
    //称kinematic triangle mesh.
    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);//将上面创建的PxRigidDynamic对象设置成KINEMATIC

    PxTriangleMeshGeometry triGeom;
    triGeom.triangleMesh = triangleMesh;
    //shape包含geometry和material, 创建一个shape同时将该shape赋给上面创建的actor
    meshShape = PxRigidActorExt::createExclusiveShape(*meshActor, triGeom, defaultMaterial);
    getScene().addActor(*meshActor);

    PxConvexMeshGeometry convexGeom = PxConvexMeshGeometry(convexBox);
    //只是给actor换了一个shape啊!这就是所谓的转换成dynamic actor? 难道意思是 该actor换了shape后,可以被设置成dynamic类型啦? 好像     //代码不全
    convexShape = PxRigidActorExt::createExclusiveShape(*meshActor, convexGeom,
        defaultMaterial);
}


Broad-phase Algorithms

broad-phase algorithms:一类碰撞检测的算法, https://blog.csdn.net/qq_35865125/article/details/103442582

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.

当许多的objects 处于sleeping状态时,选择 PxBroadPhaseType::eSAP 算法是一个很好的选择。 但是,当所有的object移动时,或者当添加/删除大量的objects时,这种算法会减低性能。  这种算法不需要设置world bounds。

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.
Physx 3.3引入了新的PxBroadPhaseType::eMBP算法, 该算法避免了上面算法的性能缺陷缺陷(当所有object在移动或者大量的object被添加/删除时)。  但是, 当大量的objects处于sleeping状态时,其性能不如上面的算法。另外,该算法需要用户去定义world bounds。

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.

PxBroadPhaseType::eABP 算法是eMBP算法的进一步,该算法在PhysX4.1中被提出。 该算法自动管理world bound和broad-phase regions,因此该算法既有 算法的方便优点,又具备eMBP算法的性能优势。   eSAP算法能在大量object处于sleep时,比该算法更快; eMBP算法能在使用大量的合理定义的region时,比该算法更快。但是 , eABP算法通常会又最好的平均性能和最好的内存使用。 对于boradphase来说,eABP算法是一个好的默认选项。

The desired broad-phase algorithm is controlled by the PxBroadPhaseType enum, within the PxSceneDesc structure.

PxSceneDesc结构中,通过 PxBroadPhaseType 类型的枚举来选择使用哪种算法。



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.

兴趣region是一个AABB包围盒 around a volume of space controlled by the broad-phase。 处于包围盒内部的物体会参与到broad phase的碰撞检测。 其他的不在内部的物体则不会参与碰撞检测。 理论上,这些区域应该包括整个仿真空间,同时要少地覆盖到empty space.

---- AABB(axis-aligned bounding box)包围盒被称为轴对齐包围盒。

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.

Region可以相互重叠。为了最大化效率,需要尽量地减少重叠。 如果两个AABB region刚刚touch,则不会被认为是overlapping。 例如, PxBroadPhaseExt::createRegionsFromWorldBounds函数创建许多个不相互重叠的region,创建方式是 划分一个AABB成多个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.

用户使用PxBroadPhaseRegion结构体来定义region。 可以在创建scene的时候定义region,也可以在运行过程中使用PxScene::addBroadPhaseRegion函数来创建。 创建函数会返回新创的region的句柄,以后,可以通过该句柄将region消除掉,方法是调用 PxScene::removeBroadPhaseRegion函数。

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

新建立的一个region可能会与已存在的object重叠。 如果,用PxScene::addBroadPhaseRegion函数新建region时,使用了populateRegion 参数,那么, sdk会自动将这些被新region罩住的object添加到新region中。 但是,这种操作会影响性能,特别是在同一帧中加入多个reigion时! 因此,建议不要使用。    不使用的话,新建的region就是empty的当后续又新的ojbects被添加到secne时,新添加的object可能会属于该region,该region创建之前就已经存在的object被update时,也可能进入到该region. 

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.

上节提到的PxBroadPhaseType::eMBP算法需要定义regions。 另外两种eSAP和eABP算法则不需要定义region. This information(用户选择了哪种算法的信息) is captured within the PxBroadPhaseCaps 结构,该结构包括了每种borad phase 算法的的信息和能力,该结构可以被PxScene::getBroadPhaseCaps 函数retrived出来。

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.

当前最大的region数为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.

可以使用PxSceneDesc structure来定义broad phase 相关的回调函数。 回调函数调用时机: 当发现object越出了指定的兴趣region时。对于超出了兴趣region的object,skd会对其disable collision detection, 当object重新进入到兴趣区域时会重新enable collision detection.

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


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.

几乎所有的应用程序都有这样的需求: 不让某些object参与碰撞检测, 或者,用特殊的方式配置碰撞检测。 在潜艇例子中,我们需要被notified,当潜艇碰到地雷或地雷链的时候。 crab's AI也需要被通知当螃蟹触碰到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.

理解例子是如何实现之前,我们需要先理解sdk filtering system的可能性。过滤掉可能会发生碰撞的一对object这一过滤动作发生在仿真引擎的最深处,并且, 需要对所有的相互接近的pair做检测, 故而,事关性能。     最简单的实施方式是 对任何两个可能将要发生碰撞的ojbects调用回调函数,由函数进行一些判断(eg. consulting its world data base),来决定是否产生碰撞。  但是,这种方法对于large world game world来说,太慢啦,特别是当collisioin detection process发生在远端的处理器(例如GPU或其他的vector processor)时,处理器需要暂停它的parllel computation, 打断运行game code的主处理器,以执行碰撞检测的回调函数。 即使这些回调函数在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。  更好的办法时使用一种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。在3.0版本中,我们引入了shader system和a totally flexible callback mechanismshader system允许用户实现一个任意规则系统,其运行在vector processor中(and is therefore not able to access any eventual game data base in main memory),这比2.0版本中的fixed function filtering更灵活,亦具备同样的效率。totally flexible callback mechanism,该种机制: 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:

以下是shader system的例子,来自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.

以上函数是 PxFiltering.h一个简单的shader function, 该函数是 PxFiltering.h文件中的PxSimulationFilterShader 原型函数的一个实现。shader filter 函数不引用任何的内存,除了该函数的入参和函数的local stack variables.---- 这是因为函数可能在远端的处理器上被编译执行。

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.

对于所有的相互接近的一对 shape, 以上函数都会被调用, 更准确地说,调用的时机是: 两个shape的轴对齐包围盒第一次相交时。   该函数的返回值 会决定采取的下一步行动。

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.

该函数的入参包括,两个object的PxFilterObjectAttributes 和 PxFilterData,以及, 一个const block of memory.

注意,入参中没有两个object相关的指针, 因为这些指针指向的是计算机的main memory,因此,在运行在其他计算机上的程序不能取得这些地址,否则会导致崩溃。  PxFilterObjectAttributes 和 PxFilterData参数   包含了所有有用的信息, 效果同传入指针。PxFilterObjectAttributes占32 bits, 包含了type of object: 例如,       PxFilterObjectType::eRIGID_STATIC or ::eRIGID_DYNAMIC, 另外

,用户可通过该参数知道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.

每一个PxShape都有一个PxFilterData类型的成员变量(这个概念很重要)。    变量是128 bit的用户定义的data, 用户可以使用该变量去存储与碰撞过滤相关的application specific information。该变量会被传给上面函数。
 

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.

还有一个入参对应const block, 存储的是 per-scene global information,该信息传给shader以让shader去operate, 用户也可以使用此去存储过滤信息。

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

最后,介绍一下SampleSubmarineFilterShader() 函数的PxPairFlags参数。 该参数用以告诉sdk 是否应该:   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.

PxPairFlags 亦指定其他的flags,这些flag代表了后续需要对this pair采取什么样的action。 例如, eNOTIFY_TOUCH_FOUND 意味着 当the pair真正地开始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.

上面这段的意思是: 当其中任意一个是trigger时(trigger的概念??), 就执行default trigger behavior(通知application开始touch和结束touch), 否则,就去执行后面的default collision detection.

// 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 pair中的任何一个都不是trigger时,则执行default collison handling. 另外, filterDatas中包含了一个规则,其决定particular pairs where we ask for touch notifications。要理解的话,我们需要知道该例子中赋值给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:
该例子非常简单, 首先定义了一个枚举,然后,给不同的object type一个枚举值,即定义的枚举用来表示各个不同的ojbect type(潜艇各个组成部分)。

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:

该例子将object type数据存到了 PxFilterData::word0字段。然后, 在PxFilterData::word1字段存储另一种object type,意思是,word0代表的类型需要与word1代表的类型产生碰撞。字段的赋值可以发生在创建shape的时候,也可以使用如下的函数:

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:

上面的函数为入参actor的每个shape都设置了PxFilterDatas (每一个PxShape都有一个PxFilterData类型的成员变量), 以下是如何使用这个函数的例子:

setupFiltering(mSubmarineActor, FilterGroup::eSUBMARINE, FilterGroup::eMINE_HEAD |
    FilterGroup::eMINE_LINK);

//让潜艇与SUBMARINE产生碰撞通知:
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.

这种机制对于serious application来说或许太简易,但是, 其展示了filter shader的基本用法,其可以保证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.
另外一种基于group的filtering mechanism在extensions function PxDefaultSimulationFilterShader中被源码展示。 另外,如果这种shader based system不够灵活的话,考虑使用callback approach provided with PxSimulationFilterCallback.



Aggregates (a collection of actors)

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.
Aggregate是很多个actor的集合。aggregate不会提供额外的simulatioin or queey feature,其意义在于将多个actor集合起来,使得sdk可以优化spatial data operations. 一个典型的应用是ragdoll(布娃娃),一个ragdoll由多个不同的actor构成。如果不使用aggerate的话,在borad-phase阶段的entry数目等于ragdoll所包含的全部的shape的数量,用了之后就只有一个了,因此效率更高。对ragdoll使用了aggerate后,如果需要的话 可以perform internal overlap test in a second necessory.      Aggregate的另一个应用场景是 一个actor附带了很多shape。


Creating an Aggregate

Create an aggregate from the PxPhysics object:

需要使用PxPhysics object创建aggregate:

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.
一个aggerate里面的actor数目最大是128, 考虑到效率原因,应尽可能地设小一些。

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.
如果你不需要对aggerate内部的各个actor进行碰撞检测的话,可以在创建aggerate的时候disable这个功能,这比使用scene filtering更加有效率。 这方面的一个典型应用场景是将static or kiinematic actors放到一个aggregate里面。

Note that both the maximum number of actors and the self-collision attribute are immutable.

注意,创建aggerate时,在physics->createAggregate函数入参中指定的actor的最大数目和是否进行内部的碰撞检测,这两个特征在创建后不能修改。


Populating an Aggregate

Adds an actor to an aggregate as follows:

将一个actor放入到一个aggregate 内部操作:

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.

注意:如果一个actor早已经属于一个scene了,再将这个actor往aggregate(也属于同一个scene)内部放是无效的。正确的做法是: 将actor加入到一个aggregate内部,然后将该aggregate放入到scene; 或者, 先将aggregate加入到scene中,然后,将actor加入到该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.

Release PxAggregate 并不会release掉内部的actors. 如果被release的PxAggregate属于一个scene,其内部的actor会被re-inserted in that secne.  如果你想要将PxAggregate 内部的actors也release掉,那么, 最有效的方式和是先将各个ators  release掉,然后再将PxAggregate干掉。

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.
在一个frame内一下子添加很多个object到scene里面是非常cost的。例如,将ragdoll添加到scene中的情况, 再例如localized debris(碎片),一般情况下localized debris的self-collision是被disable掉的。 一种分摊cost of inserting objects into borad-phase structure的方法是 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 shape在仿真过程中不扮演角色(但是,可以让trigger shape参与scene query)。trigger shape的职责是报告there has been an overlap with another shape.   trigger不会产生contacts,因此,contact report对于trigger来说是not available的。  由于shape triggers 在仿真中play no part, sdk 不允许eSIMULATION_SHAPE 和 eTRIGGER_SHAPE 标志 同时被 raised,否则会报错

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:
 

在SampleSubmarine例子中就使用到了trigger shapes来确定submarine是否已经触碰到了珍宝。 在限免的代码中,treasure对应的PxActor的shape被配置成了tregger shape.

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:

当trigger shape与其他的东东overlap的时候,该事件会触发PxSimulationEventCallback::onTrigger函数,该函数属于PxSampleSubmarine类,该类是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.

以上code 遍历所有的pair of overlapping shapes. 如果发现treasure被submarine 触碰到了,那么就设置gTreasureFound 为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.

sdk在内部会为每一个"overlapping pair reported by the broad-phase"创建一个interactioin object. 此种object不仅仅为“pairs of colliding rigid bodies”而创建,也为“pairs of overlapping triggers”而创建。 一般来说,用户应该认为: 此种object会被创建,不管相互overlapping的object是什么类型 或 有什么PxFilterFlag flag。 

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.

当前,每个actor的这种interaction objects的最大数目是65535。 如果超过6535个interactioin都涉及到相同的一个actor,sdk会报错,超过的extra interaction会被ignored。

发布了341 篇原创文章 · 获赞 87 · 访问量 22万+

猜你喜欢

转载自blog.csdn.net/qq_35865125/article/details/103396322