DragonNest/Client/EtCutSceneTool/IntersectUtil.cpp
2024-12-19 09:48:26 +08:00

213 lines
No EOL
5.8 KiB
C++

#include "stdafx.h"
#include "EternityEngine.h"
#include "intersectUtil.h"
const float EPSILON = 0.000001f;
CIntersectUtil::CIntersectUtil(void)
{
}
CIntersectUtil::~CIntersectUtil(void)
{
}
// :*: 교차 판정의 원리 및 순서 :*:
// 1. 우선, 광선의 시작 점 뒤쪽에 있는지 판정해서 뒤쪽에 있으면 100% 충돌 실패이므로 그대로 리턴한다.
// 광선의 방향 벡터와 광선의 시작점에서 구의 중심으로 향한 벡터의 내적이 음수, 즉, 두 직선이 이루는
// 각이 둔각일 때이고, 광선의 시작점이 구의 외부일 경우가 되겠다.
//
// 2. 그 다음에, 광선 시작점에서 구의 중심까지의 거리와 광선과 구의 중심까지 그은 선분과 피타고라스의 법칙을 사용하여
// 두 직선의 거리를 구한다. 그 거리가 반지름 보다 크면 충돌실패.
bool CIntersectUtil::RaySphereIntersect( const IN EtVector3& vRayStartPos, const IN EtVector3& vRayDir /* 반드시 정규화 된 벡터 */,
const IN float fRadius, const IN EtVector3& vSpherePos,
OUT EtVector3* pvNearIntersectPos, OUT EtVector3* pvFarIntersectPos )
{
EtVector3 vRayStartToCenter;
float fDotResult = 0.0f;
float fLengthSqA = 0.0f; // 광선 시작점에서 구의 중심까지의 거리 제곱
float fLengthSqB = 0.0f; // 구의 중심에서 구의 접점까지의 거리 제곱
float fLengthSqC = 0.0f; // 광선 시작점에서 구와의 접점까지의 거리 제곱
float fRadiusSq = fRadius * fRadius;
vRayStartToCenter = vSpherePos - vRayStartPos;
fDotResult = EtVec3Dot( &vRayDir, &vRayStartToCenter );
fLengthSqA = EtVec3LengthSq( &vRayStartToCenter );
if( fDotResult < 0.0f && fLengthSqA > fRadiusSq )
return false;
fLengthSqC = fDotResult * fDotResult;
fLengthSqB = fLengthSqA - fLengthSqC;
if( fRadiusSq < fLengthSqB )
return false;
if( NULL != pvNearIntersectPos || NULL != pvFarIntersectPos )
{
fLengthSqC = fRadiusSq - fLengthSqB;
if( pvNearIntersectPos )
*pvNearIntersectPos = vRayStartPos + (fDotResult - sqrt(fLengthSqC)) * vRayDir;
if( pvFarIntersectPos )
*pvFarIntersectPos = vRayStartPos + (fDotResult + sqrt(fLengthSqC)) * vRayDir;
}
return true;
}
// 직선과 평면의 교차 판정
//int RayTriIntersect(CVector orig, CVector dir,CVector vert0, CVector vert1, CVector vert2,
// Scalar *t, Scalar *u, Scalar *v)
//{
// CVector edge1, edge2, tvec, pvec, qvec;
// Scalar det,inv_det;
//
// /* find vectors for two edges sharing vert0 */
// SUB(edge1, vert1, vert0);
// SUB(edge2, vert2, vert0);
//
// /* begin calculating determinant - also used to calculate U parameter */
// CROSS(pvec, dir, edge2);
//
// /* if determinant is near zero, ray lies in plane of triangle */
// det = DOT(edge1, pvec);
//
//#ifdef TEST_CULL /* define TEST_CULL if culling is desired */
// if (det < EPSILON)
// return 0;
//
// /* calculate distance from vert0 to ray origin */
// SUB(tvec, orig, vert0);
//
// /* calculate U parameter and test bounds */
// *u = DOT(tvec, pvec);
// if (*u < 0.0 || *u > det)
// return 0;
//
// /* prepare to test V parameter */
// CROSS(qvec, tvec, edge1);
//
// /* calculate V parameter and test bounds */
// *v = DOT(dir, qvec);
// if (*v < 0.0 || *u + *v > det)
// return 0;
//
// /* calculate t, scale parameters, ray intersects triangle */
// *t = DOT(edge2, qvec);
// inv_det = 1.0 / det;
// *t *= inv_det;
// *u *= inv_det;
// *v *= inv_det;
//#else /* the non-culling branch */
// if (det > -EPSILON && det < EPSILON)
// return 0;
// inv_det = 1.0 / det;
//
// /* calculate distance from vert0 to ray origin */
// SUB(tvec, orig, vert0);
//
// /* calculate U parameter and test bounds */
// *u = DOT(tvec, pvec) * inv_det;
// if (*u < 0.0 || *u > 1.0)
// return 0;
//
// /* prepare to test V parameter */
// CROSS(qvec, tvec, edge1);
//
// /* calculate V parameter and test bounds */
// *v = DOT(dir, qvec) * inv_det;
// if (*v < 0.0 || *u + *v > 1.0)
// return 0;
//
// /* calculate t, ray intersects triangle */
// *t = DOT(edge2, qvec) * inv_det;
//#endif
// return 1;
// 직선과 폴리곤의 교차판정
bool CIntersectUtil::RayTriIntersect( const IN EtVector3& vRayStart, const IN EtVector3& vRayDir, /* 정규화된 벡터 */
const IN EtVector3 vVert0, const IN EtVector3 vVert1, const IN EtVector3 vVert2,
float *t, float *u, float *v )
{
EtVector3 vEdge[ 2 ];
EtVector3 vTvec, vPvec, vQvec;
float fDet = 0.0f;
float fInv_det = 0.0f;
// vVert0 을 공유하는 두 개의 변을 구한다.
vEdge[ 0 ] = vVert1 - vVert0;
vEdge[ 1 ] = vVert2 - vVert0;
// 행렬식을 계산하기 시작한다. 이 값은 U 값을 계산할 때도 쓰인다.
EtVec3Cross( &vPvec, &vRayDir, &vEdge[ 1 ] );
// 행렬식이 제로에 가까워지면 Ray가 평면과 평행하다는 사실~!! 그럼 절대로 충돌이 일어날 수가 없지.
fDet = EtVec3Dot( &vEdge[ 0 ], &vPvec );
#ifdef TEST_CULL // 후면 컬링된 것 까지 전부 계산?
if( det < EPSILON )
return false;
// vVert0 부터 직선의 원점까지의 거리
vTvec = vRayStart - vVert0;
// U 값을 계산하고 범위안에 있는지 체크한다.
*u = EtVec3Dot( &vTvec, &vPvec );
if( *u < 0.0f || *u > fDet )
return false;
// V 값 테스트를 위한 준비
EtVec3Cross( &vQvec, &vTvec, &vEdge[ 0 ] );
// V 값이 범위안에 있는지 체크한다.
*v = EtVec3Dot( &vRayDir, &vQvec );
if( *v < 0.0f || *u + *v > det )
return false;
// T 값을 계산하고 확대 파라메터를 만들어서 적용해준다.
*t = EtVec3Dot( &vRayDir, &vQvec );
fInv_det = 1.0f / fDet;
*t *= fInv_det;
*u *= fInv_det;
*v *= fInv_det;
#else // 컬링을 안 쓸경우.
if( fDet > -EPSILON && fDet < EPSILON )
return false;
fInv_det = 1.0f / fDet;
// vVert0 부터 RayStart 까지의 거리
vTvec = vRayStart - vVert0;
// U 값 계산
*u = EtVec3Dot( &vTvec, &vPvec ) * fInv_det;
if( *u < 0.0f || *u > 1.0f )
return false;
// V 값 계산
EtVec3Cross( &vQvec, &vTvec, &vEdge[ 0 ] );
*v = EtVec3Dot( &vRayDir, &vQvec ) * fInv_det;
if( *v < 0.0f || *u + *v > 1.0f )
return 0;
*t = EtVec3Dot( &vEdge[ 1 ], &vQvec ) * fInv_det;
#endif
return true;
}