I don't know why, but ray-triangle intersection algorithm (Möller-Trumbore and Watertight) works with 2 directions.
from origin to end - how it must work
from end to origin - the problem
white cross - an unnecessary intersection
I tried to use 3 component vectors, so I have removed all W values from code.
Ray
class Ray
{
public:
Ray(){}
~Ray(){}
Vector4 m_origin;
Vector4 m_end;
Vector4 m_direction;
void update()
{
m_end._f32[3] = 0.f;
m_origin._f32[3] = 0.f;
m_direction._f32[ 0 ] = m_end._f32[0] - m_origin._f32[0];
m_direction._f32[ 1 ] = m_end._f32[1] - m_origin._f32[1];
m_direction._f32[ 2 ] = m_end._f32[2] - m_origin._f32[2];
m_direction._f32[ 3 ] = 0.f;
m_direction.normalize(); // without W
}
};
Triangle and algorithm
struct Triangle
{
Vector4 v1; // vertex 1 position
Vector4 v2; // vertex 2 position
Vector4 v3; // vertex 3 position
Vector4 e1; // edge, see update()
Vector4 e2; // edge, see update()
void update()
{
e1 = Vector4( v2._f32[0] - v1._f32[0],
v2._f32[1] - v1._f32[1],
v2._f32[2] - v1._f32[2],
0.f);
e2 = Vector4( v3._f32[0] - v1._f32[0],
v3._f32[1] - v1._f32[1],
v3._f32[2] - v1._f32[2],
0.f);
}
// Möller-Trumbore algorithm
bool rayTest_MT( const Ray& ray, bool withBackFace, f32& T, f32& U, f32& V, f32& W )
{
Vector4 pvec = ray.m_direction.cross_return(e2);
f32 det = e1.dot(pvec);
if( withBackFace )
{
if( std::abs(det) < 0.0000001f && det > -0.0000001f )
return false;
}
else
{
if( det < 0.0000001f && det > -0.0000001f )
return false;
}
Vector4 tvec(
ray.m_origin._f32[0] - v1._f32[0],
ray.m_origin._f32[1] - v1._f32[1],
ray.m_origin._f32[2] - v1._f32[2],
0.f);
//tvec.setW(1.f);//...
f32 inv_det = 1.f / det;
U = tvec.dot(pvec) * inv_det;
if( U < 0.f || U > 1.f )
return false;
Vector4 qvec = tvec.cross_return(e1);
V = ray.m_direction.dot(qvec) * inv_det;
if( V < 0.f || U + V > 1.f )
return false;
T = e2.dot(qvec) * inv_det;
W = 1.f - U - V;
return true;
}
};
Simple vector
Vector4
{
public:
f32 _f32[4];
Vector4()
{
_f32[ 0 ] = 0.f;
_f32[ 1 ] = 0.f;
_f32[ 2 ] = 0.f;
_f32[ 3 ] = 0.f;
}
.........
Maybe I NEED to use W component...somewhere🤔 NO. see my comment.
The last value calculated T
is the unit distance on the ray from the ray origin. Where T == 0.0f
is at the origin, T == 1.0f
is at the ray end, T > 1.0f
are past the ray end and T < 0.0f
are before the ray origin.
Thus you need to check T
to make sure that the intercept is on the correct part of the ray. Examples
Ahead of the not including the origin. return T > EPSILON;
Ahead of the ray including the origin. return T > -EPSILON;
On the ray including origin and end. return T > -EPSILON && T < 1.0f + EPSILON;
On the ray excluding origin and end. return T > EPSILON && T < 1.0f - EPSILON;
and so on...
We use EPSILON
to deal with floating point errors, for this algorithm you may want to use a larger value than EPSILON
especially if the angle between the ray and the polygon normal is close to 90deg