먼저 유틸리티 기능을 담당할 MathUtils 클래스를 생성합니다.
Engine/99.Headers/Math/PointInXXX, ClosestPoint 시리즈struct MathUtils
{
static bool PointInSphere(const Point3D& point, const Sphere3D sphere);
static Point3D ClosestPoint(const Sphere3D& sphere, const Point3D& point);
static bool PointInAABB(const Point3D& point, const AABB3D& aabb);
static Point3D ClosestPoint(const AABB3D& aabb, const Point3D& point);
static bool PointInOBB(const Point3D& point, const OBB3D& obb);
static Point3D ClosestPoint(const OBB3D& obb, const Point3D& point);
static bool PointOnPlane(const Point3D& point, const Plane3D& plane);
static Point3D ClosestPoint(const Plane3D& plane, const Point3D& point);
static bool PointOnLine(const Point3D& point, const Line3D& line);
static Point3D ClosestPoint(const Line3D& line, const Point3D& point);
static bool PointOnRay(const Point3D& point, const Ray3D& ray);
static Point3D ClosestPoint(const Ray3D& ray, const Point3D& point);
};
PointInSphere)구 내부에 점이 있는지를 판단하려면, 중심과 점 사이의 거리를 구하고 그것이 반지름보다 작거나 같은지를 비교합니다.
bool MathUtils::PointInSphere(const Point3D& point, const Sphere3D sphere)
{
float distSq = (point - sphere.position).LengthSquared();
return distSq <= sphere.radius * sphere.radius;
}
ClosestPoint)Point3D MathUtils::ClosestPoint(const Sphere3D& sphere, const Point3D& point)
{
Vec3 dir = (point - sphere.position).Normalized();
return sphere.position + dir * sphere.radius;
}
PointInAABB)각 축의 최소/최대값 사이에 있는지 체크합니다.
bool MathUtils::PointInAABB(const Point3D& point, const AABB3D& aabb)
{
Point3D min = AABB3D::GetMin(aabb);
Point3D max = AABB3D::GetMax(aabb);
return (point.x >= min.x && point.x <= max.x &&
point.y >= min.y && point.y <= max.y &&
point.z >= min.z && point.z <= max.z);
}
Point3D MathUtils::ClosestPoint(const AABB3D& aabb, const Point3D& point)
{
Point3D min = AABB3D::GetMin(aabb);
Point3D max = AABB3D::GetMax(aabb);
return Point3D(
std::clamp(point.x, min.x, max.x),
std::clamp(point.y, min.y, max.y),
std::clamp(point.z, min.z, max.z)
);
}
PointInOBB)OBB는 축이 회전되어 있는 박스이므로, 내적을 통한 투영으로 판별합니다.
bool MathUtils::PointInOBB(const Point3D& point, const OBB3D& obb)
{
Vec3 dir = point - obb.position;
auto right = obb.orientation.Right();
auto up = obb.orientation.Up();
auto forward = obb.orientation.Backward();
vector<Vec3> axis = { right, up, forward };
vector<float> size = { obb.size.x, obb.size.y, obb.size.z };
for (int i = 0; i < 3; ++i)
{
float proj = dir.Dot(axis[i]);
if (proj > size[i] || proj < -size[i])
return false;
}
return true;
}
Point3D MathUtils::ClosestPoint(const OBB3D& obb, const Point3D& point)
{
Vec3 dir = point - obb.position;
Point3D result = obb.position;
auto right = obb.orientation.Right();
auto up = obb.orientation.Up();
auto forward = obb.orientation.Backward();
vector<Vec3> axis = { right, up, forward };
vector<float> size = { obb.size.x, obb.size.y, obb.size.z };
for (int i = 0; i < 3; ++i)
{
float proj = dir.Dot(axis[i]);
proj = std::clamp(proj, -size[i], size[i]);
result += axis[i] * proj;
}
return result;
}
PointOnPlane)평면은 normal · point == distance를 만족하면 해당 평면 위에 있습니다.
bool MathUtils::PointOnPlane(const Point3D& point, const Plane3D& plane)
{
return fabs(point.Dot(plane.normal) - plane.distance) < FLT_EPSILON;
}
Point3D MathUtils::ClosestPoint(const Plane3D& plane, const Point3D& point)
{
float dot = point.Dot(plane.normal);
float dist = dot - plane.distance;
return point - plane.normal * dist;
}
PointOnLine)선분 상의 점은 해당 선분과의 가장 가까운 점이 자신일 때 포함됩니다.
bool MathUtils::PointOnLine(const Point3D& point, const Line3D& line)
{
Point3D closest = ClosestPoint(line, point);
return (closest - point).LengthSquared() < FLT_EPSILON;
}
Point3D MathUtils::ClosestPoint(const Line3D& line, const Point3D& point)
{
Vec3 lineVec = line.end - line.start;
float t = (point - line.start).Dot(lineVec) / lineVec.Dot(lineVec);
t = std::clamp(t, 0.0f, 1.0f);
return line.start + lineVec * t;
}
PointOnRay)광선은 방향성이 있으므로, 시작점과 동일하거나, 방향이 같고 출발점으로부터 동일 선상에 있어야 포함됩니다.
bool MathUtils::PointOnRay(const Point3D& point, const Ray3D& ray)
{
if (point == ray.origin)
return true;
Vec3 toPoint = (point - ray.origin).Normalized();
return fabs(toPoint.Dot(ray.direction) - 1.0f) < FLT_EPSILON;
}
Point3D MathUtils::ClosestPoint(const Ray3D& ray, const Point3D& point)
{
float t = (point - ray.origin).Dot(ray.direction);
t = std::max(0.0f, t); // 음수면 시작점보다 뒤쪽 → ray 시작점으로 고정
return ray.origin + ray.direction * t;
}