An essay in 3d computation, ray collision
Fun work with Elric, using its OpenGL library we worked together on a collision detection algorithm in order to compute as fast as it would be possible a 3d lighting map. His work was inspired and motivated by the photo-realism rendering of radiosity algorithms.
Download raycol.c
Read raycol.c
- /*
- * extract of the Silence OpenGL project
- * (sglMath.cpp part)
- * v1.0
- *
- * 02/05/02
- *
- * by SINTES 'elric' Arnaud / PESCE 'joke' François
- * elric(at)silentbreed.com / joke(at)r00tworld.com
- *
- */
- /*
- * During the development, we (elric+joke) have to found the
- * _fastest_ algo for Ray/Triangle collision.
- *
- * here is our result.
- *
- */
- /*
- * NOTES:
- *
- * assumes that it's basic C/OpenGL/SGL syntax.
- *
- * GLfloat is standard C float
- * GLBoolean is standard C bool
- * GL_TRUE is true and GL_FALSE is false
- *
- * SGLpoint3f is a typedef struct covered with 3 GLfloat x, y and z
- *
- * SGL_EPSILUM is equal to 0.0005f
- *
- */
- /*
- * the sglRayCol function parameters are:
- *
- * p1, p2 : start and end of the ray line
- * pa, pb, pc : points of the triangle to test intersection with
- * function return false if no collision detected,
- * else true and p is set to the collision point
- *
- */
- /*
- * min/max funcs for imediate rejection
- */
- static inline GLfloat min2(GLfloat x1, GLfloat x2)
- {
- return (x1 <= x2) ? x1 : x2;
- }
- static inline GLfloat max2(GLfloat x1, GLfloat x2)
- {
- return (x1 >= x2) ? x1 : x2;
- }
- static inline GLfloat min3(GLfloat x1, GLfloat x2, GLfloat x3)
- {
- return (x1 <= x2) ? ((x1 <= x3) ? x1 : x3) : ((x2 <= x3) ? x2 : x3);
- }
- static inline GLfloat max3(GLfloat x1, GLfloat x2, GLfloat x3)
- {
- return (x1 >= x2) ? ((x1 >= x3) ? x1 : x3) : ((x2 >= x3) ? x2 : x3);
- }
- static inline GLboolean
- sglImmediateReject(const GLfloat p1x, const GLfloat p1y, const GLfloat p1z,
- const GLfloat p2x, const GLfloat p2y, const GLfloat p2z,
- const GLfloat pax, const GLfloat pay, const GLfloat paz,
- const GLfloat pbx, const GLfloat pby, const GLfloat pbz,
- const GLfloat pcx, const GLfloat pcy, const GLfloat pcz)
- {
- /*
- * According to "Introduction to algorithmic" by Cormen Rivest Leiserson :
- *
- * > 2 step :
- * > 1. Immediate reject : the cube containing the segment must intersect the polygon.
- * > lp stand for limit point.
- * > Cube1 = (lp1, lp2);
- * > Cube2 = (lp3, lp4);
- */
- /* limit boxes */
- GLfloat lpM, lpm;
- /* lp4z lp1z */
- lpM = max3(paz, pbz, pcz);
- lpm = min2(p1z, p2z);
- if (lpM < lpm)
- return GL_TRUE;
- /* lp2z lp3z */
- lpM = max2(p1z, p2z);
- lpm = min3(paz, pbz, pcz);
- if (lpM < lpm)
- return GL_TRUE;
- /* lp2x lp3x */
- lpM = max2(p1x, p2x);
- lpm = min3(pax, pbx, pcx);
- if (lpM < lpm)
- return GL_TRUE;
- /* lp4x lp1x */
- lpM = max3(pax, pbx, pcx);
- lpm = min2(p1x, p2x);
- if (lpM < lpm)
- return GL_TRUE;
- /* lp4y lp1y */
- lpM = max3(pay, pby, pcy);
- lpm = min2(p1y, p2y);
- if (lpM < lpm)
- return GL_TRUE;
- /* lp2y lp3y */
- lpM = max2(p1y, p2y);
- lpm = min3(pay, pby, pcy);
- if (lpM < lpm)
- return GL_TRUE;
- return GL_FALSE;
- }
- GLboolean
- sglRayCol(const SGLpoint3f p1, const SGLpoint3f p2, const SGLpoint3f pa,
- const SGLpoint3f pb, const SGLpoint3f pc, SGLpoint3f * p)
- {
- /*
- * Determine whether or not the line segment p1,p2
- * Intersects the 3 vertex facet bounded by pa,pb,pc
- *
- * Return SGL_NULL if no collison or the intersection
- * point p else
- *
- *
- * The equation of the line is p = p1 + mu (p2 - p1)
- * The equation of the plane is a x + b y + c z + d = 0
- * n.x x + n.y y + n.z z + d = 0
- *
- * this version is heavily inspired from
- * softSurfer (www.softsurfer.com)
- * and hardly optimised.
- */
- GLfloat p1x, p1y, p1z;
- GLfloat p2x, p2y, p2z;
- GLfloat pax, pay, paz;
- GLfloat pbx, pby, pbz;
- GLfloat pcx, pcy, pcz;
- GLfloat uu, uv, uw;
- GLfloat vu, vv, vw;
- GLfloat nu, nv, nw;
- GLfloat diru, dirv, dirw;
- GLfloat w0u, w0v, w0w;
- GLfloat a, b, r, D, s, t;
- GLfloat uud, uvd, vvd;
- GLfloat wu, wv, ww;
- GLfloat wud, wvd;
- p1x = p1.x;
- p1y = p1.y;
- p1z = p1.z;
- p2x = p2.x;
- p2y = p2.y;
- p2z = p2.z;
- pax = pa.x;
- pay = pa.y;
- paz = pa.z;
- pbx = pb.x;
- pby = pb.y;
- pbz = pb.z;
- pcx = pc.x;
- pcy = pc.y;
- pcz = pc.z;
- /* imediate reject */
- if (sglImmediateReject(p1x, p1y, p1z, p2x, p2y, p2z, pax, pay, paz, pbx, pby, pbz, pcx, pcy, pcz))
- return GL_FALSE;
- /* get triangle edge vectors and plane normal */
- uu = pbx - pax;
- uv = pby - pay;
- uw = pbz - paz;
- vu = pcx - pax;
- vv = pcy - pay;
- vw = pcz - paz;
- nu = uv * vw - uw * vv;
- nv = uw * vu - uu * vw;
- nw = uu * vv - uv * vu;
- /* ray direction vector */
- diru = p2x - p1x;
- dirv = p2y - p1y;
- dirw = p2z - p1z;
- b = nu * diru + nv * dirv + nw * dirw;
- if (b < SGL_EPSILUM)
- /* reduction of rayCol with Culled triangles
- * consider ray parallel to triangle plane and
- * ray lies in triangle plane refer to cull test
- */
- return GL_FALSE;
- /* old code:
- *
- * if( b < 0 ) b = -b; // invert culling
- * if( b < SGL_EPSILUM) { // ray is parallel to triangle plane
- * return GL_FALSE;
- * if( a == 0) // ray lies in triangle plane
- * return GL_TRUE; // in fact, return another value
- * else
- * return GL_FALSE; // ray disjoint from plane
- * }
- */
- /* get intersect point of ray with triangle plane */
- w0u = p1x - pax;
- w0v = p1y - pay;
- w0w = p1z - paz;
- a = nu * w0u + nv * w0v + nw * w0w;
- r = -a / b;
- if (r < 0 || r > 1) /* ray goes away from triangle */
- return GL_FALSE; /* => no intersect */
- /* intersect point of ray and plane */
- p->x = p1x + r * diru;
- p->y = p1y + r * dirv;
- p->z = p1z + r * dirw;
- /* is p inside the triangle ? */
- uud = uu * uu + uv * uv + uw * uw;
- uvd = uu * vu + uv * vv + uw * vw;
- vvd = vu * vu + vv * vv + vw * vw;
- wu = p->x - pax;
- wv = p->y - pay;
- ww = p->z - paz;
- wud = wu * uu + wv * uv + ww * uw;
- wvd = wu * vu + wv * vv + ww * vw;
- D = 1 / (uvd * uvd - uud * vvd);
- /* get and test parametric coords */
- s = (uvd * wvd - vvd * wud) * D;
- if (s < 0 || s > 1) /* p is outside triangle */
- return GL_FALSE;
- t = (uvd * wud - uud * wvd) * D;
- if (t < 0 || (s + t) > 1) /* p is outside triangle */
- return GL_FALSE;
- return GL_TRUE; /* p is inside triangle */
- }