本人水平有限,翻译中如果出现比较恶的句子...大家一定要查阅原文。
更新: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
翻译:kun
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
翻译: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
翻译: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。将来会允许使用者设置任意的小数来表达“部分单向”交互。
射线查询是一种基本的碰撞检测手段。和关联到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