PhysX官方手册翻译

PhysX官方手册翻译


本人水平有限,翻译中如果出现比较恶的句子...大家一定要查阅原文。

更新:2008-7-1 22:22 Raycasting(射线查询)

更新:2008-6-26 23:33

更新:2008-6-26 0:38

更新:2008-6-25 0:50


Broad Phase Collision Detection

翻译:kun


碰撞检测的第一步是找出场景中可能发生碰撞的碰撞对。因为存在n*n/2对可能的碰撞对,如果场景很大,或者对象很多的话,全部检测将花费相当多的时间。物理引擎将自动的为各种几何体划分空间,通过这种方式,一个形状将只检测在它空间附近的几何体。(空间分割)注意:在以前的版本中,SDK允许使用者自己定义空间划分算法,后来发现不提供这种特性反而更好。

几何体对过滤如果一对几何体被判定为可能相交,将做3次进一步的判定,根据结果来确定这对几何体是不是使用者所关心的几何体对。只有通过这3次检测之后,才会花时间进行碰撞模拟的计算。如果以下条件为true,则表示几何体A和几何体B需要进行碰撞检测:

(a->getActor()->isDynamic() || b->getActor()->isDynamic())

&& NxScene::getGroupCollisionFlag(a->getGroup(), b->getGroup())

&& (!(NxScene::getShapePairFlags(a,b) & NX_IGNORE_PAIR))

第1个检测的含义是静态对象不会主动引起碰撞,所以静态对象不会进行碰撞检测。(但不表示动态对象不撞它)

第2个检测的含义是只有Actor的group掩码一致才能发生碰撞。

第3个检测的含义是两个被连接杆连接的对象将不会发生碰撞检测。如果需要该特性,需要设置变量 NX_COLL_VETO_JOINTED 为false.

碰撞组 首先将检测2个几何体的碰撞组是否一致。所有的几何体都可以通过以下形式指派碰撞组。

NxShape::setGroup(NxCollisionGroup)

碰撞组是一个0到31的整数。下面的代码表示将一个几何体设置到11组。

myShape->setGroup(11);

所有的几何体的碰撞组都默认为0,but this does not really come with any built in meaning(不懂啥意思...)。SDK维护着一张32*32的表格,该表格精确的指示了各个组之间的碰撞关系。默认情况下,任意组之间都可以发生碰撞。你可以通过以下调用来更改该表的条目:

NxScene::setGroupCollisionFlag(CollisionGroup g1, CollisionGroup g2, bool enable);

例如,以下代码表示设置组11和组0之间不碰撞。

gScene->setGroupCollisionFlag(11, 0, false);

如果事先知道各个组之间的关系,那么这张表会十分有用。 Actor对象也可以指派组。Actor组和Shape组将同时起作用,(但是谁覆盖谁?)虽然它们是出于同一个目的。它们的工作方式相同,但是Actor可以具有更大的范围,最多支持0x7FFF个组,而Shape组则只有32个。Actor组将更加灵活。首先通过以下代码可以设置Actor的组:

myActor->setGroup(333); 每个Actor默认组为0。

接下来是设置组之间的关系:

gScene->setActorGroupPairFlags(333,334, NX_NOTIFY_ON_START_TOUCH | NX_NOTIFY_ON_END_TOUCH);

以下标志量可以被联合使用:

NX_NOTIFY_ON_START_TOUCH, NX_NOTIFY_ON_END_TOUCH, NX_NOTIFY_ON_TOUCH.

在触发器章节中,可以得到以上3个标志量的详细解释。

禁止检测的几何体对

判断一对几何体之间的碰撞检测是否被禁止。任意一对Actor或Shape都可以通过以下代码来设置:

NxScene::setActorPairFlags(NxActor&, NxActor&, NxU32 flags);

NxScene::setShapePairFlags(NxShape&, NxShape&, NxU32 flags);

如果一个Actor有多个Shape,显然你不希望这些Shape相互之间进行碰撞检测,这个时候上面的第2种调用就十分有用了。在这种情况下,只需要设置相交标志量为 NX_IGNORE_PAIR。以下代码表示将忽略这2个Actor之间的碰撞。

gScene->setActorPairFlags(a1, a2, NX_IGNORE_PAIR);

注意:其他的标志量将在之后的章节里讨论。

自定义Actor过滤规则

可以自定义Actor对的过滤规则,你完全可以自己决定哪些Actor可以相交。如果需要使用这个特性,需要做以下步骤:

1、实现NxUserActorPairFiltering 接口,包括onActorPairs 回调函数。这个回调函数将会在决定是否将 Actor对 存入碰撞检测数组时被调用。

2、为每一个应用你的划分策略的Actor设置NX_AF_USER_ACTOR_PAIR_FILTERING 标志量。

3、当你更换划分策略的时候,你需要显示的调用那些应用自定义规则的Actor的NxActor::resetUserActorPairFiltering() 方法,这样他们就会更新自己的Actor碰撞对数组。否则只有等到下一次碰撞发生的时候新规则才能起作用。

例子:Sample Filtering


Contact Filtering

翻译:kun


除了NxScene::setGroupCollisionFlag() 和 NxScene::setShapePairFlags() 提供的过滤机制,SDK还提供了一种久经考验的过滤机制,它基于布尔值和标志量。使用者可以为每一个几何体指定一个128位的组掩码。该掩码将与用户指定常数和操作进行混合,并且它的结果将决定一对几何体是否发生接触。剔除算法的伪代码如下:

bool shouldGenerateContacts(shape0Bits, shape1Bits)

{

value = (shape0Bits op0 constant0) op2 (shape1Bits op1 constant1)

return ((bool)value)==filterBool;

}

也就是说,shape0的掩码将和[常数0]以[操作规则0]的方式进行混合,shape1的掩码将和[常数1]以[操作规则1]的方式进行混合。最后,2个结果以[操作规则2]的方式进行混合,并且结果将和变量filterBool进行比较。如果最后的结果为true,则判定该对几何体将发生接触。

有效的操作(NxFilterOp的成员)

NX_FILTEROP_AND - result = A & B

NX_FILTEROP_OR - result = A | B

NX_FILTEROP_XOR - result = A ^ B

NX_FILTEROP_NAND - result = ~ (A & B)

NX_FILTEROP_NOR - result = ~ (A | B)

NX_FILTEROP_NXOR - result = ~ (A ^ B)

NX_FILTEROP_SWAP_AND -

results.bits0 = A.bits0 & B.bits2;

results.bits1 = A.bits1 & B.bits3;

results.bits2 = A.bits2 & B.bits0;

results.bits3 = A.bits3 & B.bits1;

可以通过以下方式设置 操作规则/常数/过滤布尔值(filterBool):

void NxScene::setFilterOps(NxFilterOp op0, NxFilterOp op1, NxFilterOp op2);

void NxScene::setFilterBool(bool flag);

void NxScene::setFilterConstant0(const NxGroupsMask& mask);

void NxScene::setFilterConstant1(const NxGroupsMask& mask);

NxGroupsMask 是一个将128位的值封装成4个32位的值的类:

class NxGroupsMask

{

public: NxU32 bits0, bits1, bits2, bits3;

};

最后,NxShape 提供了设置/获取 组掩码的方法:

void NxShape::setGroupsMask(const NxGroupsMask& mask);

const NxGroupsMask NxShape::getGroupsMask() const;

另外,组掩码也可以通过 shape 描述符在创建的时候指定。

NxShapeDesc::groupsMask;

下面以4个几何体为例子,说明碰撞检测的过程。(*表示将发生接触)

(表略)

首先规定4个基本掩码,然后以合适的方式进行混合,并分别设置到4个几何体上,其中几何体D的组掩码为混合的。如下表:

(表略)

然后设置 常量/操作规则,确保 如果2个几何体的组掩码进行ANDed 操作的时候产生一个非0的值,则判定为发生接触。

gScene->setFilterOps(NX_FILTEROP_OR, NX_FILTEROP_OR, NX_FILTEROP_AND);

void NxScene::setFilterBool(true);

NxGroupsMask zeroMask;

zeroMask.bits0=zeroMask.bits1=zeroMask.bits2=zeroMask.bits3=0;

void NxScene::setFilterConstant0(zeroMask);

void NxScene::setFilterConstant1(zeroMask);

设置常数0和常数1为0,并且设置操作规则0和操作规则1为NX_FILTEROP_OR,等价于不对参与计算的几何体的组掩码做计算,然后这2个掩码执行NX_FILTEROP_AND 操作,最后的结果和filterBool 进行比较。也就是前面所说的判定规则。

注意1:对于任意两个参与碰撞检测的几何体,如果设计出了A碰B 和B碰A 不相等的结果,那这种过滤器就要不得。(规则必须满足交换律)

例子: Sample Filtering


Triggers

翻译:kun


触发器是一个几何体,它允许其他几何体穿越自己。每一个几何体在穿过它的时候可以产生相对应的事件,如进入、离开、或者停留。触发器可以用来实现感应门之类的东西。

触发器可以是任意形状的,除了环行。Triangle mesh triggers count as hollow surfaces for collision detection, not volumes. Note that convex vs. triangle mesh and mesh vs. mesh triggers are not implemented. 创建一个触发器,并将它绑定到某个Actor上的步骤,跟添加一个几何体形状到Actor差不多。唯一的不同就是需要在Shape描述符里标记成触发器:

//This trigger is a cube.

NxBoxShapeDesc boxDesc;

boxDesc.dimensions = NxVec3(10.0f, 10.0f, 10.0f);

boxDesc.shapeFlags |= NX_TRIGGER_ENABLE;

NxActorDesc actorDesc;

actorDesc.shapes.pushBack(&boxDesc);

NxActor * triggerActor = gScene->createActor(actorDesc);

在上面的例子中,我们指定了 NX_TRIGGER_ENABLE 标志,这表示触发器将产生所有的事件。当然也可以通过以下标志来进行选择:NX_TRIGGER_ON_ENTER, NX_TRIGGER_ON_LEAVE, 和 NX_TRIGGER_ON_STAY.

要接收触发器事件,就应该实现事件回调函数。回调函数可以通过派生 NxUserTriggerReport 类来实现。下面是个例子:

class TriggerCallback : public NxUserTriggerReport

{

void onTrigger(NxShape& triggerShape, NxShape& otherShape, NxTriggerFlag status) {

if(status & NX_TRIGGER_ON_ENTER)

{

//A body entered the trigger area for the first time

gNbTouchedBodies++;

}

if(status & NX_TRIGGER_ON_LEAVE)

{

//A body left the trigger area

gNbTouchedBodies--;

}

//Should not go negative

NX_ASSERT(gNbTouchedBodies>=0);

}

} myTriggerCallback;

gScene->setUserTriggerReport(&myTriggerCallback);

警告:

1、对Actor执行“根据Shape计算mass”的操作时,被标记为触发器的几何体,不会参与其中的计算。因此,如果创建了一个动态Actor,它只有一个Shape,而这个Shape又刚好被标记为触发器,则必须为Actor指定一个mass和惯性。

2、触发器对射线和扫描检测透明。

3、SDK全局变量 NX_TRIGGER_TRIGGER_CALLBACK 决定是否在2个触发器几何体接触的时候产生触发器事件。

4、一个静态几何体不会触发一个静态触发器。

5、其他任意的组合都会产生触发器事件。比如 运动对象&动态对象,运动对象&动态对象,动态触发器&运动对象等.

6、不能在OnTrigger()函数中改变物理环境,具体的说就是不能创建或销毁对象。如果必须改变模拟环境,也要等到本次时间片模拟完,才可以进行相关操作。

7、凸多边形&三角形网格 和 网格&网格触发器 还没实现。

8、触发器不参与连续检测(CCD)。

线程安全

NxUserTriggerReport 对象只会在用户线程被调用,因此不需要保证它的线程安全。


Continuous Collision Detection

翻译:kun


连续检测对快速移动的对象十分有用。在非连续检测的方案里,如果一个对象足够的快,那它就可以在一个时间片里穿越一些足够薄的障碍,就象隧道效应。想象一颗子弹飞向一个薄片,前一贞里子弹还在片的左边,当进行当前贞位置计算的时候,子弹速度*时间片>片的厚度,那么子弹的位置就直接被设置到薄片的另一边了。在这种情况下,SDK将无法检测出子弹和片的碰撞。

为了解决这个问题,PhysX采用了连续检测(CCD)策略。该策略不是检测一些离散的点,而是构造一个几何体,该几何体是对象在整个时间片内的路径几何体。检测这个几何体,如果发现有碰撞,那么就可以计算出实际发生碰撞的时间,并且根据计算结果产生一些合理的动作。

在当前版本,CCD支持 动态对象&动态对象,动态对象&静态对象之间的检测。

CCD 骨骼 CCD是通过内嵌到对象的骨骼网格实现的。它比离散检测更加简洁。骨骼会阻止一个对象穿越另外一个对象。当一个对象静止或者套在另外一个对象上,更高频率的离散检测可以提供更加符合现实的行为。

CCD骨骼是一种网格,不过较之标准的网格来说,CCD骨骼的限制更少。它可以使用游离的顶点,也就是说,它的顶点不会拿来形成三角面,但是不能给出一个退化的三角形。因此你可以使用单独的顶点来做CCD射线检测。

另外,一个单独的CCD骨骼,可以关联到多种shape,这样可以减少内存开销。

SDK全局变量 NX_CCD_EPSILON 用来指定CCD骨骼的厚度,从而改善计算的精确度。当一个点试图穿越一个三角面,CCD将阻止它,并且把它的位置设置成贴着三角面的位置。不过,由于计算精度问题,这个点可能会被设置到三角形的另外一个面帖着。这样一来它便穿越了这个三角面,就象有隧道效应一样。因此 NX_CCD_EPSILON 在判断这个问题上就十分有用。因为 NX_CCD_EPSILON 实际上就是数学上的 “极小的正数”,它可以被设置成极小的值。

要创建一个CCD骨骼,可以通过使用NxPhysicsSDK::createCCDSkeleton() 。该函数接受一个 NxSimpleTriangle 对象,该对象用来描述网格。要关联一个CCD骨骼和一个shape,使用 NxShape::setCCDSkeleton() 方法。

CCD骨骼指导方针:

1、CCD的开销比普通的碰撞检测要大的多,因此使用尽量少的三角面和顶点。

2、对于多shape的Actor,每一个形状的子骨骼将合并成一个单独的骨骼。下面的约束将影响如何计算这个单独的骨骼的顶点和边。

3、骨骼不支持超过64个顶点。

4、骨骼最多只能有256个边

5、静态对象不需要CCD骨骼,并且也不使用它们,因为直接使用形状的mesh要好的多。(注意,只有顶点和三角形网格才参与CCD)

6、CCD骨骼几何体应该完全包含在它关联的shape内部,实际上,最好能跟外边框保持一定距离。

对子弹例子的进一步说明。有时候你可能为了一些特定的表现效果,需要给一些对象指定精确的CCD骨骼,好让它们可以在某些情况下利用隧道效应发生穿越,但是需要注意以下几点:

1、你是希望CCD骨骼越精确越好呢,还是希望简单而有效呢?只有一个顶点的CCD骨骼也许是效率最高的选择。

2、一个小的CCD骨骼可以让Actor对象通过一个一般情况下都不能走进去的狭长通道。 3、CCD骨骼越大,那它被CCD算法检测出来的几率就越大?

CCD可以有效的检测Actor的碰撞,从而使它的动作停下来。但是CCD把碰撞后的侧滑动作也给停了,可能这并不是你想要的效果。为了防止这样的事情发生的频率过快,可能你会使CCD骨骼比它关联的形状要小(形状要减去皮肤厚度),这样一来CCD骨骼会被用来检测一个对象是不是嵌入另外一个对象太深(正常情况下,只会嵌2个对象的皮肤厚度)。

//-------------------------------------------------------------------

// 这里翻译的有问题

One situation where penetration depths normally increase past the skin width is in large stacks, where the iterative solver is working hard to de-penetrate objects. There are two main ways of finding a CCD skeleton that fits your game scenario, depending on what you are optimizing for:

Accurate collision A good way to find a CCD skeleton offset that fits your game scenario, is to start with a big CCD skeleton and make it smaller until you no longer see too many CCD blockages at low velocities.

Fast collision A good way to find a CCD skeleton for you scenario is to start off with a one-vertex skeleton and grow it until objects stop escaping through narrow passages, or stop missing in dynamic/dynamic collision scenarios.

这个方法比离散检测要有效的多。有2种方法来确定合适的CCD骨骼大小:

1、先给一个很大的CCD骨骼,然后逐渐调小

2、先给一个很小的CCD骨骼,然后逐渐调大

//-------------------------------------------------------------------

你可以通过 NX_VISUALIZE_COLLISION_SKELETONS 参数来显示CCD骨骼。注意,由于CCD骨骼一般都比shape小,因此可能需要在半透明/线框模式下才能看到他们。

NxSimpleTriangleMesh triMesh; //Fill in triMesh...

NxCCDSkeleton *newSkeleton=gPhysicsSDK->createCCDSkeleton(triMesh);

NxShape *myShape=...

myShape->setCCDSkeleton(newSkeleton);

什么时候使用CCD?

通过设置 NX_CONTINUOUS_CD 变量来启用CCD计算。因为CCD比离散检测要慢,这里有几个优化方法。首先,只有快速运动的对象才需要使用CCD,如果一个对象很容易被一个表面所阻挡,离散检测对它来说就足够了。

为了控制什么时候开始进行CCD,可以通过NxActor::setCCDMotionThreshold() (或者通过 NxBodyDesc 描述符 的 CCDMotionTreshold 字段) 来指定一个启用CCD的最小速度。换句话说,如果低于这个最小速度,则只会使用离散检测。这个规则对绝对速度和相对速度都有用。(比如 动态对象&动态对象 之间的CCD)

如果一个shape要参与 动态对象&动态对象 的CCD,那这个shape 需要设置 NX_SF_DYNAMIC_DYNAMIC_CCD 标志。

/// 不知道为什么有两段一样的话... NX_CCD_EPSILON 变量为CCD骨骼提供了一个非常小的厚度,从而可以提高计算精度。当一个点试图穿越一个三角面,CCD将阻止它,并且把它的位置设置成贴着三角面的位置。不过,由于计算精度问题,这个点可能会被设置到三角形的另外一个面帖着。这样一来它便穿越了这个三角面,就象有隧道效应一样。因此 NX_CCD_EPSILON 在判断这个问题上就十分有用。因为 NX_CCD_EPSILON 实际上就是数学上的 “极小的正数”,它可以被设置成极小的值。

下面的条件用来决定 是否在 动态对象和静态对象之间进行CCD:

1、NX_CONTINUOUS_CD 被设置,或者在某个地方 NX_CF_INHERIT_SETTINGS 标志被清除 ,NX_CONTINUOUS_CD 被设置。

2、动态对象有骨骼。

3、对象的点速度超过CCDMotionThreshold.

4、静态对象是一个 凸多边形网格,或者是三角面网格。

下面的条件用来决定 是否在 动态对象和动态对象之间进行CCD:

1、NX_CONTINUOUS_CD 被设置,或者在某个地方 NX_CF_INHERIT_SETTINGS 标志被清除 ,NX_CONTINUOUS_CD 被设置。

2、两个都有body,是动态对象

3、至少存在一对被设置了 NX_SF_DYNAMIC_DYNAMIC_CCD 的几何体对 存在。

4、2个对象都有CCD骨骼

5、2个对象的点速度都超过CCDMotionThreshold.

6、2个对象的相对速度都超过CCDMotionThreshold.

串行化 CCD骨骼可以使用以下函数来保存到内存中。

NxU32 NxCCDSkeleton::save(void * destBuffer, NxU32 bufferSize);

NxU32 NxCCDSkeleton::getDataSize();

可以用以下的方式来加载一个骨骼。

NxCCDSkeleton *NxPhysicsSDK::createCCDSkeleton(const void * memoryBuffer, NxU32 bufferSize);

直接使用内存数据比手动构造CCD骨骼要更快,因为这样可以预先储存特定的骨骼数据格式。

限制:

1、CCD骨骼最多64个顶点

2、骨骼最多256条边

3、当静态对象是 球、胶囊、片等形状的时候,不会在动态对象和静态对象之间发生CCD。因为静态对象一般都不会使用这些形状,而是使用多边形。(CCD骨骼基于网格工作)

4、如果静态对象的几何体非常复杂,有很多细节(高模,一般不会在游戏中使用),动态对象可能会在处理接触点的时候犯迷糊。一般可以通过增大CCD骨骼的尺寸来减小这方面的影响,但是这取决于模型网格,并不能完全的消除影响。

5、动态对象和动态对象之间的CCD 使用近似值,它假定一个形状 正在飞快的旋转。如果2个对象的形状都在非常快的旋转,那CCD的结果可能就会出现很大偏差。

6、CCD 假设模型的旋转 在一个时间片里不会超过180度。

7、CCD会停止对象的动作,因此当一个渗透被检测到,对象就停止了,甚至它只是模拟了时间片的一半。这个可能会造成对象在某贞里被挂在空中。

8、触发器不参与CCD

9、在将一个CCD骨骼关联到一个动态对象的Shape上的时候,需要十分小心。因为可能会产生 一个非CCD形状已经检测到了碰撞,而CCD形状还没有。你需要确定自己不会创建出这样的东西。

10、运动学对象不会被CCD中断动作,因此对一个快速移动的运动学对象使用CCD是没有效果的。(除非这个运动学对象正在快速移动,同时另外一个具有CCD形状的动态对象也在高速运动,那这个动态对象的行为可能会被运动学对象中断)

11、CCD在受限制的硬件场景中不工作。

例子:略

例子: Sample CCD Explosion Sample CCD Dynamic


Dominance Groups

翻译:kun


通过 “组间优势”(组的优先级),可以在Actor对象之间设置单向约束。比如 骑手和坐骑,角色和它的尾巴,你不希望这些对象之间相互影响,或者说你不希望马尾巴摇摆的时候,还带动马屁股摇摆(但是马屁股摇摆的时候,马尾巴会做出相应的动作),对于这种情况,“组间优势”所规定的单向影响就十分有用。

一个 NxDominanceGroup 对象 是个5位的组标志量(范围从0-31)。默认情况下所有的Actor对象都是0号组,静态对象永远都是0号组。你可以通过 描述符(dominanceGroup 字段)或者调用NxActor::setDominanceGroup().来改变它。

含义:

任何时候,如果需要确定两个对象(a0,a1)之间的约束条件,就需要这两个对象的 组参数。然后可以使用 getDominanceGroupPair(g0, g1) ,算出一个 NxConstraintDominance 对象。

在约束条件里,NxConstraintDominance::dominance0 是a0的优势值,NxConstraintDominance::dominance1 是代表a1的优势值。 默认情况下 NxConstraintDominance::dominanceN 的值为 1.0f,根据约束条件,这表示 aN 可以被 a1-aN 推或者拉动。而如果值为0.0,表示 a1 - aN 不会对它产生影响。这就是说,(1.f,1.f)表示互相影响,(0.f, 0.f)表示互不影响,而(1.f,0.f)就产生了单向影响。

默认行为:

getDominanceGroupPair(g1, g2)的返回值被定义如下:

1、 g1 == g2 返回 (1.f, 1.f)

2、 g1 < g2 返回 (0.f, 1.f)

3、 g1 < g2 返回 (1.f, 0.f)

换句话说,默认情况下,组 编号高的对象会被组对象低的对象影响。也就是说 组号越低,优先级越高。

可以通过使用 NxScene::setDominanceGroupPair() 来更改设置。

It is not possible to make the matrix asymmetric, or to change the diagonal. 换句话说:

1、如果(g1 == g2),无法更改 (g1,g2) 的结果。

2、如果你将 ( g1, g2) 的结果设置为 X,那么(g2,g1) 的结果将自动被设置为 ~X:

~(1.f, 1.f) == (1.f, 1.f)

~(0.f, 1.f) == (1.f, 0.f)

~(1.f, 0.f) == (0.f, 1s.f)

这两个约束条件保证了 g1,g2 的顺序无关性。

优先级设置目前只能设置为 0.f 或者 1.f。将来会允许使用者设置任意的小数来表达“部分单向”交互。


Raycasting 翻译:kun

射线查询是一种基本的碰撞检测手段。和关联到Actor或者Shape上的线段不同,射线查询由用户发起,在查询过程中穿透1个或多个shape。它很有用,比如你想实现“拾取”效果的时候。

下面是一些射线查询相关的函数,每一种所使用的射线都有一些不同,他们都是NxScene的成员函数,并且长的都象 raycast*():

1、raycastAnyBounds, raycastAnyShape -- 这2个方法是最原始的,但是也是最快的。它们简单的返回射线查询碰到的第1个AABB或者Shape对象。

2、raycastClosestBounds, raycastClosestShape -- 这2个方法跟前面2个方法差不多,不过同时还会返回射线点和交点的距离。

3、raycastAllBounds, raycastAllShapes -- 这2个方法是最复杂的版本,它们返回所有被射线穿透的对象,同时也包含到各个对象的距离。为了使用这些信息,你需要实现一个叫做 NxUserRaycastReport 的接口,它会由每一个被射线穿透的对象调用。

下面是一个实现 NxUserRaycastReport 使用 raycastAllShapes 的例子:

class myRaycastReport : public NxUserRaycastReport

{ virtual bool onHit(const NxRaycastHit& hits)

{

//Record information here.

return true; //Or false to stop the raycast.

}

}gMyReport;

NxRay worldRay;

worldRay.orig= cstart;

worldRay.dir= cend - cstart;

worldRay.dir.normalize(); //Important!!

NxU32 nbShapes = gScene->raycastAllShapes(worldRay, gMyReport, NxUserRaycastReport::ALL_SHAPES);

警告:

1、轮子型形状不会被 射线查询 检测到。

2、被射线查询检测到的 几何体 进行onHit() 回调的时候,SDK不保证它们是按照真实的几何顺序来进行的。

3、SDK 在做射线查询的时候会返回 2个面索引 。 faceID 是 mesh 被 cooked 之前的索引,而 internalFaceID 则是已经 cook 后的索引(cook 会导致重排面索引)(译者:SDK可能将三角面重新排列--以更加适合物理计算的方式)。只有在使用 被cooked 版本的 mesh 的函数--比如 readback系列函数 或者 saveToDesc() --才使用 internalFaceID.(参见Reading Back Mesh Data)

4、在 onHit() 的执行流程中, 不应该更改SDK的状态。也就是说,不要创建或销毁任何对象。如果一定要这样做,那就应该等到射线查询执行完成,并且将SDK的当前状态保存到缓存指针。

5、由于SDK是使用双缓存指针进行异步模拟,而双缓存对一些函数来说是透明的(也就是说它们不知道有双缓存,只知道‘当前缓存’),因此SDK的状态发生了变化之后,应该调用simulate()/fetchResults(),对它们来说SDK状态才有所变化。

多线程

NxUserRaycastReport 类只会在用户线程中调用,因此它不需要保证线程安全。

例子:

Sample Raycast

你可能感兴趣的:(设计模式,多线程,游戏,算法,F#)