981 lines
26 KiB
C++
981 lines
26 KiB
C++
#include "raycast_mesh.h"
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <vector>
|
|
|
|
// This code snippet allows you to create an axis aligned bounding volume tree for a triangle mesh so that you can do
|
|
// high-speed raycasting.
|
|
//
|
|
// There are much better implementations of this available on the internet. In particular I recommend that you use
|
|
// OPCODE written by Pierre Terdiman.
|
|
// @see: http://www.codercorner.com/Opcode.htm
|
|
//
|
|
// OPCODE does a whole lot more than just raycasting, and is a rather significant amount of source code.
|
|
//
|
|
// I am providing this code snippet for the use case where you *only* want to do quick and dirty optimized raycasting.
|
|
// I have not done performance testing between this version and OPCODE; so I don't know how much slower it is. However,
|
|
// anytime you switch to using a spatial data structure for raycasting, you increase your performance by orders and orders
|
|
// of magnitude; so this implementation should work fine for simple tools and utilities.
|
|
//
|
|
// It also serves as a nice sample for people who are trying to learn the algorithm of how to implement AABB trees.
|
|
// AABB = Axis Aligned Bounding Volume trees.
|
|
//
|
|
// http://www.cgal.org/Manual/3.5/doc_html/cgal_manual/AABB_tree/Chapter_main.html
|
|
//
|
|
//
|
|
// This code snippet was written by John W. Ratcliff on August 18, 2011 and released under the MIT. license.
|
|
//
|
|
// mailto:jratcliffscarab@gmail.com
|
|
//
|
|
// The official source can be found at: http://code.google.com/p/raycastmesh/
|
|
//
|
|
//
|
|
|
|
#pragma warning(disable:4100)
|
|
|
|
namespace RAYCAST_MESH
|
|
{
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* A method to compute a ray-AABB intersection.
|
|
* Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990
|
|
* Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500)
|
|
* Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only)
|
|
*
|
|
* Hence this version is faster as well as more robust than the original one.
|
|
*
|
|
* Should work provided:
|
|
* 1) the integer representation of 0.0f is 0x00000000
|
|
* 2) the sign bit of the RmReal is the most significant one
|
|
*
|
|
* Report bugs: p.terdiman@codercorner.com
|
|
*
|
|
* \param aabb [in] the axis-aligned bounding box
|
|
* \param origin [in] ray origin
|
|
* \param dir [in] ray direction
|
|
* \param coord [out] impact coordinates
|
|
* \return true if ray intersects AABB
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#define RAYAABB_EPSILON 0.00001f
|
|
//! Integer representation of a RmRealing-point value.
|
|
#define IR(x) ((RmUint32&)x)
|
|
|
|
bool intersectRayAABB(const RmReal MinB[3],const RmReal MaxB[3],const RmReal origin[3],const RmReal dir[3],RmReal coord[3])
|
|
{
|
|
bool Inside = true;
|
|
RmReal MaxT[3];
|
|
MaxT[0]=MaxT[1]=MaxT[2]=-1.0f;
|
|
|
|
// Find candidate planes.
|
|
for(RmUint32 i=0;i<3;i++)
|
|
{
|
|
if(origin[i] < MinB[i])
|
|
{
|
|
coord[i] = MinB[i];
|
|
Inside = false;
|
|
|
|
// Calculate T distances to candidate planes
|
|
if(IR(dir[i])) MaxT[i] = (MinB[i] - origin[i]) / dir[i];
|
|
}
|
|
else if(origin[i] > MaxB[i])
|
|
{
|
|
coord[i] = MaxB[i];
|
|
Inside = false;
|
|
|
|
// Calculate T distances to candidate planes
|
|
if(IR(dir[i])) MaxT[i] = (MaxB[i] - origin[i]) / dir[i];
|
|
}
|
|
}
|
|
|
|
// Ray origin inside bounding box
|
|
if(Inside)
|
|
{
|
|
coord[0] = origin[0];
|
|
coord[1] = origin[1];
|
|
coord[2] = origin[2];
|
|
return true;
|
|
}
|
|
|
|
// Get largest of the maxT's for final choice of intersection
|
|
RmUint32 WhichPlane = 0;
|
|
if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1;
|
|
if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2;
|
|
|
|
// Check final candidate actually inside box
|
|
if(IR(MaxT[WhichPlane])&0x80000000) return false;
|
|
|
|
for(RmUint32 i=0;i<3;i++)
|
|
{
|
|
if(i!=WhichPlane)
|
|
{
|
|
coord[i] = origin[i] + MaxT[WhichPlane] * dir[i];
|
|
#ifdef RAYAABB_EPSILON
|
|
if(coord[i] < MinB[i] - RAYAABB_EPSILON || coord[i] > MaxB[i] + RAYAABB_EPSILON) return false;
|
|
#else
|
|
if(coord[i] < MinB[i] || coord[i] > MaxB[i]) return false;
|
|
#endif
|
|
}
|
|
}
|
|
return true; // ray hits box
|
|
}
|
|
|
|
|
|
|
|
|
|
bool intersectLineSegmentAABB(const RmReal bmin[3],const RmReal bmax[3],const RmReal p1[3],const RmReal dir[3],RmReal &dist,RmReal intersect[3])
|
|
{
|
|
bool ret = false;
|
|
|
|
if ( dist > RAYAABB_EPSILON )
|
|
{
|
|
ret = intersectRayAABB(bmin,bmax,p1,dir,intersect);
|
|
if ( ret )
|
|
{
|
|
RmReal dx = p1[0]-intersect[0];
|
|
RmReal dy = p1[1]-intersect[1];
|
|
RmReal dz = p1[2]-intersect[2];
|
|
RmReal d = dx*dx+dy*dy+dz*dz;
|
|
if ( d < dist*dist )
|
|
{
|
|
dist = sqrtf(d);
|
|
}
|
|
else
|
|
{
|
|
ret = false;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* a = b - c */
|
|
#define vector(a,b,c) \
|
|
(a)[0] = (b)[0] - (c)[0]; \
|
|
(a)[1] = (b)[1] - (c)[1]; \
|
|
(a)[2] = (b)[2] - (c)[2];
|
|
|
|
#define innerProduct(v,q) \
|
|
((v)[0] * (q)[0] + \
|
|
(v)[1] * (q)[1] + \
|
|
(v)[2] * (q)[2])
|
|
|
|
#define crossProduct(a,b,c) \
|
|
(a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \
|
|
(a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \
|
|
(a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1];
|
|
|
|
|
|
static inline bool rayIntersectsTriangle(const RmReal *p,const RmReal *d,const RmReal *v0,const RmReal *v1,const RmReal *v2,RmReal &t)
|
|
{
|
|
RmReal e1[3],e2[3],h[3],s[3],q[3];
|
|
RmReal a,f,u,v;
|
|
|
|
vector(e1,v1,v0);
|
|
vector(e2,v2,v0);
|
|
const RmReal edgeScale = 1.1f; // Adjust this value as needed
|
|
e1[0] *= edgeScale;
|
|
e1[1] *= edgeScale;
|
|
e1[2] *= edgeScale;
|
|
|
|
e2[0] *= edgeScale;
|
|
e2[1] *= edgeScale;
|
|
e2[2] *= edgeScale;
|
|
crossProduct(h,d,e2);
|
|
a = innerProduct(e1,h);
|
|
|
|
if (a > -0.00001 && a < 0.00001)
|
|
return(false);
|
|
|
|
f = 1/a;
|
|
vector(s,p,v0);
|
|
u = f * (innerProduct(s,h));
|
|
|
|
if (u < 0.0 || u > 1.0)
|
|
return(false);
|
|
|
|
crossProduct(q,s,e1);
|
|
v = f * innerProduct(d,q);
|
|
if (v < 0.0 || u + v > 1.0)
|
|
return(false);
|
|
// at this stage we can compute t to find out where
|
|
// the intersection point is on the line
|
|
t = f * innerProduct(e2,q);
|
|
if (t > 0) // ray intersection
|
|
return(true);
|
|
else // this means that there is a line intersection
|
|
// but not a ray intersection
|
|
return (false);
|
|
}
|
|
|
|
static RmReal computePlane(const RmReal *A,const RmReal *B,const RmReal *C,RmReal *n) // returns D
|
|
{
|
|
RmReal vx = (B[0] - C[0]);
|
|
RmReal vy = (B[1] - C[1]);
|
|
RmReal vz = (B[2] - C[2]);
|
|
|
|
RmReal wx = (A[0] - B[0]);
|
|
RmReal wy = (A[1] - B[1]);
|
|
RmReal wz = (A[2] - B[2]);
|
|
|
|
RmReal vw_x = vy * wz - vz * wy;
|
|
RmReal vw_y = vz * wx - vx * wz;
|
|
RmReal vw_z = vx * wy - vy * wx;
|
|
|
|
RmReal mag = sqrt((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z));
|
|
|
|
if ( mag < 0.000001f )
|
|
{
|
|
mag = 0;
|
|
}
|
|
else
|
|
{
|
|
mag = 1.0f/mag;
|
|
}
|
|
|
|
RmReal x = vw_x * mag;
|
|
RmReal y = vw_y * mag;
|
|
RmReal z = vw_z * mag;
|
|
|
|
|
|
RmReal D = 0.0f - ((x*A[0])+(y*A[1])+(z*A[2]));
|
|
|
|
n[0] = x;
|
|
n[1] = y;
|
|
n[2] = z;
|
|
|
|
return D;
|
|
}
|
|
|
|
|
|
#define TRI_EOF 0xFFFFFFFF
|
|
|
|
enum AxisAABB
|
|
{
|
|
AABB_XAXIS,
|
|
AABB_YAXIS,
|
|
AABB_ZAXIS
|
|
};
|
|
|
|
enum ClipCode
|
|
{
|
|
CC_MINX = (1<<0),
|
|
CC_MAXX = (1<<1),
|
|
CC_MINY = (1<<2),
|
|
CC_MAXY = (1<<3),
|
|
CC_MINZ = (1<<4),
|
|
CC_MAXZ = (1<<5),
|
|
};
|
|
|
|
|
|
class BoundsAABB
|
|
{
|
|
public:
|
|
|
|
|
|
void setMin(const RmReal *v)
|
|
{
|
|
mMin[0] = v[0];
|
|
mMin[1] = v[1];
|
|
mMin[2] = v[2];
|
|
}
|
|
|
|
void setMax(const RmReal *v)
|
|
{
|
|
mMax[0] = v[0];
|
|
mMax[1] = v[1];
|
|
mMax[2] = v[2];
|
|
}
|
|
|
|
void setMin(RmReal x,RmReal y,RmReal z)
|
|
{
|
|
mMin[0] = x;
|
|
mMin[1] = y;
|
|
mMin[2] = z;
|
|
}
|
|
|
|
void setMax(RmReal x,RmReal y,RmReal z)
|
|
{
|
|
mMax[0] = x;
|
|
mMax[1] = y;
|
|
mMax[2] = z;
|
|
}
|
|
|
|
void include(const RmReal *v)
|
|
{
|
|
if ( v[0] < mMin[0] ) mMin[0] = v[0];
|
|
if ( v[1] < mMin[1] ) mMin[1] = v[1];
|
|
if ( v[2] < mMin[2] ) mMin[2] = v[2];
|
|
|
|
if ( v[0] > mMax[0] ) mMax[0] = v[0];
|
|
if ( v[1] > mMax[1] ) mMax[1] = v[1];
|
|
if ( v[2] > mMax[2] ) mMax[2] = v[2];
|
|
}
|
|
|
|
void getCenter(RmReal *center) const
|
|
{
|
|
center[0] = (mMin[0]+mMax[0])*0.5f;
|
|
center[1] = (mMin[1]+mMax[1])*0.5f;
|
|
center[2] = (mMin[2]+mMax[2])*0.5f;
|
|
}
|
|
|
|
bool intersects(const BoundsAABB &b) const
|
|
{
|
|
if ((mMin[0] > b.mMax[0]) || (b.mMin[0] > mMax[0])) return false;
|
|
if ((mMin[1] > b.mMax[1]) || (b.mMin[1] > mMax[1])) return false;
|
|
if ((mMin[2] > b.mMax[2]) || (b.mMin[2] > mMax[2])) return false;
|
|
return true;
|
|
}
|
|
|
|
bool containsTriangle(const RmReal *p1,const RmReal *p2,const RmReal *p3) const
|
|
{
|
|
BoundsAABB b;
|
|
b.setMin(p1);
|
|
b.setMax(p1);
|
|
b.include(p2);
|
|
b.include(p3);
|
|
return intersects(b);
|
|
}
|
|
|
|
bool containsTriangleExact(const RmReal *p1,const RmReal *p2,const RmReal *p3,RmUint32 &orCode) const
|
|
{
|
|
bool ret = false;
|
|
|
|
RmUint32 andCode;
|
|
orCode = getClipCode(p1,p2,p3,andCode);
|
|
if ( andCode == 0 )
|
|
{
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
inline RmUint32 getClipCode(const RmReal *p1,const RmReal *p2,const RmReal *p3,RmUint32 &andCode) const
|
|
{
|
|
andCode = 0xFFFFFFFF;
|
|
RmUint32 c1 = getClipCode(p1);
|
|
RmUint32 c2 = getClipCode(p2);
|
|
RmUint32 c3 = getClipCode(p3);
|
|
andCode&=c1;
|
|
andCode&=c2;
|
|
andCode&=c3;
|
|
return c1|c2|c3;
|
|
}
|
|
|
|
inline RmUint32 getClipCode(const RmReal *p) const
|
|
{
|
|
RmUint32 ret = 0;
|
|
|
|
if ( p[0] < mMin[0] )
|
|
{
|
|
ret|=CC_MINX;
|
|
}
|
|
else if ( p[0] > mMax[0] )
|
|
{
|
|
ret|=CC_MAXX;
|
|
}
|
|
|
|
if ( p[1] < mMin[1] )
|
|
{
|
|
ret|=CC_MINY;
|
|
}
|
|
else if ( p[1] > mMax[1] )
|
|
{
|
|
ret|=CC_MAXY;
|
|
}
|
|
|
|
if ( p[2] < mMin[2] )
|
|
{
|
|
ret|=CC_MINZ;
|
|
}
|
|
else if ( p[2] > mMax[2] )
|
|
{
|
|
ret|=CC_MAXZ;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
inline void clamp(const BoundsAABB &aabb)
|
|
{
|
|
if ( mMin[0] < aabb.mMin[0] ) mMin[0] = aabb.mMin[0];
|
|
if ( mMin[1] < aabb.mMin[1] ) mMin[1] = aabb.mMin[1];
|
|
if ( mMin[2] < aabb.mMin[2] ) mMin[2] = aabb.mMin[2];
|
|
if ( mMax[0] > aabb.mMax[0] ) mMax[0] = aabb.mMax[0];
|
|
if ( mMax[1] > aabb.mMax[1] ) mMax[1] = aabb.mMax[1];
|
|
if ( mMax[2] > aabb.mMax[2] ) mMax[2] = aabb.mMax[2];
|
|
}
|
|
|
|
RmReal mMin[3];
|
|
RmReal mMax[3];
|
|
};
|
|
|
|
|
|
class NodeAABB;
|
|
|
|
class NodeInterface
|
|
{
|
|
public:
|
|
virtual NodeAABB * getNode(void) = 0;
|
|
virtual void getFaceNormal(RmUint32 tri,RmReal *faceNormal) = 0;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class NodeAABB
|
|
{
|
|
public:
|
|
NodeAABB(void)
|
|
{
|
|
mLeft = NULL;
|
|
mRight = NULL;
|
|
mLeafTriangleIndex= TRI_EOF;
|
|
}
|
|
|
|
NodeAABB(RmUint32 vcount,const RmReal *vertices,RmUint32 tcount,RmUint32 *indices,
|
|
RmUint32 maxDepth, // Maximum recursion depth for the triangle mesh.
|
|
RmUint32 minLeafSize, // minimum triangles to treat as a 'leaf' node.
|
|
RmReal minAxisSize,
|
|
NodeInterface *callback,
|
|
TriVector &leafTriangles) // once a particular axis is less than this size, stop sub-dividing.
|
|
|
|
{
|
|
mLeft = NULL;
|
|
mRight = NULL;
|
|
mLeafTriangleIndex = TRI_EOF;
|
|
TriVector triangles;
|
|
triangles.reserve(tcount);
|
|
for (RmUint32 i=0; i<tcount; i++)
|
|
{
|
|
triangles.push_back(i);
|
|
}
|
|
mBounds.setMin( vertices );
|
|
mBounds.setMax( vertices );
|
|
const RmReal *vtx = vertices+3;
|
|
for (RmUint32 i=1; i<vcount; i++)
|
|
{
|
|
mBounds.include( vtx );
|
|
vtx+=3;
|
|
}
|
|
split(triangles,vcount,vertices,tcount,indices,0,maxDepth,minLeafSize,minAxisSize,callback,leafTriangles);
|
|
}
|
|
|
|
NodeAABB(const BoundsAABB &aabb)
|
|
{
|
|
mBounds = aabb;
|
|
mLeft = NULL;
|
|
mRight = NULL;
|
|
mLeafTriangleIndex = TRI_EOF;
|
|
}
|
|
|
|
~NodeAABB(void)
|
|
{
|
|
}
|
|
|
|
// here is where we split the mesh..
|
|
void split(const TriVector &triangles,
|
|
RmUint32 vcount,
|
|
const RmReal *vertices,
|
|
RmUint32 tcount,
|
|
const RmUint32 *indices,
|
|
RmUint32 depth,
|
|
RmUint32 maxDepth, // Maximum recursion depth for the triangle mesh.
|
|
RmUint32 minLeafSize, // minimum triangles to treat as a 'leaf' node.
|
|
RmReal minAxisSize,
|
|
NodeInterface *callback,
|
|
TriVector &leafTriangles) // once a particular axis is less than this size, stop sub-dividing.
|
|
|
|
{
|
|
// Find the longest axis of the bounding volume of this node
|
|
RmReal dx = mBounds.mMax[0] - mBounds.mMin[0];
|
|
RmReal dy = mBounds.mMax[1] - mBounds.mMin[1];
|
|
RmReal dz = mBounds.mMax[2] - mBounds.mMin[2];
|
|
|
|
AxisAABB axis = AABB_XAXIS;
|
|
RmReal laxis = dx;
|
|
|
|
if ( dy > dx )
|
|
{
|
|
axis = AABB_YAXIS;
|
|
laxis = dy;
|
|
}
|
|
|
|
if ( dz > dx && dz > dy )
|
|
{
|
|
axis = AABB_ZAXIS;
|
|
laxis = dz;
|
|
}
|
|
|
|
RmUint32 count = triangles.size();
|
|
|
|
// if the number of triangles is less than the minimum allowed for a leaf node or...
|
|
// we have reached the maximum recursion depth or..
|
|
// the width of the longest axis is less than the minimum axis size then...
|
|
// we create the leaf node and copy the triangles into the leaf node triangle array.
|
|
if ( count < minLeafSize || depth >= maxDepth || laxis < minAxisSize )
|
|
{
|
|
// Copy the triangle indices into the leaf triangles array
|
|
mLeafTriangleIndex = leafTriangles.size(); // assign the array start location for these leaf triangles.
|
|
leafTriangles.push_back(count);
|
|
for (auto i = triangles.begin(); i != triangles.end(); ++i) {
|
|
RmUint32 tri = *i;
|
|
leafTriangles.push_back(tri);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RmReal center[3];
|
|
mBounds.getCenter(center);
|
|
BoundsAABB b1,b2;
|
|
splitRect(axis,mBounds,b1,b2,center);
|
|
|
|
// Compute two bounding boxes based upon the split of the longest axis
|
|
|
|
BoundsAABB leftBounds,rightBounds;
|
|
|
|
TriVector leftTriangles;
|
|
TriVector rightTriangles;
|
|
|
|
|
|
// Create two arrays; one of all triangles which intersect the 'left' half of the bounding volume node
|
|
// and another array that includes all triangles which intersect the 'right' half of the bounding volume node.
|
|
for (auto i = triangles.begin(); i != triangles.end(); ++i) {
|
|
|
|
RmUint32 tri = (*i);
|
|
|
|
{
|
|
RmUint32 i1 = indices[tri*3+0];
|
|
RmUint32 i2 = indices[tri*3+1];
|
|
RmUint32 i3 = indices[tri*3+2];
|
|
|
|
const RmReal *p1 = &vertices[i1*3];
|
|
const RmReal *p2 = &vertices[i2*3];
|
|
const RmReal *p3 = &vertices[i3*3];
|
|
|
|
RmUint32 addCount = 0;
|
|
RmUint32 orCode=0xFFFFFFFF;
|
|
if ( b1.containsTriangleExact(p1,p2,p3,orCode))
|
|
{
|
|
addCount++;
|
|
if ( leftTriangles.empty() )
|
|
{
|
|
leftBounds.setMin(p1);
|
|
leftBounds.setMax(p1);
|
|
}
|
|
leftBounds.include(p1);
|
|
leftBounds.include(p2);
|
|
leftBounds.include(p3);
|
|
leftTriangles.push_back(tri); // Add this triangle to the 'left triangles' array and revise the left triangles bounding volume
|
|
}
|
|
// if the orCode is zero; meaning the triangle was fully self-contiained int he left bounding box; then we don't need to test against the right
|
|
if ( orCode && b2.containsTriangleExact(p1,p2,p3,orCode))
|
|
{
|
|
addCount++;
|
|
if ( rightTriangles.empty() )
|
|
{
|
|
rightBounds.setMin(p1);
|
|
rightBounds.setMax(p1);
|
|
}
|
|
rightBounds.include(p1);
|
|
rightBounds.include(p2);
|
|
rightBounds.include(p3);
|
|
rightTriangles.push_back(tri); // Add this triangle to the 'right triangles' array and revise the right triangles bounding volume.
|
|
}
|
|
assert( addCount );
|
|
}
|
|
}
|
|
|
|
if ( !leftTriangles.empty() ) // If there are triangles in the left half then...
|
|
{
|
|
leftBounds.clamp(b1); // we have to clamp the bounding volume so it stays inside the parent volume.
|
|
mLeft = callback->getNode(); // get a new AABB node
|
|
new ( mLeft ) NodeAABB(leftBounds); // initialize it to default constructor values.
|
|
// Then recursively split this node.
|
|
mLeft->split(leftTriangles,vcount,vertices,tcount,indices,depth+1,maxDepth,minLeafSize,minAxisSize,callback,leafTriangles);
|
|
}
|
|
|
|
if ( !rightTriangles.empty() ) // If there are triangles in the right half then..
|
|
{
|
|
rightBounds.clamp(b2); // clamps the bounding volume so it stays restricted to the size of the parent volume.
|
|
mRight = callback->getNode(); // allocate and default initialize a new node
|
|
new ( mRight ) NodeAABB(rightBounds);
|
|
// Recursively split this node.
|
|
mRight->split(rightTriangles,vcount,vertices,tcount,indices,depth+1,maxDepth,minLeafSize,minAxisSize,callback,leafTriangles);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void splitRect(AxisAABB axis,const BoundsAABB &source,BoundsAABB &b1,BoundsAABB &b2,const RmReal *midpoint)
|
|
{
|
|
switch ( axis )
|
|
{
|
|
case AABB_XAXIS:
|
|
{
|
|
b1.setMin( source.mMin );
|
|
b1.setMax( midpoint[0], source.mMax[1], source.mMax[2] );
|
|
|
|
b2.setMin( midpoint[0], source.mMin[1], source.mMin[2] );
|
|
b2.setMax(source.mMax);
|
|
}
|
|
break;
|
|
case AABB_YAXIS:
|
|
{
|
|
b1.setMin(source.mMin);
|
|
b1.setMax(source.mMax[0], midpoint[1], source.mMax[2]);
|
|
|
|
b2.setMin(source.mMin[0], midpoint[1], source.mMin[2]);
|
|
b2.setMax(source.mMax);
|
|
}
|
|
break;
|
|
case AABB_ZAXIS:
|
|
{
|
|
b1.setMin(source.mMin);
|
|
b1.setMax(source.mMax[0], source.mMax[1], midpoint[2]);
|
|
|
|
b2.setMin(source.mMin[0], source.mMin[1], midpoint[2]);
|
|
b2.setMax(source.mMax);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
virtual void raycast(bool &hit,
|
|
const RmReal *from,
|
|
const RmReal *to,
|
|
const RmReal *dir,
|
|
RmReal *hitLocation,
|
|
RmReal *hitNormal,
|
|
RmReal *hitDistance,
|
|
RmUint32 *GridID,
|
|
RmUint32 *WidgetID,
|
|
const RmReal *vertices,
|
|
const RmUint32 *indices,
|
|
const RmUint32 *grids,
|
|
const RmUint32 *widgets,
|
|
RmReal &nearestDistance,
|
|
NodeInterface *callback,
|
|
RmUint32 *raycastTriangles,
|
|
RmUint32 raycastFrame,
|
|
const TriVector &leafTriangles,
|
|
RmUint32 &nearestTriIndex,
|
|
RmMap* ignored_widgets)
|
|
{
|
|
RmReal sect[3];
|
|
RmReal nd = nearestDistance;
|
|
if ( !intersectLineSegmentAABB(mBounds.mMin,mBounds.mMax,from,dir,nd,sect) )
|
|
{
|
|
return;
|
|
}
|
|
if ( mLeafTriangleIndex != TRI_EOF )
|
|
{
|
|
const RmUint32 *scan = &leafTriangles[mLeafTriangleIndex];
|
|
RmUint32 count = *scan++;
|
|
for (RmUint32 i=0; i<count; i++)
|
|
{
|
|
RmUint32 tri = *scan++;
|
|
if ( raycastTriangles[tri] != raycastFrame )
|
|
{
|
|
raycastTriangles[tri] = raycastFrame;
|
|
RmUint32 i1 = indices[tri*3+0];
|
|
RmUint32 i2 = indices[tri*3+1];
|
|
RmUint32 i3 = indices[tri*3+2];
|
|
|
|
const RmReal *p1 = &vertices[i1*3];
|
|
const RmReal *p2 = &vertices[i2*3];
|
|
const RmReal *p3 = &vertices[i3*3];
|
|
|
|
RmReal t;
|
|
if ( rayIntersectsTriangle(from,dir,p1,p2,p3,t))
|
|
{
|
|
bool accept = false;
|
|
if ( t == nearestDistance && tri < nearestTriIndex )
|
|
{
|
|
accept = true;
|
|
}
|
|
if ( t < nearestDistance || accept )
|
|
{
|
|
if(ignored_widgets && ignored_widgets->find(widgets[tri]) != ignored_widgets->end()) {
|
|
continue;
|
|
}
|
|
|
|
nearestDistance = t;
|
|
if ( hitLocation )
|
|
{
|
|
hitLocation[0] = from[0]+dir[0]*t;
|
|
hitLocation[1] = from[1]+dir[1]*t;
|
|
hitLocation[2] = from[2]+dir[2]*t;
|
|
}
|
|
if ( hitNormal )
|
|
{
|
|
callback->getFaceNormal(tri,hitNormal);
|
|
}
|
|
if ( hitDistance )
|
|
{
|
|
*hitDistance = t;
|
|
}
|
|
if(GridID) {
|
|
*GridID = grids[tri];
|
|
}
|
|
if(WidgetID) {
|
|
*WidgetID = widgets[tri];
|
|
}
|
|
|
|
nearestTriIndex = tri;
|
|
hit = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( mLeft )
|
|
{
|
|
mLeft->raycast(hit,from,to,dir,hitLocation,hitNormal,hitDistance,GridID,WidgetID,vertices,indices,grids,widgets,nearestDistance,callback,raycastTriangles,raycastFrame,leafTriangles,nearestTriIndex,ignored_widgets);
|
|
}
|
|
if ( mRight )
|
|
{
|
|
mRight->raycast(hit,from,to,dir,hitLocation,hitNormal,hitDistance,GridID,WidgetID,vertices,indices,grids,widgets,nearestDistance,callback,raycastTriangles,raycastFrame,leafTriangles,nearestTriIndex,ignored_widgets);
|
|
}
|
|
}
|
|
}
|
|
|
|
NodeAABB *mLeft; // left node
|
|
NodeAABB *mRight; // right node
|
|
BoundsAABB mBounds; // bounding volume of node
|
|
RmUint32 mLeafTriangleIndex; // if it is a leaf node; then these are the triangle indices.
|
|
};
|
|
|
|
class MyRaycastMesh : public RaycastMesh, public NodeInterface
|
|
{
|
|
public:
|
|
|
|
MyRaycastMesh(RmUint32 vcount,const RmReal *vertices,RmUint32 tcount,const RmUint32 *indices,const RmUint32 *grids,const RmUint32 *widgets,RmUint32 maxDepth,RmUint32 minLeafSize,RmReal minAxisSize)
|
|
{
|
|
mRaycastFrame = 0;
|
|
if ( maxDepth < 2 )
|
|
{
|
|
maxDepth = 2;
|
|
}
|
|
if ( maxDepth > 15 )
|
|
{
|
|
maxDepth = 15;
|
|
}
|
|
RmUint32 pow2Table[16] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 65536 };
|
|
mMaxNodeCount = 0;
|
|
for (RmUint32 i=0; i<=maxDepth; i++)
|
|
{
|
|
mMaxNodeCount+=pow2Table[i];
|
|
}
|
|
mNodes = new NodeAABB[mMaxNodeCount];
|
|
mNodeCount = 0;
|
|
mVcount = vcount;
|
|
mVertices = (RmReal *)::malloc(sizeof(RmReal)*3*vcount);
|
|
memcpy(mVertices,vertices,sizeof(RmReal)*3*vcount);
|
|
mTcount = tcount;
|
|
mIndices = (RmUint32 *)::malloc(sizeof(RmUint32)*tcount*3);
|
|
memcpy(mIndices,indices,sizeof(RmUint32)*tcount*3);
|
|
mRaycastTriangles = (RmUint32 *)::malloc(tcount*sizeof(RmUint32));
|
|
memset(mRaycastTriangles,0,tcount*sizeof(RmUint32));
|
|
mGrids = (RmUint32 *)::malloc(sizeof(RmUint32)*tcount);
|
|
memcpy(mGrids,grids,sizeof(RmUint32)*tcount);
|
|
mWidgets = (RmUint32 *)::malloc(sizeof(RmUint32)*tcount);
|
|
memcpy(mWidgets,widgets,sizeof(RmUint32)*tcount);
|
|
mRoot = getNode();
|
|
mFaceNormals = NULL;
|
|
new ( mRoot ) NodeAABB(mVcount,mVertices,mTcount,mIndices,maxDepth,minLeafSize,minAxisSize,this,mLeafTriangles);
|
|
}
|
|
|
|
~MyRaycastMesh(void)
|
|
{
|
|
delete []mNodes;
|
|
::free(mVertices);
|
|
::free(mIndices);
|
|
::free(mFaceNormals);
|
|
::free(mRaycastTriangles);
|
|
::free(mGrids);
|
|
::free(mWidgets);
|
|
}
|
|
|
|
virtual bool raycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance,RmUint32 *GridID,RmUint32 *WidgetID, RmMap* ignored_widgets)
|
|
{
|
|
bool ret = false;
|
|
|
|
RmReal dir[3];
|
|
dir[0] = to[0] - from[0];
|
|
dir[1] = to[1] - from[1];
|
|
dir[2] = to[2] - from[2];
|
|
RmReal distance = sqrtf( dir[0]*dir[0] + dir[1]*dir[1]+dir[2]*dir[2] );
|
|
if ( distance < 0.0000000001f ) return false;
|
|
RmReal recipDistance = 1.0f / distance;
|
|
dir[0]*=recipDistance;
|
|
dir[1]*=recipDistance;
|
|
dir[2]*=recipDistance;
|
|
mRaycastFrame++;
|
|
RmUint32 nearestTriIndex=TRI_EOF;
|
|
mRoot->raycast(ret,from,to,dir,hitLocation,hitNormal,hitDistance,GridID,WidgetID,mVertices,mIndices,mGrids,mWidgets,distance,this,mRaycastTriangles,mRaycastFrame,mLeafTriangles,nearestTriIndex,ignored_widgets);
|
|
return ret;
|
|
}
|
|
|
|
virtual void release(void)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
virtual const RmReal * getBoundMin(void) const // return the minimum bounding box
|
|
{
|
|
return mRoot->mBounds.mMin;
|
|
}
|
|
virtual const RmReal * getBoundMax(void) const // return the maximum bounding box.
|
|
{
|
|
return mRoot->mBounds.mMax;
|
|
}
|
|
|
|
virtual NodeAABB * getNode(void)
|
|
{
|
|
assert( mNodeCount < mMaxNodeCount );
|
|
NodeAABB *ret = &mNodes[mNodeCount];
|
|
mNodeCount++;
|
|
return ret;
|
|
}
|
|
|
|
virtual void getFaceNormal(RmUint32 tri,RmReal *faceNormal)
|
|
{
|
|
if ( mFaceNormals == NULL )
|
|
{
|
|
mFaceNormals = (RmReal *)::malloc(sizeof(RmReal)*3*mTcount);
|
|
for (RmUint32 i=0; i<mTcount; i++)
|
|
{
|
|
RmUint32 i1 = mIndices[i*3+0];
|
|
RmUint32 i2 = mIndices[i*3+1];
|
|
RmUint32 i3 = mIndices[i*3+2];
|
|
const RmReal*p1 = &mVertices[i1*3];
|
|
const RmReal*p2 = &mVertices[i2*3];
|
|
const RmReal*p3 = &mVertices[i3*3];
|
|
RmReal *dest = &mFaceNormals[i*3];
|
|
computePlane(p3,p2,p1,dest);
|
|
}
|
|
}
|
|
const RmReal *src = &mFaceNormals[tri*3];
|
|
faceNormal[0] = src[0];
|
|
faceNormal[1] = src[1];
|
|
faceNormal[2] = src[2];
|
|
}
|
|
|
|
virtual bool bruteForceRaycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance,RmUint32 *GridID,RmUint32 *WidgetID, RmMap* ignored_widgets)
|
|
{
|
|
bool ret = false;
|
|
|
|
RmReal dir[3];
|
|
|
|
dir[0] = to[0] - from[0];
|
|
dir[1] = to[1] - from[1];
|
|
dir[2] = to[2] - from[2];
|
|
|
|
RmReal distance = sqrtf( dir[0]*dir[0] + dir[1]*dir[1]+dir[2]*dir[2] );
|
|
if ( distance < 0.0000000001f ) return false;
|
|
RmReal recipDistance = 1.0f / distance;
|
|
dir[0]*=recipDistance;
|
|
dir[1]*=recipDistance;
|
|
dir[2]*=recipDistance;
|
|
const RmUint32 *indices = mIndices;
|
|
const RmReal *vertices = mVertices;
|
|
RmReal nearestDistance = distance;
|
|
|
|
for (RmUint32 tri=0; tri<mTcount; tri++)
|
|
{
|
|
RmUint32 i1 = indices[tri*3+0];
|
|
RmUint32 i2 = indices[tri*3+1];
|
|
RmUint32 i3 = indices[tri*3+2];
|
|
|
|
const RmReal *p1 = &vertices[i1*3];
|
|
const RmReal *p2 = &vertices[i2*3];
|
|
const RmReal *p3 = &vertices[i3*3];
|
|
|
|
RmReal t;
|
|
if ( rayIntersectsTriangle(from,dir,p1,p2,p3,t))
|
|
{
|
|
if ( t < nearestDistance )
|
|
{
|
|
if(ignored_widgets && ignored_widgets->find(mWidgets[tri]) != ignored_widgets->end()) {
|
|
continue;
|
|
}
|
|
|
|
nearestDistance = t;
|
|
if ( hitLocation )
|
|
{
|
|
hitLocation[0] = from[0]+dir[0]*t;
|
|
hitLocation[1] = from[1]+dir[1]*t;
|
|
hitLocation[2] = from[2]+dir[2]*t;
|
|
}
|
|
|
|
if ( hitNormal )
|
|
{
|
|
getFaceNormal(tri,hitNormal);
|
|
}
|
|
|
|
if ( hitDistance )
|
|
{
|
|
*hitDistance = t;
|
|
}
|
|
|
|
if(GridID) {
|
|
*GridID = mGrids[tri];
|
|
}
|
|
if(WidgetID) {
|
|
*WidgetID = mWidgets[tri];
|
|
}
|
|
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
RmUint32 mRaycastFrame;
|
|
RmUint32 *mRaycastTriangles;
|
|
RmUint32 mVcount;
|
|
RmReal *mVertices;
|
|
RmReal *mFaceNormals;
|
|
RmUint32 mTcount;
|
|
RmUint32 *mIndices;
|
|
NodeAABB *mRoot;
|
|
RmUint32 mNodeCount;
|
|
RmUint32 mMaxNodeCount;
|
|
NodeAABB *mNodes;
|
|
TriVector mLeafTriangles;
|
|
RmUint32 *mGrids;
|
|
RmUint32 *mWidgets;
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
using namespace RAYCAST_MESH;
|
|
|
|
|
|
RaycastMesh * createRaycastMesh(RmUint32 vcount, // The number of vertices in the source triangle mesh
|
|
const RmReal *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc.
|
|
RmUint32 tcount, // The number of triangles in the source triangle mesh
|
|
const RmUint32 *indices, // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ...
|
|
const RmUint32 *grids,
|
|
const RmUint32 *widgets,
|
|
RmUint32 maxDepth, // Maximum recursion depth for the triangle mesh.
|
|
RmUint32 minLeafSize, // minimum triangles to treat as a 'leaf' node.
|
|
RmReal minAxisSize ) // once a particular axis is less than this size, stop sub-dividing.
|
|
{
|
|
auto m = new MyRaycastMesh(vcount, vertices, tcount, indices, grids, widgets, maxDepth, minLeafSize, minAxisSize);
|
|
return static_cast< RaycastMesh * >(m);
|
|
} |