- 직선 - OBB 충돌검사는 AABB와 원리는 동일하고, 대신 회전된 축을 기준 좌표계로 하여 계산하면 된다.
- 즉 Ray의 원점과 방향을 회전된 축을 기준으로 맞춰서 계산하면 된다.
bool RaycastOBB(const Ray& ray, const OBBCollider* obb, float maxDistance, float& outDistance) {
// OBB는 AABB와 원리는 동일하고, 대신 회전된 축을 기준으로 바꿔서 계산하면 됨
// 즉 Ray의 원점과 방향을 회전된 축을 기준으로 맞춰서 계산
// OBB를 AABB 좌표계로 변환
float rad = degToRad(obb->GetRotation());
float cosR = std::cos(rad);
float sinR = std::sin(rad);
// OBB의 중심 (월드 좌표)
Vector2<float> obbCenter(static_cast<float>(obb->GetX()), static_cast<float>(obb->GetY()));
float halfWidth = obb->GetWidth() / 2.0f;
float halfHeight = obb->GetHeight() / 2.0f;
// 광선의 원점을 OBB 중심 기준으로 변환
Vector2<float> localOrigin = ray.origin - obbCenter;
// 광선의 원점을 OBB의 로컬 좌표계로 변환
Vector2<float> transformedOrigin(
localOrigin.x * cosR + localOrigin.y * sinR,
-localOrigin.x * sinR + localOrigin.y * cosR
);
// OBB의 로컬 AABB 범위 (중심이 0,0인 정렬된 AABB)
Vector2<float> localMin(-halfWidth, -halfHeight);
Vector2<float> localMax(halfWidth, halfHeight);
// 광선의 시작점이 OBB 내부에 있으면 바로 충돌로 처리
if (transformedOrigin.x >= localMin.x && transformedOrigin.x <= localMax.x &&
transformedOrigin.y >= localMin.y && transformedOrigin.y <= localMax.y) {
outDistance = 0.0f;
return true;
}
// 광선 방향도 OBB의 로컬 좌표계로 변환
Vector2<float> transformedDirection(
ray.direction.x * cosR + ray.direction.y * sinR,
-ray.direction.x * sinR + ray.direction.y * cosR
);
float txmin, txmax, tymin, tymax;
// X축 Slab Test
if (transformedDirection.x != 0.0f) {
txmin = (localMin.x - transformedOrigin.x) / transformedDirection.x;
txmax = (localMax.x - transformedOrigin.x) / transformedDirection.x;
if (txmin > txmax) std::swap(txmin, txmax);
}
else {
if (transformedOrigin.x < localMin.x || transformedOrigin.x > localMax.x)
return false;
txmin = -FLT_MAX;
txmax = FLT_MAX;
}
// Y축 Slab Test
if (transformedDirection.y != 0.0f) {
tymin = (localMin.y - transformedOrigin.y) / transformedDirection.y;
tymax = (localMax.y - transformedOrigin.y) / transformedDirection.y;
if (tymin > tymax) std::swap(tymin, tymax);
}
else {
if (transformedOrigin.y < localMin.y || transformedOrigin.y > localMax.y)
return false;
tymin = -FLT_MAX;
tymax = FLT_MAX;
}
// Slab 교차 구간 확인
if ((txmin > tymax) || (tymin > txmax))
return false;
// 최종 진입/이탈 지점 계산
float t_min = std::max<float>(txmin, tymin);
float t_max = std::min(txmax, tymax);
// Ray가 OBB 뒤쪽에서 시작하는 경우
if (t_min < 0)
return false;
// 충돌이 maxDistance 이내인지 확인
if (t_max <= maxDistance) {
outDistance = t_min;
return true;
}
return false;
}