반응형
Point test라 해서 각각의 물체에 대해서 어떠한 점이 포함이 되어 있는지 여부를 체크하는 걸 진행한다.
1. MathUtils 클래스 만들기
Engine/99. Headers/Math필터에 MathUtils라는 클래스를 추가한다.
Point contained in sphere
Point contained in AABB
Point contained in OBB
Point on surface of plane
Point on line segment
Point on ray
이걸 체크할 것이다.
응용 범위가 넓고 기본이 되는 내용이다.
SimpleMath를 굳이 다 구현하는 건 쉽지 않다. 하지만 SimpleMath 코드를 봐도 이해가 안가는 경우가 있기 때문에 한번씩은 구현해보는 연습을 해야 한다. 그걸 이 MathUtils에서 해본다.
#pragma once
#include "Primitive3D.h"
struct MathUtils
{
//-------------------
// Point Test
//-------------------
// Sphere2Point
static bool PointInSphere(const Point3D& point, const Sphere3D sphere);
static Point3D ClosestPoint(const Sphere3D& sphere, const Point3D& point);
// AABB2Point
static bool PointInAABB(const Point3D& point, const AABB3D& aabb);
static Point3D ClosestPoint(const AABB3D& aabb, const Point3D& point);
// OBB2Point
static bool PointInOBB(const Point3D& point, const OBB3D& obb);
static Point3D ClosestPoint(const OBB3D& obb, const Point3D& point);
// Plane2Point
static bool PointOnPlane(const Point3D& point, const Plane3D& plane);
static Point3D ClosestPoint(const Plane3D& plane, const Point3D& point);
// Line2Point
static bool PointOnLine(const Point3D& point, const Line3D& line);
static Point3D ClosestPoint(const Line3D& line, const Point3D& point);
// Ray2Point
static bool PointOnRay(const Point3D& point, const Ray3D& ray);
static Point3D ClosestPoint(const Ray3D& ray, const Point3D& point);
};
#include "pch.h"
#include "MathUtils.h"
// 안에 소속이 되어 있는지
bool MathUtils::PointInSphere(const Point3D& point, const Sphere3D sphere)
{
float magSq = (point - sphere.position).LengthSquared();
float radSq = sphere.radius * sphere.radius;
return magSq <= radSq;
}
// 가장 가까이 있는 표면의 점은
Point3D MathUtils::ClosestPoint(const Sphere3D& sphere, const Point3D& point)
{
Vec3 sphereToPointDir = (point - sphere.position);
sphereToPointDir.Normalize();
return sphere.position + sphereToPointDir * sphere.radius;
}
bool MathUtils::PointInAABB(const Point3D& point, const AABB3D& aabb)
{
Point3D min = AABB3D::GetMin(aabb);
Point3D max = AABB3D::GetMax(aabb);
if (point.x < min.x || point.y < min.y || point.z < min.z)
return false;
if (point.x > max.x || point.y > max.y || point.z > max.z)
return false;
return true;
}
Point3D MathUtils::ClosestPoint(const AABB3D& aabb, const Point3D& point)
{
Point3D result = point;
Point3D minPt = AABB3D::GetMin(aabb);
Point3D maxPt = AABB3D::GetMax(aabb);
result.x = max(result.x, minPt.x);
result.y = max(result.y, minPt.y);
result.z = max(result.z, minPt.z);
result.x = min(result.x, maxPt.x);
result.y = min(result.y, maxPt.y);
result.z = min(result.z, maxPt.z);
return result;
}
Point3D MathUtils::ClosestPoint(const OBB3D& obb, const Point3D& point)
{
Point3D result = obb.position;
Vec3 dir = point - obb.position;
vector<Vec3> axis;
axis.push_back(obb.orientation.Right());
axis.push_back(obb.orientation.Up());
axis.push_back(obb.orientation.Backward());
vector<float> size;
size.push_back(obb.size.x);
size.push_back(obb.size.y);
size.push_back(obb.size.z);
for (int i = 0; i < 3; ++i)
{
float distance = dir.Dot(axis[i]);
if (distance > size[i])
distance = size[i];
if (distance < -size[i])
distance = -size[i];
result = result + (axis[i] * distance);
}
return result;
}
// x,y,z축으로 각각 프로젝션을 하는데 내적을 해서 해당하는 축 자체의 성분을 본 다음에 -, +범위를 벗어나면 안되는 거
// 이런 내적응 응용하는 센스를 기억해야 한다.
bool MathUtils::PointInOBB(const Point3D& point, const OBB3D& obb)
{
Vec3 dir = point - obb.position;
vector<Vec3> axis;
axis.push_back(obb.orientation.Right());
axis.push_back(obb.orientation.Up());
axis.push_back(obb.orientation.Backward()); // look 벡터
vector<float> size;
size.push_back(obb.size.x);
size.push_back(obb.size.y);
size.push_back(obb.size.z);
for (int32 i = 0; i < 3; i++)
{
float distance = dir.Dot(axis[i]);
if (distance > size[i])
return false;
if (distance < -size[i])
return false;
}
return true;
}
// 어떤 점에 평면에 있다는 건 평면을 대상으로 distance를 어떻게 구했는지 생각하면 된다.
// 평면에 점이 있다면 점의 위치랑 normal이랑 내적을 한 결과물이 distance가 나와야 한다.
// 코드를 보고 어떠한 의미로 만들어져 있구나를 유추할 수 있어야 한다.
bool MathUtils::PointOnPlane(const Point3D& point, const Plane3D& plane)
{
float dot = point.Dot(plane.normal);
// CMP(dot - plane.distance, 0.0f);
return dot == plane.distance;
}
Point3D MathUtils::ClosestPoint(const Plane3D& plane, const Point3D& point)
{
float dot = point.Dot(plane.normal);
float distance = dot - plane.distance;
return point - plane.normal * distance;
}
// 손코딩 문제로 나올 확률이 높다
Point3D MathUtils::ClosestPoint(const Line3D& line, const Point3D& point)
{
Vec3 lVec = line.end - line.start; // Line Vector
float t = (point - line.start).Dot(lVec) / lVec.Dot(lVec);
t = fmaxf(t, 0.0f); // Clamp to 0
t = fminf(t, 1.0f); // Clamp to 1
return line.start + lVec * t;
}
bool MathUtils::PointOnLine(const Point3D& point, const Line3D& line)
{
Point3D closest = ClosestPoint(line, point);
float distanceSq = (closest - point).LengthSquared();
// CMP(distanceSq, 0.0f);
return distanceSq == 0.f;
}
bool MathUtils::PointOnRay(const Point3D& point, const Ray3D& ray)
{
if (point == ray.origin)
return true;
Vec3 norm = point - ray.origin;
norm.Normalize();
float diff = norm.Dot(ray.direction);
return diff == 1.0f; // Consider using epsilon!
}
Point3D MathUtils::ClosestPoint(const Ray3D& ray, const Point3D& point)
{
float t = (point - ray.origin).Dot(ray.direction);
t = fmaxf(t, 0.0f);
return Point3D(ray.origin + ray.direction * t);
}
내적 시리즈를 잘 응용할 수 있어야 한다가 결론이다.
이 시리즈를 대략적으로 설명할 수 있을 정도로 머리에 넣는게 수학 면접을 준비한다면 의미가 있을 것이다.
포인트 테스트는 수학적인 연습이었고, 그거 보다는 물체와 물체끼리 어떻게 레이케스팅이 되어야 하고 인터섹션이 일어나는지, 물체끼리 포개졌을 때 어떤 식으로 일어나는지가 더 핵심이다.
반응형
'DirectX' 카테고리의 다른 글
85_Raycasting(수학) (0) | 2024.03.31 |
---|---|
84_Intersection (수학) (0) | 2024.03.31 |
82_기본 도형 (수학) (0) | 2024.03.30 |
81_Terrain Picking (0) | 2024.03.29 |
80_Picking (0) | 2024.03.28 |
댓글