http://blog.csdn.net/kun1234567/article/details/2580500
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
Basics:
Math Classes
4種資料結構:
NxVec3
NxQuat
NxMat33
NxMat34
Simple Shapes
7種基本實體模型:
NxBounds3 軸對稱邊界盒
NxBox 對象邊界盒
NxCapsule 交囊
NxPlane 任意的面
NxRay 無限射線/直線
NxSegment 有限射線/線段
NxSphere 球
(事實上還有多邊形,不過它應該不能稱為簡單)
SDK Initialization
NxCreatePhysicsSDK: 建立接口指針。
參數一:接口版本
參數二:記憶體管理類實體
參數三:用于輸出的流
要點一:SDK版本要和驅動版本比對
要點二:可以多次調用NxCreatePhysicsSDK,但是都将得到同一個實體,就象單件模式一樣。但是存在引用計數,是以需要嚴格比對 call 和 release 的次數。
要點三:多次調用的時候,後設定的記憶體管理類将被忽略,但是輸出流對象則會被覆寫。
Casting and Instancing
建立執行個體:
1、聲明描述符
2、調用PhysX接口建立該執行個體
釋放:
父對象的釋放會引起子對象的自動釋放。
轉換:
NxShape / NxSphereShape / NxMoxsSs
向下轉換
Shape->isSphere() shape->isBox()
失敗傳回NULL。
Memory Management
雖然不是必須的,但是PhysX提供了記憶體管理接口。(繼承NxUserAllocator)
重載了 malloc / realloc / free。
一旦被設定,就不能更改。
該類提供的記憶體管理函數必須保證是“線程安全的”。
Debug Rendering
不能使對象之間的間距可視化
Debug Information通過 gScene->getDebugRenderable() 傳回的 NxDebugRenderable得到。
PhysX不是圖象庫,使用者需要自己繪制這些資訊。
User Data
可以給對象(Actor)關聯資料。
如 myShape->userData = ***;
User Defined Classes
8個需要使用者自己實作的類:
NxUserAllocator 記憶體管理 NxUserOutputStream 錯誤資訊 NxUserContactReport 反射(接觸)通知NxUserNotify 事件通知 NxUserRaycastReport 射線查詢回調 NxUserTriggerReport 觸發器回調NxUserEntityReport 實體回調?? NxStream 串行化(用于資料IO)
Error Reporting
使用NxUserOutputStream
Saving the Simulation State
所有通過描述符建立的對象都有2個方法:saveToDesc() 和 loadFromDesc()
可以用于儲存對象目前的狀态,這個特性可以應用于儲存遊戲,或者其他目的。
SDK Parameters
PhysX有一套集中管理的全局變量,85個定義,其中變量83個,啞元1個,記錄數量1個)
使用者可以通過 setParameters() / getParameters() 來修改他們。
大多數變量的預設值是不需要更改的。
Parameter Summary
Parameter | Default Value | Description |
NX_PENALTY_FORCE | DEPRECATED | |
NX_SKIN_WIDTH | 0.025 | 皮膚厚度 (range: [0, inf) Unit: distance. |
NX_DEFAULT_SLEEP_LIN_VEL_SQUARED | (0.15*0.15) | The default linear velocity, squared, below which objects start going to sleep. (range: [0, inf)) |
NX_DEFAULT_SLEEP_ANG_VEL_SQUARED | (0.14*0.14) | The default angular velocity, squared, below which objects start going to sleep. (range: [0, inf)) |
NX_BOUNCE_TRESHOLD | -2 | A contact with a relative velocity below this will not bounce. (range: (-inf, 0]) |
NX_DYN_FRICT_SCALING | 1 | This lets the user scale the magnitude of the dynamic friction applied to all objects. (range: [0, inf)) |
NX_STA_FRICT_SCALING | 1 | This lets the user scale the magnitude of the static friction applied to all objects. (range: [0, inf)) |
NX_MAX_ANGULAR_VELOCITY | 7 | See API Reference for NxBody::setMaxAngularVelocity() for details. |
NX_CONTINUOUS_CD | Enable/disable continuous collision detection (0.0f to disable) | |
NX_VISUALIZATION_SCALE | This overall visualization scale gets multiplied with the individual scales. Setting to zero turns off debug visualizations. | |
NX_VISUALIZE_WORLD_AXES | Visualize the world axes. | |
NX_VISUALIZE_BODY_AXES | Visualize a body's axes. | |
NX_VISUALIZE_BODY_MASS_AXES | Visualize a body's mass axes. | |
NX_VISUALIZE_BODY_LIN_VELOCITY | Visualize the bodies' linear velocity. | |
NX_VISUALIZE_BODY_ANG_VELOCITY | Visualize the bodies' angular velocity. | |
NX_VISUALIZE_BODY_JOINT_GROUPS | Visualize joint groups. | |
NX_VISUALIZE_JOINT_LOCAL_AXES | Visualize local joint axes. | |
NX_VISUALIZE_JOINT_WORLD_AXES | Visualize joint world axes. | |
NX_VISUALIZE_JOINT_LIMITS | Visualize joint limits. | |
NX_VISUALIZE_CONTACT_POINT | Visualize contact points. | |
NX_VISUALIZE_CONTACT_NORMAL | Visualize contact normals. | |
NX_VISUALIZE_CONTACT_ERROR | Visualize contact errors. | |
NX_VISUALIZE_CONTACT_FORCE | Visualize contact forces. | |
NX_VISUALIZE_ACTOR_AXES | Visualize actor axes. | |
NX_VISUALIZE_COLLISION_AABBS | Visualize bounds (AABBs in world space). | |
NX_VISUALIZE_COLLISION_SHAPES | Visualize shape. | |
NX_VISUALIZE_COLLISION_AXES | Visualize shape axes. | |
NX_VISUALIZE_COLLISION_COMPOUNDS | Visualize compound (compound AABBs in world space). | |
NX_VISUALIZE_COLLISION_VNORMALS | Visualize mesh & convex vertex normals. | |
NX_VISUALIZE_COLLISION_FNORMALS | Visualize mesh & convex face normals. | |
NX_VISUALIZE_COLLISION_EDGES | Visualize active edges for meshes. | |
NX_VISUALIZE_COLLISION_SPHERES | Visualize bounding spheres. | |
NX_VISUALIZE_COLLISION_STATIC | Visualize static pruning structures. | |
NX_VISUALIZE_COLLISION_DYNAMIC | Visualize dynamic pruning structures. | |
NX_VISUALIZE_COLLISION_FREE | Visualize "free" pruning structures. | |
NX_VISUALIZE_COLLISION_CCD | Visualize CCD tests. | |
NX_VISUALIZE_COLLISION_SKELETONS | Visualize CCD skeletons. | |
NX_VISUALIZE_FLUID_EMITTERS | Visualize emitter. | |
NX_VISUALIZE_FLUID_POSITION | Visualize particle position. | |
NX_VISUALIZE_FLUID_VELOCITY | Visualize particle velocity. | |
NX_VISUALIZE_FLUID_KERNEL_RADIUS | Visualize particle kernel radius. | |
NX_VISUALIZE_FLUID_BOUNDS | Visualize fluid AABB. | |
NX_VISUALIZE_FLUID_PACKETS | Visualize fluid packets. | |
NX_VISUALIZE_FLUID_MOTION_LIMIT | Visualize fluid motion limits. | |
NX_VISUALIZE_FLUID_DYN_COLLISION | Visualize fluid dynamic mesh collision. | |
NX_VISUALIZE_FLUID_STC_COLLISION | Not implemented: Visualize fluid static collision. | |
NX_VISUALIZE_FLUID_MESH_PACKETS | Visualize available fluid mesh packets. | |
NX_VISUALIZE_FLUID_DRAINS | Visualize fluid drain shapes. | |
NX_VISUALIZE_FLUID_PACKET_DATA | Visualize fluid data packets. | |
NX_VISUALIZE_CLOTH_MESH | Visualize cloth meshes. | |
NX_VISUALIZE_CLOTH_COLLISIONS | Visualize cloth rigid body collision. | |
NX_VISUALIZE_CLOTH_SELFCOLLISIONS | Visualize cloth self collision. | |
NX_VISUALIZE_CLOTH_WORKPACKETS | Visualize cloth clustering for the PPU. | |
NX_VISUALIZE_CLOTH_SLEEP | Visualize cloth sleeping. | |
NX_VISUALIZE_CLOTH_SLEEP_VERTEX | Visualize cloth sleeping with full per-vertex information. | |
NX_VISUALIZE_CLOTH_TEARABLE_VERTICES | Visualize tearable cloth vertices. | |
NX_VISUALIZE_CLOTH_TEARING | Visualize cloth tearing. | |
NX_VISUALIZE_CLOTH_ATTACHMENT | Visualize cloth attachments. | |
NX_VISUALIZE_CLOTH_VALIDBOUNDS | Visualize cloth valid bounds. | |
NX_VISUALIZE_SOFTBODY_MESH | Visualize soft body meshes. | |
NX_VISUALIZE_SOFTBODY_COLLISIONS | Visualize soft body collisions with rigid bodies. | |
NX_VISUALIZE_SOFTBODY_WORKPACKETS | Visualize soft body clustering for simulation on the PPU. | |
NX_VISUALIZE_SOFTBODY_SLEEP | Visualize soft body sleeping. | |
NX_VISUALIZE_SOFTBODY_SLEEP_VERTEX | Visualize soft body sleeping with full per-vertex information. | |
NX_VISUALIZE_SOFTBODY_TEARABLE_VERTICES | Visualize tearable soft body vertices. | |
NX_VISUALIZE_SOFTBODY_TEARING | Visualize soft body tearing. | |
NX_VISUALIZE_SOFTBODY_ATTACHMENT | Visualize soft body attachments. | |
NX_VISUALIZE_SOFTBODY_VALIDBOUNDS | Visualize soft body valid bounds. | |
NX_ADAPTIVE_FORCE | 1 | Used to enable adaptive forces to accelerate convergence of the solver. |
NX_COLL_VETO_JOINTED | 1 | Controls default filtering for jointed bodies (true = collision disabled). |
NX_TRIGGER_TRIGGER_CALLBACK | 1 | Controls whether two touching triggers generate a callback or not. |
NX_SELECT_HW_ALGO | Internal parameter, used for debugging and testing. Not to be used. | |
NX_VISUALIZE_ACTIVE_VERTICES | Internal parameter, used for debugging and testing. Not to be used. | |
NX_CCD_EPSILON | 0.01 | Distance epsilon for CCD algorithm. |
NX_SOLVER_CONVERGENCE_THRESHOLD | Used to accelerate the solver. | |
NX_BBOX_NOISE_LEVEL | 0.001 | Used to accelerate HW Broad Phase. |
NX_IMPLICIT_SWEEP_CACHE_SIZE | 5.0 | Used to set the sweep cache size. |
NX_DEFAULT_SLEEP_ENERGY | 0.005 | The default sleep energy threshold. Objects with an energy below this threshold are allowed to go to sleep. Note: Only used when the NX_BF_ENERGY_SLEEP_TEST flag is set. |
NX_CONSTANT_FLUID_MAX_PACKETS | 925 | Constant for the maximum number of packets per fluid. Used to compute the fluid packet buffer size in NxFluidPacketData. |
NX_CONSTANT_FLUID_MAX_PARTICLES_PER_STEP | 4096 | Constant for the maximum number of new fluid particles per frame. |
NX_VISUALIZE_FORCE_FIELDS | Force field visualization. | |
NX_ASYNCHRONOUS_MESH_CREATION | [Experimental] Disables scene locks when creating/releasing meshes. | |
NX_FORCE_FIELD_CUSTOM_KERNEL_EPSILON | 0.001 | Epsilon for custom force field kernels. |
NX_IMPROVED_SPRING_SOLVER | 1 | Enable/disable improved spring solver for joints and wheelshapes. |
NX_PARAMS_NUM_VALUES | This is not a parameter, it just records the current number of parameters. |
Parameter Ranges
- rigid body transform – 一個坐标向量+一個旋轉矩陣
- position vector – 一個坐标向量,可能是本地坐标或者是世界坐标
- rotation matrix – 旋轉矩陣
- unit quaternion – 4元數
- direction/extents vector - 偏移量/方向向量。
- force vector - Linear force vector. Forces are added to the momentum of a body; care should be taken to keep the momentum from growing too large.
- torque vector - Torque vector. A torque is added to the angular momentum of a body; care should be taken to keep the angular momentum within a reasonable range.
- velocity vector - Linear velocity vector. Consider the magnitude of the velocity when multiplied by the mass, i.e., a large velocity when applied to a body with a large mass will result in a very large momentum.
- angular velocity vector - Angular velocity vector. Consider the magnitude of the angular velocity when multiplied by the inertia tensor, i.e., the angular momentum.
- momentum vector - Linear momentum vector.
- angular momentum vector - Angular momentum vector.
Utility Functions
2.3以前可以直接調用工具函數,但是之後必須通過調用NxGetUtilLib()建立一個工具庫指針,通過該指針進行調用。
Prior 2.3: NxSetFPUPrecision24();
After 2.3 NxUtilLib* gUtiLib = NxGetUtilLib(); gUtiLib-> NxSetFPUPrecision24();
Dynamics(動力學)
概念
一共有 種概念,分别是:
Scenes
所有的實體模拟都是在場景中進行的。一個場景包含actors, joints, effectors , 可以同時存在多個場景,每個場景各自進行模拟演算,各個場景之間的對象沒有聯系。不過你可以通過一些方法使場景之間可以進行通信。
場景沒有空間上的範圍,屬于一個場景中的各種對象實際上構成了邏輯上的“組”。場景的任何特性都将作用于組内的各種對象和動作。
大多數的模拟都隻需要在一個場景中進行。如果你在開發網絡遊戲,那麼可能在Server 和Client各自存在一個場景,這些場景共同使用一個CPU(GPU?PPU?)。這個情況下,一個處理器就會建立多個場景:Client場景模拟使用者周圍的環境,而Server場景則模拟整個世界。
直接使用預設值,而不為場景描述符指定任何屬性,就可以建立場景了。不過一般來說,場景總是需要一個重力加速度的,可以通過 NxScene::setGravity()來更改這一屬性。
Simulation Timing
場景主要的特性是對實體模拟的實時演算。它将根據時間來計算各種變量的目前值。
模拟演算一針隻會進行一次。通常我們會根據FPS來計算時間的流逝,以期望得到更加真實的模拟結果。
注意,時間片不能太長,否則模拟的穩定性就會下降,演算得出的結果也不再可靠。
一般使用以下方法來推進模拟:
Void simulate( NxReal elapsedTime);
通過調用該方法,可以通知引擎根據流逝的時間來進行從上次情況到目前情況的模拟演算。不過該方法的演算精确度還要取決于 setTiming()
Void setTinging ( NxReal maxTimestep = 1.0f / 60.0f,
NxU32 maxIter = 8,
NxTimeStepMethod method = NX_TIMESTEP_FIXED)
時間片變量當然也可以通過場景描述符來指定。預設值可以适用于大多數程式的需求。
“使用修正的時間”這個步驟非常重要,它保證了模拟的穩定性和可重複性,是以在很多程式裡,都推薦使用它—通過設定NX_TIMESTEP_FIXED。
在引擎内部,引擎自身可以将流逝的時間劃分為更小的時間片。
當調用simulate() 的時候傳遞了一個 比 maxTimestep還要的大的值,引擎就使用maxTimestep對時間進行劃片。剩餘部分将累計到下一次的模拟中。
maxIter 變量規定了劃分時間片的最大次數。如果超過了劃分次數時間仍然有剩餘,剩餘部分将累計到下一次的模拟中。
不過使用可變長度的時間片依然有效。通過指定 NX_TIMESTEP_VARIABLE,引擎将不會對時間進行分片,而是直接使用使用者給出的時間長度作為一個片。
Recommended Time Stepping Method
推薦使用如下方法來劃分時間片:
使用定長時間片劃分政策,而流逝時間是一個常量,并且是maxTimestep的倍數。通過這種方法,使用者可以清楚的知道自己把時間劃分成了多少個片,
這樣做的好處在于,任何實體上的行為,都是确定的,可以預測的。如果使用者使用的是變長時間片模式來進行模拟,一切都将變得不可預測。不過使用變長時間片,使用者可以得到多次調用 simulate的能力。比如在 moveGlobalPose() 和 addForce() 這2個函數中分别調用。
Asynchronous Stepping
PhysX 是多線程的。實體演算在獨立的線程中進行。演算的狀态更新需要按以下順序進行函數調用:
1、 開始模拟
2、 確定所有必須的資料都已經發送到模拟線程
3、 檢查模拟是否完成,如果完成了就把模拟結果寫入緩沖區。
4、 交換狀态資料指針(雙緩存?),将新的結果應用于程式。
函數隊列可能長的跟下面差不多:
NxScene* gScene;
NxReal myTimestep = 1.0f / 60.0f
…
Void mySimulationStepFuncion()
{
gScene->simulate( myTimestep);
gScene->flushStream();
// …在這裡利用上一貞計算的結果做些事情
gScene->fetchResults( NX_RIGID_BODY_FINISHED, true);
}
這裡有一些關于 fetchResults()函數的使用方法的其他版本。通過函數的變量來進行選擇,或者通過調用checkResults()來檢查是否完成都可以。上面那段函數将發生死鎖,直到所有的剛體計算完成(NX_RIGID_BODY_FISHED == true)。使用不會發生死鎖的執行流程,可以更加充分的利用資源。
void mySimulationStepFunction() { gScene->simulate(myTimestep); gScene->flushStream();
//...perform useful work here using previous frame's state data
while(!gScene->checkResults(NX_RIGID_BODY_FINISHED, false) { // do something useful }
gScene-> fetchResults(NX_RIGID_BODY_FINISHED, true);
}
或者
void mySimulationStepFunction() { gScene->simulate(myTimestep); gScene->flushStream();
//...perform useful work here using previous frame's state data
while(!gScene->fetchResults(NX_RIGID_BODY_FINISHED, false) { // do something useful }
}
變量 NX_RIGID_BODY_FINISHED 是NxSimulationStatus的一個标志量
注意:在2.1.2之前,對應的函數是 startRun() / finishRun().
注意:在調用 simulate() / fetchResults() 這對函數之前,動态切換雙緩存這個事情對一些函數是不可見的,比如 overlap? 和 raycasting?.
Asynchronous Physics Programming
次世代主機正在朝着多處理器體系發展,是以也具有了更加強大的計算能力,這種提升将應用于渲染、動畫、AI、實體、聲音以及遊戲的方方面面。這也是為實體引擎設計多線程模拟能力的基本動機。在多處理器的環境下,運作simulate()/fetchResults()函數塊的線程可以從主線程中獨立出去。是以就更快。
多線程實體API是為了“完美級”的實體表現效果而設計的,但是這也意味着次世代的開發者們面臨着同步、負載平衡、系統瓶頸等等一系列新的問題。AGEIA針對次世代系統的前沿而設計,是以将發揮它們的最大潛力。
Solver Accuracy
1、 PhysX預設計算4級連接配接以内的效果。這個值最大為30。
2、 旋轉太快會出問題,是以應該設定最大值。
actor->setMaxAngularVelocity(maxAV);
3、 應該保證參與實體計算的各項值(如品質、速度、力)都是比較合理的,這樣得出的結果的精确度才能夠保證。可以參考真實世界的大緻比例。
4、 通過gPhysicsSDK->setParameter(NX_ADAPTIVE_FORCE, 0); 關閉各種作用力的影響。
技巧:
1、盡量使用 彈性連接配接杆 來代替 彈簧 和 泵閥受動器(?)。
2、使用彈簧的時候,保持彈性常量 ,并且将衰減設定的低一些。
3、不要亂設質點,盡量把質點設定到對象的碰撞體内。特别是對那些附載了彈簧的對象。
4、通過調整皮膚厚度,可以讓對象更友善的堆砌在一起。
Actors
對象分為兩種:靜态對象、動态對象。
靜态對象主要用來做碰撞檢測。有形狀屬性,沒剛體屬性。
動态對象,形狀屬性不是必須的,但是剛體屬性是必須的。
Shapes in Actors
靜态對象必須要指定形狀。
沒有固定形狀的對象 必須指定剛體屬性,包括質點和慣性張力。
一個有固定形狀的動态對象應該滿足以下的某一條:
1、 有品質,0密度,沒有慣性張力(後兩者可以計算得到)
2、 0品質,非0密度,沒有慣性張力
3、 有品質,0密度,有慣性張力。
不滿足以上條件的對象将不會被建立。
Compound shapes
混合形狀是自動構造的。
1、 為一個非混合形狀的對象添加一個新形狀,将産生一個混合形狀。
2、 一個個的添加形狀,比在構造對象的時候一次指定所有的形狀要慢。
3、 應該避免靜态對象有一個混合形狀。混合形狀有3角面限制,超過上限後,再添加的形狀将直接忽略。
Reference Frames
對象的一個重要屬性是它的pose(位置和朝向)。PhysX在這個問題上提供了一種靈活的解決方法,是以首先應該了解一些空間關系。
舉個例子,現在我們要模拟一張桌子。這個桌子有一個Box桌面和4個Box腿。
Rigid Body Properties
動态對象有一系列關于剛體模拟的屬性。
線性相關變量:
Mass
Position
Velocity
Force
旋轉相關變量:
Inertia
Orientation
Angular velocity
Torque
The Inertia Tensor
開發人員一般都對運動學知之甚少,是以慣性張量可能是剛體屬性中最特殊的地方。之前在“剛體屬性”這一章節,我們也提到過,慣性張量隻用來描述剛體品質分布,即使這個Actor沒有Shape,也可以設定Inertia。不過就算Actor有Shape和品質,我們為了實作一些特别的效果,可能會直接修改慣性張量。(也就是說慣性張量的優先級高于品質和形狀。不過暫時未确定該說法…)
(譯者: 假設物體繞着某個點旋轉,物體和點之間的距離為 r, 角速度為 w.
慣性張量 I = m * r*r;
向心力 F = m * r * w*w
旋轉紐矩 t = F*r = m * r*r * w*w = I * r;
是以對于旋轉來說,慣性張量描述了改變物體旋轉狀态的難易程度。當物體繞着自己的質心旋轉時,就反映了自轉的難易程度。)
是以慣性張量 也代表了對象 轉向的難易程度。從生活中就可以感覺到:旋轉一根棍子很簡單,但是旋轉一個鍊條則不那麼順暢,擺動它遠比擺動一根棍子要難。
高校的實體課本裡曾經告訴你如何計算一個特定形狀的瞬間慣性張量。不過這些入門級的知識隻講了計算沿着特定軸的慣性張量。在SDK裡,我們使用矩陣(NxMat34),它可以描述對象在任意方向上的慣性張量。可以使用向量或矩陣在任意坐标系(本地-世界)中描述向量。
在做内部計算的時候,矩陣會被分解為 一個旋轉矩陣 和一個平移向量。這樣可以提高計算速度。
當你建立一個剛體,你也許會想為這個剛體單獨指定慣性張量。預設情況下的慣性張量都是一樣的,這可能跟你設定的形狀不相符。最簡單的方法是将慣性張量的大小和Actor的形狀進行關聯,SDK可以通過密度或總品質來計算出這個形狀的慣性張量。當然,更有效的辦法是你自己計算慣性張量。SDK提供了大量的相關函數,可以參閱
NxExportedUtils.h
NxInertiaTensor.h
跟mesh相關的,以下兩個函數也很有用:
NxConvexMesh::getMassInformation
NxTriangleMesh::getMassInformation
NxActor 的方法:
void setMassSpaceInertiaTensor(const NxVec3 &m); NxVec3 getMassSpaceInertiaTensor();
以上兩個函數提供了對慣性張量的通路函數。
旋轉的慣性張量可以通過以下函數來通路
void setCMassLocalOrientation (const NxMat33 &); NxMat33 getCMassLocalOrientation ();
以下方法:
void getGlobalInertiaTensor (NxMat33 &dest); NxMat33 getGlobalInertiaTensorInverse();
rotate this tensor using the current actor orientation into world space; as a result, the tensor changes every time the actor moves.不懂..囧
Manual Computation of Inertia Tensors
有時候,手動給Actor對象指定一個質心很有用,可以描述不均勻的品質分布。(比如不倒翁)。指定一個比普通質心低的質心将增強不倒翁效果。
預設情況下,建立一個Actor時SDK自動計算質心和慣性張量。不過使用者也可以通過Actor的成員變量 massLocalPose, massSpaceInertia 來重新設定這2個值:
NxReal NxComputeSphereMass (NxReal radius, NxReal density);
NxReal NxComputeSphereDensity (NxReal radius, NxReal mass);
NxReal NxComputeBoxMass (const NxVec3& extents, NxReal density);
NxReal NxComputeEllipsoidMass (const NxVec3& extents, NxReal density);
...
void NxComputeBoxInertiaTensor (NxVec3& diagInertia, NxReal mass, NxReal xlength, NxReal ylength, NxReal zlength);
void NxComputeSphereInertiaTensor(NxVec3& diagInertia, NxReal mass, NxReal radius, bool hollow);
massLocalPose 是一個将慣性張量對角線化的變換矩陣,用于改變對象原有的Pose(Pose,對象在本地坐标系的原點,并且沒有旋轉的狀态)。
譯者:如果将慣性張量(有九個分量,其中六個是獨立的)對角線化,那麼會得到一組主軸,以及一個轉動慣量(隻有三個分量)
另外一個常用的函數:
bool NxDiagonalizeInertiaTensor(const NxMat33 & denseInertia, NxVec3 & diagonalInertia, NxMat33 & rotation);
該函數根據一個旋轉矩陣和一個密度慣性張量(慣性張量的密度?不懂囧)求得一個對角線化的慣性張量。
Applying Forces and Torques
Setting the Velocity
Sleeping
Sleep Events
Active Transform Notification
Static Actors
靜态對象跟動态對象不同的地方在于沒有任何關于剛體屬性的描述。要建立一個靜态對象,隻需要保持Actor描述符中的剛體字段為空即可。
一旦靜态對象被建立,就不要對它進行任何操作。雖然移動位置、添加形狀、或者删除靜态對象等操作并沒有明确被禁止,這裡有2個理由說明為什麼:
1、 SDK認為靜态對象一直都是靜态的(不會改變初始狀态),是以很多優化政策都是基于這點來進行的。改變靜态對象将導緻SDK重新計算這些預先優化的資料結構。
2、 編寫一些關于Actor或者joints 的控制的代碼時,已經假設了靜态對象在模拟時間片内是不會發生改變的,因為這會帶來極大的性能優化空間。
舉個例子:如果移動一個在一堆盒子下面的靜态對象,雖然這些盒子都飛到了半空中,但是他們不會被激活,即使他們被激活了,在“移動的靜态對象”和動态對象之間進行碰撞檢測的結果品質也不會很高。
為了實作可移動的靜态對象,應該使用 “運動學對象”。
Kinematic Actors
Adaptive Force
Character Controller
Character Controller – Creation
建立一個角色控制器,第一步要做的事情是決定邊界體。就目前來說,PhysX隻支援立方體(NxBoxController)和膠囊(NxCapsulesController)。
第二,建立一個控制器管理器(controller manager)。如:
NxControllerManager* gManager = NxCreateControllerManager(myAllocator);
注意:控制器管理器是單件(同一時間隻存在一個執行個體)。
第三、建立一個角色控制器。如:
NxScene* scene;
NxCapsuleControllerDesc desc;
<填充控制器描述符>
NxController* cpController = gManager->createController(scene,desc);
當你不需要控制器管理器的時候,應該釋放它。
NxReleaseControllerManager( gManager);
注意:以前的類名“ControllerManager”現在也有效,但是不建議使用,并且它将在下一個版本被取消掉。
Character Controller - Update
每一貞都可以調用以下函數來移動角色。
NxController::move(constNxVec3& disp,
NxU32 activeGroups,NxF32 minDist,NxU32& collisionFlags,NxF32 sharpness=1.0f,const NxGroupsMask* groupsMask=NULL);
Disp: 位移向量
activeGroups: 碰撞組掩碼
minDist:最小距離,角色的位移低于這個值将不發生位移。
collisionFlags:碰撞檢測類型掩碼
sharpness:銳度?
groupsMask:?
Graphics Update
在每一貞裡,可以通過角色控制器的position資訊來保持外觀模型的位置同步。一個控制器從來都不管旋轉的事。是以也隻能得到位置相關的資訊。如:
const NxVec3& NxController::getPosition() const;
把這個資訊傳遞給你的世界坐标矩陣,然後去渲染,這樣你的角色位置就是經過碰撞檢測修正過的,并且表現出更加平滑的移動。
Character Controller - Volume
角色使用的邊界體隻限于PhysX提供的标準幾何體。
目前隻支援2種不同的形狀:
AABB:軸對齊的邊界盒。由一個position和一個extents向量定義。這個盒子不會随着角色的旋轉而旋轉,始終是與世界的X、Y、Z 3個軸對齊的。
Capsule:膠囊。由一個position, 一個 height 和 一個 radius定義。(2個球+1個圓柱)。它可以獲得更加平滑的移動,但是也會稍微多花些CPU時間。
注意:在版本2.3之前,有一個 球型控制器 NxSphereController,但是自從有了膠囊,它就被抛棄了。
注意:兩種控制器都使用 Sweep API ,這就意味着并不是所有的可碰撞的形狀都可以用于角色運動,隻有那些可以被掃描的形狀才能用于角色運動的判定。
角色碰撞體周圍有一層被定義為“皮膚”,它被用來決定是否與其他形狀有接觸。皮膚的厚度由使用者定義。如果為了Debug而顯示角色碰撞體,别忘了還有個皮膚的概念。
AABB相關變量:
NxControllerDesc::position
NxControllerDesc::skinWidth
NxBoxControllerDesc::extents
Capsule相關變量
NxControllerDesc::position
NxControllerDesc::skinWidth
NxCapsuleControllerDesc::radius
NxCapsuleControllerDesc::height
關于AABB的一些解釋:
皮膚:外表面
Extents:内表面
Character Controller - Auto-Stepping
如果沒有自動上台階(跨越障礙)的功能,地面上一個非常小的台階都會阻擋角色的移動。顯然這不自然,因為如果台階不是很高的話,我們可以輕松的走上去才對。但是台階太高了的話,就不應該走上去。這個界定值由以下變量給出:
NxControllerDesc::stepOffset
為了實作自動上台階,PhysX需要知道“上”的方向。目前PhysX隻支援3個軸向向量:
NX_X ( 1, 0, 0 )
NX_Y ( 0, 1, 0 )
NX_Z ( 0, 0, 1 )
相關的變量為:
NxControllerDesc::upDirection
Character Controller - Walkable Parts
預設情況下,角色可以走到任何地方。不過在現實世界總是有限制存在。是以,“不允許在角度很大的多邊形上走”的限制就有存在的必要。隻要使用者給出角度界定,PhysX就可以自動的把多邊形分為“可以在上面行走的”和“不可以在上面行走的”。将來可能把檢測級别推到三角面級别的精确度。
可以通過以下變量來設定界定值:
NxControllerDesc::slopeLimit
它是角度的cos。下面的代碼規定了隻有小于45度的多邊形上才可以行走:
slopeLimit = cosf( NxMath::degToRad(45.0F));
如果值設定為0.0f,該特性就自動關閉(那樣的話,角色哪都能走了)
這個特性并不總是需要的。一個更加普遍的做法是關掉這個特性,然後使用看不見的牆來限制角色的運動範圍。
Character Controller - Volume Update
有時候實時的改變角色碰撞體的大小非常有用。比如如果你的角色可以蹲下,那麼如果可以把碰撞體的高度減少的話,就可以移動到之前不能去的地方了
角色控制庫支援動态更新碰撞體。但是如果不經過檢測就直接改變碰撞體大小,很可能會使碰撞體在設定完之後與其他幾何體相交或重疊。為了避免這種情況,首先應該調用PhysX為此準備的一些API來進行測試,保證碰撞體有足夠的空間來更改大小,隻有通過這些API檢測之後,才對碰撞體進行更新。
跟AABB相關的函數:
bool NxBoxController::setExtents( const NxVec3& extents) = 0;
跟膠囊相關的函數:
bool NxCapsuleController::setRadius(NxF32 radius) = 0;
bool NxCapsuleController::setHeight(NxF32 radius) = 0;
相關的檢測函數:
bool NxScene::checkOverlapSphere();
bool NxScene::checkOverlapAABB();
bool NxScene::checkOverlapCapsule();
Character Controller - Callbacks
當控制器發生一些事件的時候,可以設定回調函數。(比如當一個角色撞到了一個形狀,或者另外一個角色)
當一個角色碰撞了另外一個形狀,可以得到以下資訊:
1、 目前控制器
2、 被碰撞的形狀
3、 碰撞點
4、 碰撞點的法向量
5、 一個特征碼(比如三角形序号)
6、 額外的運動資料。
這些資訊可以應用于遊戲的其他方面,比如播放音效、渲染細節、應用作用力或者别的什麼。
注意:現在還不是所有的資訊都可以得到,這取決于角色撞上了什麼樣的形狀。最終會支援所有的形狀。
當一個角色碰撞了另外一個角色,比如一個角色撞到了一個NPC,可以得到以下資訊:
1、一對控制器
Character Interactions
在遊戲中,根據回調函數中傳回的不同的動作編号,會産生不用的行為。例如PhysX要是覺得角色足夠強壯,那他就可以推動某些箱子。
現在支援的動作編号如下:
NX_ACTION_NONE => 不允許實體(角色推不動對象)
NX_ACTION_PUSH => 允許實體(角色推的動對象)
如果給接觸點一個作用力,實體引擎就會模拟出自然的推動過程,聽起來是非常Cool的。不過這個特性對遊戲來說可能會起反作用。邊界體的形狀是人為控制的,但是你不希望因為從Box控制器切換到Capsule控制器而導緻了推動的效果發生變化。推動的效果應該遊戲說了算。是以通常比較好的辦法是在回調函數中給接觸點一個作用力,而不是完全交給實體引擎去計算。另外如果是個膠囊的話,也很難推動一個盒子,因為你幾乎不可能對着盒子的中間,推動盒子就會使盒子旋轉,雖然你可能隻是想把盒子沿直線推一點點。
注意:NX_ACTION_PUSH 目前還沒有實作,例子中的效果是使用碰撞時提供的資訊來實作的。
Character Controller - Hidden Kinematic Actors
角色控制庫 為每個角色建立了一個隐藏的運動學Actor。有時候這就是引起Actors數量混亂的原因。是以可能得到一個錯誤的Actor數量,或者在查詢Actor的時候,傳回了一個未知的Actor。
當角色控制器處理角色移動的時候,也會同步這個Actor。
可以通過以下函數得到這個Actor:
NxActor* NxController::getActor() const;
不過,應該避免直接操作這個Actor,這會導緻問題。不過隻是讀取資訊則沒有問題。
Character Controller - Time Stepping
NxCharacter 庫裡對Actor的操作跟普通的Actor沒什麼差別。精确的講,他們使用相同的時間片政策(定長/變長)。當使用定長時間片政策時,會造成一些麻煩。因為NxController總是使用變長時間片政策(大多數情況下,是每貞的間隔)。是以在這種情況下NxController對象并不會跟Actor對象總是精确的同步。