[TOC]

今天遇到一個問題,角色卡在一個模型邊上,在PVD看模型也比較正常,一直調試跟蹤,最終找到了問題所在。

原因

1.問題出在最後一個sweepCapsuleTriangles函數里面,代碼如下:

#ifdef __SPU__
				Vec3V dummy1, dummy2;
				FloatV result = distanceSegmentTriangleSquared(
					V3LoadU(capsule.p0), V3LoadU(capsule.p1), V3LoadU(p0), V3LoadU(p1), V3LoadU(p2), dummy1, dummy2);
				PxReal dist2 = FStore(result);
	#else
				// AP: switching to SIMD version produced -25% regression in Sweep*SphereHfld
				PxReal dist2 = distanceSegmentTriangleSquared(capsule.p0, segmentExtent, p0, p1 - p0, p2 - p0);
	#endif
				if (dist2<=r2)
				{
					hitIndex    = i;
					t            = 0.0f;
					normal        = -unitDir;
					outFlags    = PxHitFlag::eDISTANCE|PxHitFlag::eNORMAL;
					_triNormal    = denormalizedNormal.getNormalized();
					return true;
				}

當p0、p1和p2有有兩個點相同的時候,會導致p1-p0和p2-p0其中一個向量爲0,得到的dist2爲負無窮大,所以就會執行代碼中的條件語句。最終結果是move函數移動成功了,但是隻移動了0的位移,表現上就是一直卡在那個地方。

  1. 至於頂點一樣的情況,其實並不是完全一樣,而是有很小的差異。再加上浮點數運算會丟失精度,這個很小的差異被抹掉了,所以到上面代碼的時候就出現相同的點了。

計算的代碼位置(沒看太明白):

PX_FORCE_INLINE void Gu::TriangleMesh::computeWorldTriangle(PxTriangle& worldTri, PxTriangleID triangleIndex, const Cm::Matrix34& worldMatrix, PxU32* PX_RESTRICT vertexIndices, PxU32* PX_RESTRICT adjacencyIndices) const
	{
		PxU32 vref0, vref1, vref2;
		if(mMesh.has16BitIndices())
		{
			const Gu::TriangleT<PxU16>& T = ((const Gu::TriangleT<PxU16>*)getTrianglesFast())[triangleIndex];
			vref0 = T.v[0];
			vref1 = T.v[1];
			vref2 = T.v[2];
		}
		else
		{
			const Gu::TriangleT<PxU32>& T = ((const Gu::TriangleT<PxU32>*)getTrianglesFast())[triangleIndex];
			vref0 = T.v[0];
			vref1 = T.v[1];
			vref2 = T.v[2];
		}
		const PxVec3* PX_RESTRICT vertices = getVerticesFast();
		worldTri.verts[0] = worldMatrix.transform(vertices[vref0]);
		worldTri.verts[1] = worldMatrix.transform(vertices[vref1]);
		worldTri.verts[2] = worldMatrix.transform(vertices[vref2]);
	
		if(vertexIndices)
		{
			vertexIndices[0] = vref0;
			vertexIndices[1] = vref1;
			vertexIndices[2] = vref2;
		}

		if(adjacencyIndices)
		{
			if(mMesh.getAdjacencies())
			{
				adjacencyIndices[0] = mMesh.getAdjacencies()[triangleIndex*3 + 0];
				adjacencyIndices[1] = mMesh.getAdjacencies()[triangleIndex*3 + 1];
				adjacencyIndices[2] = mMesh.getAdjacencies()[triangleIndex*3 + 2];
			}
			else
			{
				adjacencyIndices[0] = 0xffffffff;
				adjacencyIndices[1] = 0xffffffff;
				adjacencyIndices[2] = 0xffffffff;
			}
		}
	}
  • 問題數據

出現問題的時候,斷點的數據如下:

vertices[vref0] = {x = -4.10000086, y = -0.200000167, z = -3.56512594}
	vertices[vref1] = {x = -4.10000086, y = -0.200000077, z = -3.56512594}
	vertices[vref2] = {x = -4.10000086, y = -0.200000212, z = -4.10000229}

	worldMatrix.transform.base3 = {x=72.3379974 y=4.76143503 z=18.5843773 }

	// 得到這個三角形前兩個點完全一樣
	worldTri.verts[0] = { x = 68.237990, y = 4.56143475, z = 15.0192509}
	worldTri.verts[1] = { x = 68.237990, y = 4.56143475, z = 15.0192509}
	worldTri.verts[2] = { x = 68.237990, y = 4.56143475, z = 14.4843750}

簡單來說就是:

4.76143503 + -0.200000167 = 4.56143475
	4.76143503 + -0.200000077 = 4.56143475

至於爲什麼請參考另外一篇文章[1] 爲什單精度浮點數的有效位時7位

解決

  • 1.把頂點傳到PhysX之前,先檢查一遍。可以刪掉重新組織Vertex和Index,我簡單處理的方法是把兩個頂點很近的時候,標記爲同一個索引,這樣的三角形比較容易標識
    if (fMinDist <= 1e-5)
                  {
                      // set the same index, and then cooking will process it. eREMOVE_DUPLICATED_TRIANGLES
                      pIndices[3 * i + nIndexIndex1] = pIndices[3 * i + nIndexIndex2];
                  }
  • 2.PhysxCooking的參數增加去重的Flag,這樣上面的三角形就會被PhysX剔除,從而就解決了這樣的問題:如下
physx::PxCookingParams cookingParams(scale);
		cookingParams.buildTriangleAdjacencies = true;
		cookingParams.meshPreprocessParams = PxMeshPreprocessingFlags(PxMeshPreprocessingFlag::eWELD_VERTICES |
			PxMeshPreprocessingFlag::eREMOVE_UNREFERENCED_VERTICES | PxMeshPreprocessingFlag::eREMOVE_DUPLICATED_TRIANGLES);

參考

[1] 爲什麼單精度浮點數的精度是7位

相關文章