디버깅용 렌더링 및 RayIntersection등에 활용 가능한 박스 구조체
USceneComponent 부터 BoundingBox 멤버를 갖는다
이후 상속계층에서 BoundingBox를 사용하려면 InitBoundingBox , UpdateBoundingBox 함수 오버라이드해서 사용
BoundingBox는 박스를 가지고 있는 컴포넌트의 BeginPlay 시점에 월드에 박스를 등록한다
GetWorld()->AddBoundingBox 호출RemoveBoundBoxEndPlay 에서 해주고있다class UCubeComp : public UPrimitiveComponent
{
...
protected:
virtual void InitBoundingBox() override
{
FVector Min;
FVector Max;
UEngine::Get().GetRenderer()->GetPrimitiveLocalBounds(EPrimitiveType::EPT_Cube, Min, Max);
BoundingBox.Init(Min, Max);
}
};
World는 박스들만 따로 렌더링
void UWorld::RenderBoundingBoxes(URenderer& Renderer)
{
for (FBox* Box : BoundingBoxes)
{
if (Box && Box->IsValid())
Renderer.RenderBox(*Box);
}
}
Init
void FBox::Init(USceneComponent* InOwner, const FVector& InMin, const FVector& InMax)
{
Owner = InOwner;
FTransform WorldTransform = InOwner->GetWorldTransform();
Update(WorldTransform.GetMatrix());
InitialMin = Min = InMin;
Ini
Update
void FBox::Update(const FMatrix& InModelMatrix)
{
const FVector M0 = FVector(InModelMatrix.M[0][0], InModelMatrix.M[0][1], InModelMatrix.M[0][2]);
const FVector M1 = FVector(InModelMatrix.M[1][0], InModelMatrix.M[1][1], InModelMatrix.M[1][2]);
const FVector M2 = FVector(InModelMatrix.M[2][0], InModelMatrix.M[2][1], InModelMatrix.M[2][2]);
const FVector M3 = FVector(InModelMatrix.M[3][0], InModelMatrix.M[3][1], InModelMatrix.M[3][2]);
const FVector Origin = (InitialMin + InitialMax) * 0.5f;
const FVector Extent = (InitialMax - InitialMin) * 0.5f;
FVector NewOrigin = FVector::Replicate(Origin, 0) * M0;
NewOrigin = FVector::Replicate(Origin, 1) * M1 + NewOrigin;
NewOrigin = FVector::Replicate(Origin, 2) * M2 + NewOrigin;
NewOrigin = M3 + NewOrigin;
FVector NewExtent = FVector::Abs(FVector::Replicate(Extent, 0) * M0);
NewExtent += FVector::Abs(FVector::Replicate(Extent, 1) * M1);
NewExtent += FVector::Abs(FVector::Replicate(Extent, 2) * M2);
Min = NewOrigin - NewExtent;
Max = NewOrigin + NewExtent;
}
IntersectRay
FVector NormalizedDir = Ray.Direction.GetSafeNormal();
// 1. Ray의 방향 역수
// 0예외처리
if (NormalizedDir.X <= KINDA_SMALL_NUMBER ||
NormalizedDir.Y <= KINDA_SMALL_NUMBER ||
NormalizedDir.Z <= KINDA_SMALL_NUMBER)
return false;
FVector InvDir{
1.0f / NormalizedDir.X,
1.0f / NormalizedDir.Y,
1.0f / NormalizedDir.Z
};
// 2. Ray의 방향에 따라 TMin, TMax 계산
float T1 = (Min.X - Ray.Origin.X) * InvDir.X;
float T2 = (Max.X - Ray.Origin.X) * InvDir.X;
float T3 = (Min.Y - Ray.Origin.Y) * InvDir.Y;
float T4 = (Max.Y - Ray.Origin.Y) * InvDir.Y;
float T5 = (Min.Z - Ray.Origin.Z) * InvDir.Z;
float T6 = (Max.Z - Ray.Origin.Z) * InvDir.Z;
float TMin = FMath::Max(FMath::Max(FMath::Min(T1, T2), FMath::Min(T3, T4)), FMath::Min(T5, T6));
float TMax = FMath::Min(FMath::Min(FMath::Max(T1, T2), FMath::Max(T3, T4)), FMath::Max(T5, T6));
// Ray가 AABB 뒤에서 시작함
if (TMin < 0)
return false;
// 충돌하지 않음
if (TMin > TMax)
return false;
return true;
RasterizerState를 생성할 때 CullMode 와 FillMode 를 설정해 줄 수 있다
void URenderer::CreateRasterizerState()
{
D3D11_RASTERIZER_DESC RasterizerDesc = {};
RasterizerDesc.FillMode = D3D11_FILL_SOLID; // 채우기 모드
RasterizerDesc.CullMode = D3D11_CULL_BACK; // 백 페이스 컬링
RasterizerDesc.FrontCounterClockwise = FALSE;
HRESULT result = Device->CreateRasterizerState(&RasterizerDesc, &RasterizerState_Solid);
if (FAILED(result))
{
wchar_t errorMsg[256];
swprintf_s(errorMsg, L"Failed to create Rasterizer State! HRESULT: 0x%08X", result);
MessageBox(hWnd, errorMsg, L"Error", MB_ICONERROR | MB_OK);
return;
}
RasterizerDesc.FillMode = D3D11_FILL_WIREFRAME;
RasterizerDesc.CullMode = D3D11_CULL_NONE;
result = Device->CreateRasterizerState(&RasterizerDesc, &RasterizerState_Wireframe);
if (FAILED(result))
{
wchar_t errorMsg[256];
swprintf_s(errorMsg, L"Failed to create Wireframe Rasterizer State! HRESULT: 0x%08X", result);
MessageBox(hWnd, errorMsg, L"Error", MB_ICONERROR | MB_OK);
return;
}
}
void URenderer::PrepareRender()
{
...
switch (CurrentRasterizerStateType)
{
case EViewModeIndex::ERS_Solid:
CurrentRasterizerState = &RasterizerState_Solid;
break;
case EViewModeIndex::ERS_Wireframe:
CurrentRasterizerState = &RasterizerState_Wireframe;
break;
default:
break;
}
...
}