피직스에서 물리적인 충돌이 일어나거나 트리거 볼륨과 충돌체가 겹치는 일이 일어나면 이벤트를 발생시킬 필요가 있다. PhysX에서 물리 이벤트에 대한 콜백 함수를 어떻게 등록할 수 있는지 PhysX 예제 Snippet ContactReport 프로젝트를 분석해 알아보자.

ContactReport 프로젝트에서는 접촉이 일어날때마다 빨간색 선으로 충돌지점과 충격의 방향을 표시해주고 디버그 로그를 찍는다.

	PxSceneDesc sceneDesc(gPhysics->getTolerancesScale());
	sceneDesc.cpuDispatcher = gDispatcher;
	sceneDesc.gravity = PxVec3(0, -9.81f, 0);
	sceneDesc.filterShader	= contactReportFilterShader;			
	sceneDesc.simulationEventCallback = &gContactReportCallback;	
	gScene = gPhysics->createScene(sceneDesc);

 

 먼저 PhysX 씬을 생성하는 부분이다. sceneDesc에 filterShader와 simulationEventCallback를 제대로 설정해야 씬에서 생기는 충돌 이벤트를 잘 처리할 수 있다. simulationEventCallback은 당연히 물리 시뮬레이션 진행 도중 이벤트가 생길 때마다 호출되는 콜백객체고, filterShader는 타입이 PxSimulationFilterShader로 명명된 함수 포인터다. 이 함수 포인터 타입에 대한 설명은 다음과 같다.

/**
\brief Filter method to specify how a pair of potentially colliding objects should be processed.

Collision filtering is a mechanism to specify how a pair of potentially colliding objects should be processed by the
simulation. A pair of objects is potentially colliding if the bounding volumes of the two objects overlap.
In short, a collision filter decides whether a collision pair should get processed, temporarily ignored or discarded.
If a collision pair should get processed, the filter can additionally specify how it should get processed, for instance,
whether contacts should get resolved, which callbacks should get invoked or which reports should be sent etc.
The function returns the PxFilterFlag flags and sets the PxPairFlag flags to define what the simulation should do with the given collision pair.

\note A default implementation of a filter shader is provided in the PhysX extensions library, see #PxDefaultSimulationFilterShader.

This methods gets called when:
\li The bounding volumes of two objects start to overlap.
\li The bounding volumes of two objects overlap and the filter data or filter attributes of one of the objects changed
\li A re-filtering was forced through resetFiltering() (see #PxScene::resetFiltering())
\li Filtering is requested in scene queries

\note Certain pairs of objects are always ignored and this method does not get called. This is the case for the
following pairs:

\li Pair of static rigid actors
\li A static rigid actor and a kinematic actor (unless one is a trigger or if explicitly enabled through PxPairFilteringMode::eKEEP)
\li Two kinematic actors (unless one is a trigger or if explicitly enabled through PxPairFilteringMode::eKEEP)
\li Two jointed rigid bodies and the joint was defined to disable collision
\li Two articulation links if connected through an articulation joint

\note This is a performance critical method and should be stateless. You should neither access external objects 
from within this method nor should you call external methods that are not inlined. If you need a more complex
logic to filter a collision pair then use the filter callback mechanism for this pair (see #PxSimulationFilterCallback,
#PxFilterFlag::eCALLBACK, #PxFilterFlag::eNOTIFY).

\param[in] attributes0 The filter attribute of the first object
\param[in] filterData0 The custom filter data of the first object
\param[in] attributes1 The filter attribute of the second object
\param[in] filterData1 The custom filter data of the second object
\param[out] pairFlags Flags giving additional information on how an accepted pair should get processed
\param[in] constantBlock The constant global filter data (see #PxSceneDesc.filterShaderData)
\param[in] constantBlockSize Size of the global filter data (see #PxSceneDesc.filterShaderDataSize)
\return Filter flags defining whether the pair should be discarded, temporarily ignored, processed and whether the
filter callback should get invoked for this pair.

@see PxSimulationFilterCallback PxFilterData PxFilterObjectAttributes PxFilterFlag PxFilterFlags PxPairFlag PxPairFlags PxSceneDesc.filterShader
*/
typedef PxFilterFlags (*PxSimulationFilterShader)
	(PxFilterObjectAttributes attributes0, PxFilterData filterData0, 
	 PxFilterObjectAttributes attributes1, PxFilterData filterData1,
	 PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize);

 

 

 PhysX의 충돌 객체들은 서로의 바운딩 볼륨의 영역이 서로 겹치고 난 다음에야 더 정밀한 충돌체크를 하든, 트리거 이벤트를 발생시키든 추가적인 작업을 한다. PxSimulationFilterShader는 두 충돌체의 바운딩 볼륨이 겹쳤을 때 어떤 작업을 해야 하는지, 콜백 함수를 불러야 한다면 어떤 함수를 불러야 하는지를 정의한다. 충돌체들이 서로 어떤 관계를 가져야 하는지 정의하는 함수가 바로 FilterShader라고 할 수 있겠다.

 이 함수는 PxFilterFlag를 반환하며, PxPairFlags의 값들을 수정하는 것으로 물리 시뮬레이션이 충돌체 쌍을 어떻게 처리해야하는지에 대한 정보를 저장한다.

 이 함수는 두 충돌체의 바운딩 볼륨이 겹치기 시작할때, 혹은 바운딩 볼륨이 겹쳐진 상태에서 한 오브젝트의 속성값이 바뀌었을때 호출된다.

 이 함수는 여러 스레드에 의해 수없이 많이 불리기 때문에 퍼포먼스에 심각한 영향을 줄 수 있다. 정말 제대로 물리 이벤트가 터졌을 때 호출되는 함수가 아니라 충돌체들이 어느정도 가까워지기만 하면 호출되기 때문이다. 함수가 어떤 상태에 따라 동작이 달라져서는 아니될 것이며, 심지어는 함수 호출조차 인라인 함수만 사용해야 한다.

 

 함수의 각 파라미터들에 대한 정보는 다음과 같다.

attribute0 [in] : 첫번째 오브젝트의 필터 속성

filterData0 [in] : 첫번째 오브젝트의 사용자 정의 필터 데이터

attribute1 [in] : 두번째 오브젝트의 필터 속성

filterData1 [in] : 두번째 오브젝트의 사용자 정의 필터 데이터

pairFlags [out] : 충돌체 쌍이 어떻게 처리되어야 할지 추가 정보를 저장할 플래그 정보

constantBlock [in] : 전역 필터 데이터, 물리 시뮬레이션을 시작하기 전에 전역으로 임의의 필터링 플래그를 세워두고 이 전역 플래그에 따라 필터링 동작을 다르게 하고 싶을 때 쓸 수 있다.

constantBlockSize [in] : 전역 필터 데이터의 사이즈

 

이중 attribute와 filterData는 PxShape로부터 데이터를 추출한다.

 

 FilterShader 함수를 부를 가치조차 없는 충돌체 쌍들도 존재한다. 다음과 같은 경우들은 바운딩 볼륨이 겹치든 말든 어차피 뭔가를 처리할 필요가 없기 때문에 무시된다.

- 위치와 회전이 항상 고정된 정적 충돌체들로만 이루어진 쌍(Pair of static rigid actors)

- 트랜스폼 상태를 바꿀 수는 있지만 물리법칙에 상태가 영향받지는 않는 키네마틱 충돌체로 이루어진 쌍 (Two Kinematic actors)

- 키네마틱 충돌체와 정적 충돌체로 이루어진 쌍 (A static rigid actor and a kinematic actor)

- etc...

 

static PxFilterFlags contactReportFilterShader(	PxFilterObjectAttributes attributes0, PxFilterData filterData0, 
												PxFilterObjectAttributes attributes1, PxFilterData filterData1,
												PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
{
	PX_UNUSED(attributes0);
	PX_UNUSED(attributes1);
	PX_UNUSED(filterData0);
	PX_UNUSED(filterData1);
	PX_UNUSED(constantBlockSize);
	PX_UNUSED(constantBlock);

	// all initial and persisting reports for everything, with per-point data
	pairFlags = PxPairFlag::eSOLVE_CONTACT | PxPairFlag::eDETECT_DISCRETE_CONTACT
			  |	PxPairFlag::eNOTIFY_TOUCH_FOUND 
			  | PxPairFlag::eNOTIFY_TOUCH_PERSISTS
			  | PxPairFlag::eNOTIFY_CONTACT_POINTS;
	return PxFilterFlag::eDEFAULT;
}

 

 SnippetContactReport 프로젝트의 filterShader는 충돌체 객체들의 속성이나 필터 데이터에 상관없이 무조건 고정된 플래그들을 반환하게 만들어놨다. 이 플래그들은 각각 다음과 같은 의미를 가진다.

 

 PxPairFlag::eSOLVE_CONTACT : 충돌이 일어났을때 충돌체들이 서로 파고들지 않게 dynamics solver로 충돌체들의 트랜스폼 값들을 수정하게 한다.

 PxPairFlag::eDETECT_DISCRETE_CONTACT : 충돌체들의 충돌여부를 체크하는 방식으로 discrete_contact를 사용한다. discrete는 연속적으로 충돌여부를 체크하는 CCD(Continuous Collision Detection) 방식과 대비되는 고효율 저정밀 충돌체크 방식이다.
PxPairFlag::eNOTIFY_TOUCH_FOUND : 충돌체간에 접촉을 시작할때 접촉 콜백 함수를 호출한다.
PxPairFlag::eNOTIFY_TOUCH_PERSISTS : 충돌체가 다른 충돌체와 접촉을 유지하고 있을 때 콜백 함수를 호출한다.
PxPairFlag::eNOTIFY_CONTACT_POINTS : 충돌체가 다른 충돌체와 접촉을 그만두었을 때 콜백 함수를 호출한다.

 

/**
\brief An interface class that the user can implement in order to receive simulation events.

With the exception of onAdvance(), the events get sent during the call to either #PxScene::fetchResults() or 
#PxScene::flushSimulation() with sendPendingReports=true. onAdvance() gets called while the simulation
is running (that is between PxScene::simulate() or PxScene::advance() and PxScene::fetchResults()).

\note SDK state should not be modified from within the callbacks. In particular objects should not
be created or destroyed. If state modification is needed then the changes should be stored to a buffer
and performed after the simulation step.

<b>Threading:</b> With the exception of onAdvance(), it is not necessary to make these callbacks thread safe as 
they will only be called in the context of the user thread.

@see PxScene.setSimulationEventCallback() PxScene.getSimulationEventCallback()
*/
class PxSimulationEventCallback
{
	...

 

 sceneDesc의 simulationEventCallback은 라이브러리의 사용자가 상속받아 구현할 수 있는 인터페이스다. PhysX의 시뮬레이션 연산은 Cpu에서 멀티스레딩으로 동작하거나 GPU 상에서 동작하는데, PxSimulationEventCallback의 함수들은 한 틱의 물리 시뮬레이션을 다 끝내고 사용자가 직접 메인 스레드에서 PxScene::fetchResults() 함수를 호출할 때 실행되기 때문에 스레드 안전성을 고민할 필요까지는 없다.

 

'자체엔진 Yunuty > PhysX' 카테고리의 다른 글

PhysX 코드 분석 : Hello World  (0) 2023.11.03