Initial commit

This commit is contained in:
Eagle517
2025-02-17 23:17:30 -06:00
commit 7cad314c94
4726 changed files with 1145203 additions and 0 deletions

5
engine/util/fourcc.h Executable file
View File

@ -0,0 +1,5 @@
#ifndef MAKEFOURCC
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
((U32)(U8)(ch0) | ((U32)(U8)(ch1) << 8) | \
((U32)(U8)(ch2) << 16) | ((U32)(U8)(ch3) << 24 ))
#endif //defined(MAKEFOURCC)

166
engine/util/frustrumCuller.cpp Executable file
View File

@ -0,0 +1,166 @@
//-----------------------------------------------------------------------------
// Torque Shader Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "util/frustrumCuller.h"
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sgUtil.h"
#include "terrain/sky.h"
#include "dgl/dgl.h"
SceneState *FrustrumCuller::smSceneState;
Point3F FrustrumCuller::smCamPos;
U32 FrustrumCuller::smNumClipPlanes;
F32 FrustrumCuller::smFarDistance;
PlaneF FrustrumCuller::smClipPlane[MaxClipPlanes];
void FrustrumCuller::init(SceneState *state)
{
if(Con::getBoolVariable("$lockFrustrum", false))
return;
// Set up some general info.
smSceneState = state;
smFarDistance = gClientSceneGraph->getCurrentSky()->getVisibleDistance();
// Now determine the frustrum.
F64 realfrustumParam[6];
dglGetFrustum(&realfrustumParam[0], &realfrustumParam[1],
&realfrustumParam[2], &realfrustumParam[3],
&realfrustumParam[4], &realfrustumParam[5]);
MatrixF camToObj;
dglGetModelview(&camToObj); // BUG? Test this.
camToObj.inverse();
camToObj.getColumn(3, &smCamPos);
Point3F osCamPoint(0,0,0);
camToObj.mulP(osCamPoint);
sgComputeOSFrustumPlanes(
realfrustumParam,
camToObj,
osCamPoint,
smClipPlane[4],
smClipPlane[0],
smClipPlane[1],
smClipPlane[2],
smClipPlane[3]);
smNumClipPlanes = 4;
if (state->mFlipCull)
{
smClipPlane[0].neg();
smClipPlane[1].neg();
smClipPlane[2].neg();
smClipPlane[3].neg();
smClipPlane[4].neg();
}
AssertFatal(smNumClipPlanes <= MaxClipPlanes, "FrustrumCuller::init - got too many clip planes!");
}
F32 FrustrumCuller::getBoxDistance(const Box3F &box)
{
Point3F vec;
const Point3F &minPoint = box.min;
const Point3F &maxPoint = box.max;
if(smCamPos.z < minPoint.z)
vec.z = minPoint.z - smCamPos.z;
else if(smCamPos.z > maxPoint.z)
vec.z = maxPoint.z - smCamPos.z;
else
vec.z = 0;
if(smCamPos.x < minPoint.x)
vec.x = minPoint.x - smCamPos.x;
else if(smCamPos.x > maxPoint.x)
vec.x = smCamPos.x - maxPoint.x;
else
vec.x = 0;
if(smCamPos.y < minPoint.y)
vec.y = minPoint.y - smCamPos.y;
else if(smCamPos.y > maxPoint.y)
vec.y = smCamPos.y - maxPoint.y;
else
vec.y = 0;
return vec.len();
}
S32 FrustrumCuller::testBoxVisibility(const Box3F &bounds, const S32 mask, const F32 expand)
{
S32 retMask = 0;
Point3F minPoint, maxPoint;
for(S32 i = 0; i < smNumClipPlanes; i++)
{
if(mask & (1 << i))
{
if(smClipPlane[i].x > 0)
{
maxPoint.x = bounds.max.x;
minPoint.x = bounds.min.x;
}
else
{
maxPoint.x = bounds.min.x;
minPoint.x = bounds.max.x;
}
if(smClipPlane[i].y > 0)
{
maxPoint.y = bounds.max.y;
minPoint.y = bounds.min.y;
}
else
{
maxPoint.y = bounds.min.y;
minPoint.y = bounds.max.y;
}
if(smClipPlane[i].z > 0)
{
maxPoint.z = bounds.max.z;
minPoint.z = bounds.min.z;
}
else
{
maxPoint.z = bounds.min.z;
minPoint.z = bounds.max.z;
}
F32 maxDot = mDot(maxPoint, smClipPlane[i]);
F32 minDot = mDot(minPoint, smClipPlane[i]);
F32 planeD = smClipPlane[i].d;
if(maxDot <= -(planeD + expand))
return -1;
if(minDot <= -planeD)
retMask |= (1 << i);
}
}
// Check the far distance as well.
if(mask & FarSphereMask)
{
F32 squareDistance = getBoxDistance(bounds);
// Reject stuff outside our visible range...
if(squareDistance >= smFarDistance)
return -1;
// See if the box potentially hits the far sphere. (Sort of assumes a square box!)
if(squareDistance + bounds.len_x() + bounds.len_z() > smFarDistance)
retMask |= FarSphereMask;
}
return retMask;
}

39
engine/util/frustrumCuller.h Executable file
View File

@ -0,0 +1,39 @@
//-----------------------------------------------------------------------------
// Torque Shader Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _FRUSTRUMCULLER_H_
#define _FRUSTRUMCULLER_H_
#include "math/mBox.h"
#include "sceneGraph/sceneState.h"
namespace FrustrumCuller
{
enum
{
/// We need to store at least 4 clip planes (sides of view frustrum).
MaxClipPlanes = 5,
// Clipping related flags...
ClipPlaneMask = BIT(MaxClipPlanes) - 1,
FarSphereMask = BIT(MaxClipPlanes + 1),
FogPlaneBoxMask = BIT(MaxClipPlanes + 2),
AllClipPlanes = ClipPlaneMask | FarSphereMask,
};
extern SceneState *smSceneState;
extern Point3F smCamPos;
extern F32 smFarDistance;
extern U32 smNumClipPlanes;
extern PlaneF smClipPlane[MaxClipPlanes];
void init(SceneState *state);
S32 testBoxVisibility(const Box3F &bounds, const S32 mask, const F32 expand);
F32 getBoxDistance(const Box3F &box);
};
#endif

229
engine/util/quadTreeTracer.cpp Executable file
View File

@ -0,0 +1,229 @@
//-----------------------------------------------------------------------------
// Torque Shader Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "util/quadTreeTracer.h"
#include "platform/profiler.h"
#include "core/frameAllocator.h"
static F32 calcInterceptV(F32 vStart, F32 invDeltaV, F32 intercept)
{
return (intercept - vStart) * invDeltaV;
}
static F32 calcInterceptNone(F32, F32, F32)
{
return F32_MAX;
}
static F32 (*calcInterceptX)(F32, F32, F32);
static F32 (*calcInterceptY)(F32, F32, F32);
bool QuadTreeTracer::castRay(const Point3F &start, const Point3F &end, RayInfo *info)
{
PROFILE_START(QuadTreeTracer_castRay);
// Do some precalculations we'll use for the rest of this routine.
// Set up our intercept calculation methods.
F32 invDeltaX;
if(end.x == start.x)
{
calcInterceptX = calcInterceptNone;
invDeltaX = 0;
}
else
{
invDeltaX = 1.f / (end.x - start.x);
calcInterceptX = calcInterceptV;
}
F32 invDeltaY;
if(end.y == start.y)
{
calcInterceptY = calcInterceptNone;
invDeltaY = 0;
}
else
{
invDeltaY = 1.f / (end.y - start.y);
calcInterceptY = calcInterceptV;
}
// Subdivide our space based on the size of the lowest level of the tree...
const F32 invSize = 1.f / F32(BIT(mTreeDepth-1));
// Grab this off the frame allocator, we don't want to do a proper alloc
// on every ray!
FrameAllocatorMarker stackAlloc;
RayStackNode *stack = (RayStackNode*)stackAlloc.alloc(sizeof(RayStackNode) * (mTreeDepth * 3 + 1));
U32 stackSize = 1;
// Kick off the stack with the root node.
stack[0].startT = 0;
stack[0].endT = 1;
stack[0].squarePos.set(0,0);
stack[0].level = mTreeDepth - 1;
//Con::printf("QuadTreeTracer::castRay(%x)", this);
// Aright, now let's do some raycasting!
while(stackSize--)
{
// Get the current node for easy access...
RayStackNode *sn = stack + stackSize;
const U32 level = sn->level;
const F32 startT = sn->startT;
const F32 endT = sn->endT;
const Point2I squarePos = sn->squarePos;
AssertFatal((startT >= 0.f) && (startT <= 1.f), "QuadTreeTracer::castRay - out of range startT on stack!");
AssertFatal((endT >= 0.f) && (endT <= 1.f), "QuadTreeTracer::castRay - out of range endT on stack!");
//Con::printf(" -- node(%d, %d @ %d), sT=%g, eT=%g", squarePos.x, squarePos.y, level, startT, endT);
// Figure our start and end Z.
const F32 startZ = startT * (end.z - start.z) + start.z;
const F32 endZ = endT * (end.z - start.z) + start.z;
// Ok, now let's see if we hit the lower bound
const F32 squareMin = getSquareMin(level, squarePos);
if(startZ < squareMin && endZ < squareMin)
continue; //Nope, skip out.
// Hmm, let's check the upper bound.
const F32 squareMax = getSquareMax(level, squarePos);
if(startZ > squareMax && endZ > squareMax)
continue; //Nope, skip out.
// We might be intersecting something... If we've hit
// the tree depth let's deal with the leaf intersection.
if(level == 0)
{
//Con::printf(" ++ check node(%d, %d @ %d), sT=%g, eT=%g", squarePos.x, squarePos.y, level, startT, endT);
if(castLeafRay(squarePos, start, end, startT, endT, info))
{
PROFILE_END();
return true; // We hit, tell 'em so!
}
continue; // Otherwise, keep looking.
}
else
{
// Ok, we have to push our children as we're an inner node.
// First, figure out some widths...
U32 squareSize = BIT(level);
U32 subSqSize = BIT(level - 1);
// Now, calculate intercepts so we know how to deal with this
// situation... (intercept = position, int = t value for that pos)
const F32 xIntercept = (squarePos.x + subSqSize) * invSize;
F32 xInt = calcInterceptX(start.x, invDeltaX, xIntercept);
const F32 yIntercept = (squarePos.y + subSqSize) * invSize;
F32 yInt = calcInterceptY(start.y, invDeltaY, yIntercept);
// Our starting position for this subray...
const F32 startX = startT * (end.x - start.x) + start.x;
const F32 startY = startT * (end.y - start.y) + start.y;
// Deal with squares that might be "behind" the ray.
if(xInt < startT) xInt = F32_MAX;
if(yInt < startT) yInt = F32_MAX;
// Do a little magic to calculate our next checks...
const U32 x0 = (startX > xIntercept) * subSqSize;
const U32 y0 = (startY > yIntercept) * subSqSize;
const U32 x1 = subSqSize - x0;
const U32 y1 = subSqSize - y0;
//AssertFatal(x1 < 1000, "QuadTreeTracer::castRay - possible overflow in x1!");
//AssertFatal(y1 < 1000, "QuadTreeTracer::castRay - possible overflow in y1!");
const U32 nextLevel = level - 1;
// Ok, now let's figure out what nodes, in what order, need to go
// on the stack. We push things on in reverse order of processing.
if(xInt > endT && yInt > endT)
{
stack[stackSize].squarePos.set(squarePos.x + x0, squarePos.y + y0);
stack[stackSize].level = nextLevel;
stackSize++;
}
else if(xInt < yInt)
{
F32 nextIntersect = endT;
if(yInt <= endT)
{
stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1);
stack[stackSize].startT = yInt;
stack[stackSize].endT = endT;
stack[stackSize].level = nextLevel;
nextIntersect = yInt;
stackSize++;
}
// Do middle two, order doesn't matter.
stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y0);
stack[stackSize].startT = xInt;
stack[stackSize].endT = nextIntersect;
stack[stackSize].level = nextLevel;
stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0);
stack[stackSize+1].startT = startT;
stack[stackSize+1].endT = xInt;
stack[stackSize+1].level = nextLevel;
stackSize += 2;
}
else if(yInt < xInt)
{
F32 nextIntersect = endT;
if(xInt <= endT)
{
stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1);
stack[stackSize].startT = xInt;
stack[stackSize].endT = endT;
stack[stackSize].level = nextLevel;
nextIntersect = xInt;
stackSize++;
}
stack[stackSize].squarePos.set(squarePos.x + x0, squarePos.y + y1);
stack[stackSize].startT = yInt;
stack[stackSize].endT = nextIntersect;
stack[stackSize].level = nextLevel;
stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0);
stack[stackSize+1].startT = startT;
stack[stackSize+1].endT = yInt;
stack[stackSize+1].level = nextLevel;
stackSize += 2;
}
else
{
stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1);
stack[stackSize].startT = xInt;
stack[stackSize].endT = endT;
stack[stackSize].level = nextLevel;
stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0);
stack[stackSize+1].startT = startT;
stack[stackSize+1].endT = xInt;
stack[stackSize+1].level = nextLevel;
stackSize += 2;
}
}
}
// Nothing found, so give up.
PROFILE_END();
return false;
}

87
engine/util/quadTreeTracer.h Executable file
View File

@ -0,0 +1,87 @@
//-----------------------------------------------------------------------------
// Torque Shader Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _QUADTREETRACER_H_
#define _QUADTREETRACER_H_
#include "platform/platform.h"
#include "math/mPoint.h"
#include "sim/sceneObject.h"
/// Helper class to perform a fast, recursive ray cast against a set of
/// hierarchical bounding boxes.
///
/// This class assumes that it is working on a unit quadtree (ie, one that
/// extends from 0..1 in the XY dimensions. Z scale is unaffected).
///
/// Node indexing is done TGE Terrain style - 0 is the largest level of the
/// quadtree, while coordinates are always in the full range of the quadtree
/// (in a 6 deep tree, 0..63, for instance). This allows the quadtree descent
/// to be very fast!
class QuadTreeTracer
{
protected:
struct StackNode
{
Point2I squarePos;
U32 level;
};
struct RayStackNode : StackNode
{
F32 startT;
F32 endT;
};
U32 mTreeDepth;
QuadTreeTracer(U32 treeDepth)
: mTreeDepth(treeDepth)
{
}
/// Children better implement these! They return min/max height bounds
/// of the specified square.
virtual const F32 getSquareMin(const U32 &level, const Point2I &pos) const = 0;
virtual const F32 getSquareMax(const U32 &level, const Point2I &pos) const = 0;
/// And this does checks on leaf nodes.
virtual bool castLeafRay(const Point2I pos, const Point3F &start, const Point3F &end, const F32 &startT, const F32 &endT, RayInfo *info) = 0;
/// Helper function to calculate intercepts.
inline const F32 calcIntercept(const F32 vStart, const F32 invDeltaV, const F32 intercept) const
{
return (intercept - vStart) * invDeltaV;
}
public:
/// Size of a quadtree of depth.
static inline const U32 getNodeCount(const U32 depth)
{
return 0x55555555 & ((1 << depth*2) - 1);
}
/// Index of a node at given position in a quadtree.
static inline const U32 getNodeIndex(const U32 level, const Point2I pos)
{
//AssertFatal(level < mTreeDepth, "QuadTreeTracer::getNodeIndex - out of range level!)
AssertFatal(pos.x < BIT(level) && pos.x >= 0 , "QuadTreeTracer::getNodeIndex - out of range x for level!");
AssertFatal(pos.y < BIT(level) && pos.y >= 0 , "QuadTreeTracer::getNodeIndex - out of range y for level!");
const U32 base = getNodeCount(level);
return base + (pos.x << level) + pos.y;
}
/// Cast a ray against a quadtree of hierarchical bounding boxes.
///
/// This method assumes the quadtree extends from (0..1) along the
/// X and Y axes. Z is unscaled. You may need to adjust the points
/// you pass into this method to get the proper results.
bool castRay(const Point3F &start, const Point3F &end, RayInfo *info);
};
#endif

150
engine/util/rectClipper.cpp Executable file
View File

@ -0,0 +1,150 @@
//-----------------------------------------------------------------------------
// Torque Shader Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "util/rectClipper.h"
namespace {
inline void
swap(F32& in_one, F32& in_two)
{
F32 temp = in_one;
in_one = in_two;
in_two = temp;
}
}
bool
RectClipper::clipLine(const Point2I& in_rStart,
const Point2I& in_rEnd,
Point2I& out_rStart,
Point2I& out_rEnd) const
{
// Check for trivial rejection
if ((in_rStart.x < m_clipRect.point.x && in_rEnd.x < m_clipRect.point.x) ||
(in_rStart.x >= m_clipRect.point.x + m_clipRect.extent.x &&
in_rEnd.x >= m_clipRect.point.x + m_clipRect.extent.x))
return false;
if ((in_rStart.y < m_clipRect.point.y && in_rEnd.y < m_clipRect.point.y) ||
(in_rStart.y >= m_clipRect.point.y + m_clipRect.extent.y &&
in_rEnd.y >= m_clipRect.point.y + m_clipRect.extent.y))
return false;
F32 x1 = F32(in_rStart.x);
F32 y1 = F32(in_rStart.y);
F32 x2 = F32(in_rEnd.x);
F32 y2 = F32(in_rEnd.y);
// I'm using essentially what's in the Phoenix libs, Liang-Biarsky based, but
// converted to FP math for greater precision on the back end...
//
bool flipped = false;
if (x1 > x2)
{
swap(x1, x2);
swap(y1, y2);
flipped = !flipped;
}
F32 dx = x2 - x1;
F32 dy = y2 - y1;
// Clip x coord
F32 t;
if (x1 < F32(m_clipRect.point.x))
{
t = (F32(m_clipRect.point.x) - x1) / F32(dx);
x1 = F32(m_clipRect.point.x);
y1 += t * dy;
dx = x2 - x1;
dy = y2 - y1;
}
if (x2 >= F32(m_clipRect.point.x + m_clipRect.extent.x))
{
t = (F32(m_clipRect.point.x + m_clipRect.extent.x - 1) - x1) / F32(dx);
x2 = F32(m_clipRect.point.x + m_clipRect.extent.x - 1);
y2 = y1 + (t * dy);
dx = x2 - x1;
dy = y2 - y1;
}
// Recheck trivial rejection condition...
if((y1 > F32(m_clipRect.point.y + m_clipRect.extent.y - 1) &&
y2 > F32(m_clipRect.point.y + m_clipRect.extent.y - 1)) ||
(y1 < F32(m_clipRect.point.y) && y2 < F32(m_clipRect.point.y)))
return false;
if (y1 > y2)
{
swap(x1, x2);
swap(y1, y2);
flipped = !flipped;
}
if (y1 < F32(m_clipRect.point.y))
{
t = (F32(m_clipRect.point.y) - y1) / F32(dy);
y1 = F32(m_clipRect.point.y);
x1 += t * dx;
dx = x2 - x1;
dy = y2 - y1;
}
if (y2 > F32(m_clipRect.point.y + m_clipRect.extent.y - 1))
{
t = (F32(m_clipRect.point.y + m_clipRect.extent.y - 1) - y1) / F32(dy);
y2 = F32(m_clipRect.point.y + m_clipRect.extent.y - 1);
x2 = x1 + (t * dx);
}
if (flipped == true)
{
out_rEnd.x = S32(x1 + 0.5f);
out_rEnd.y = S32(y1 + 0.5f);
out_rStart.x = S32(x2 + 0.5f);
out_rStart.y = S32(y2 + 0.5f);
}
else
{
out_rStart.x = S32(x1 + 0.5f);
out_rStart.y = S32(y1 + 0.5f);
out_rEnd.x = S32(x2 + 0.5f);
out_rEnd.y = S32(y2 + 0.5f);
}
return true;
}
bool
RectClipper::clipRect(const RectI& in_rRect,
RectI& out_rRect) const
{
AssertFatal(in_rRect.isValidRect(), "Inappropriate min/max coords for rectangle");
if (in_rRect.point.x + in_rRect.extent.x - 1 < m_clipRect.point.x ||
in_rRect.point.x > m_clipRect.point.x + m_clipRect.extent.x - 1)
return false;
if (in_rRect.point.y + in_rRect.extent.y - 1 < m_clipRect.point.y ||
in_rRect.point.y > m_clipRect.point.y + m_clipRect.extent.y - 1)
return false;
if (in_rRect.point.x < m_clipRect.point.x) out_rRect.point.x = m_clipRect.point.x;
else out_rRect.point.x = in_rRect.point.x;
if (in_rRect.point.y < m_clipRect.point.y) out_rRect.point.y = m_clipRect.point.y;
else out_rRect.point.y = in_rRect.point.y;
Point2I bottomR;
bottomR.x = getMin(in_rRect.point.x + in_rRect.extent.x - 1,
m_clipRect.point.x + m_clipRect.extent.x - 1);
bottomR.y = getMin(in_rRect.point.y + in_rRect.extent.y - 1,
m_clipRect.point.y + m_clipRect.extent.y - 1);
out_rRect.extent.x = bottomR.x - out_rRect.point.x + 1;
out_rRect.extent.x = bottomR.y - out_rRect.point.y + 1;
return true;
}

55
engine/util/rectClipper.h Executable file
View File

@ -0,0 +1,55 @@
//-----------------------------------------------------------------------------
// Torque Shader Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _RECTCLIPPER_H_
#define _RECTCLIPPER_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _MRECT_H_
#include "math/mRect.h"
#endif
class RectClipper
{
RectI m_clipRect;
public:
RectClipper(const RectI& in_rRect);
bool clipPoint(const Point2I& in_rPoint) const;
bool clipLine(const Point2I& in_rStart,
const Point2I& in_rEnd,
Point2I& out_rStart,
Point2I& out_rEnd) const;
bool clipRect(const RectI& in_rRect,
RectI& out_rRect) const;
};
//------------------------------------------------------------------------------
//-------------------------------------- INLINES
//
inline
RectClipper::RectClipper(const RectI& in_rRect)
: m_clipRect(in_rRect)
{
//
}
inline bool
RectClipper::clipPoint(const Point2I& in_rPoint) const
{
if ((in_rPoint.x < m_clipRect.point.x) ||
(in_rPoint.y < m_clipRect.point.y) ||
(in_rPoint.x >= m_clipRect.point.x + m_clipRect.extent.x) ||
(in_rPoint.y >= m_clipRect.point.y + m_clipRect.extent.y))
return false;
return true;
}
#endif //_RECTCLIPPER_H_

15
engine/util/safeDelete.h Executable file
View File

@ -0,0 +1,15 @@
//-----------------------------------------------------------------------------
// Torque Shader Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
// Utility macros. We clobber to enforce our semantics!
#undef SAFE_DELETE
#define SAFE_DELETE(a) if( (a) != NULL ) delete (a); (a) = NULL;
#undef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(a) if( (a) != NULL ) delete [] (a); (a) = NULL;
#undef SAFE_FREE
#define SAFE_FREE(a) if( (a) != NULL ) dFree (a); (a) = NULL;

175
engine/util/triBoxCheck.cpp Executable file
View File

@ -0,0 +1,175 @@
//-----------------------------------------------------------------------------
// Torque Shader Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// AABB-triangle overlap test code originally by Tomas Akenine-M<>ller
// Assisted by Pierre Terdiman and David Hunt
// http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/
// Ported to TSE by BJG, 2005-4-14
//-----------------------------------------------------------------------------
#include "util/triBoxCheck.h"
#define FINDMINMAX(x0,x1,x2,min,max) \
min = max = x0; \
if(x1<min) min=x1;\
if(x1>max) max=x1;\
if(x2<min) min=x2;\
if(x2>max) max=x2;
bool planeBoxOverlap(Point3F normal, Point3F vert, Point3F maxbox)
{
S32 q;
F32 v;
Point3F vmin, vmax;
for(q=0;q<=2;q++)
{
v=vert[q];
if(normal[q]>0.0f)
{
vmin[q]=-maxbox[q] - v;
vmax[q]= maxbox[q] - v;
}
else
{
vmin[q]= maxbox[q] - v;
vmax[q]=-maxbox[q] - v;
}
}
if(mDot(normal, vmin) > 0.f)
return false;
if(mDot(normal, vmax) >= 0.f)
return true;
return false;
}
/*======================== X-tests ========================*/
#define AXISTEST_X01(a, b, fa, fb) \
p0 = a*v0.y - b*v0.z; \
p2 = a*v2.y - b*v2.z; \
if(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \
rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
if(min>rad || max<-rad) return false;
#define AXISTEST_X2(a, b, fa, fb) \
p0 = a*v0.y - b*v0.z; \
p1 = a*v1.y - b*v1.z; \
if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \
rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
if(min>rad || max<-rad) return false;
/*======================== Y-tests ========================*/
#define AXISTEST_Y02(a, b, fa, fb) \
p0 = -a*v0.x + b*v0.z; \
p2 = -a*v2.x + b*v2.z; \
if(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \
rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
if(min>rad || max<-rad) return false;
#define AXISTEST_Y1(a, b, fa, fb) \
p0 = -a*v0.x + b*v0.z; \
p1 = -a*v1.x + b*v1.z; \
if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \
rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
if(min>rad || max<-rad) return false;
/*======================== Z-tests ========================*/
#define AXISTEST_Z12(a, b, fa, fb) \
p1 = a*v1.x - b*v1.y; \
p2 = a*v2.x - b*v2.y; \
if(p2<p1) {min=p2; max=p1;} else {min=p1; max=p2;} \
rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
if(min>rad || max<-rad) return false;
#define AXISTEST_Z0(a, b, fa, fb) \
p0 = a*v0.x - b*v0.y; \
p1 = a*v1.x - b*v1.y; \
if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \
rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
if(min>rad || max<-rad) return false;
bool triBoxOverlap(Point3F boxcenter, Point3F boxhalfsize, Point3F triverts[3])
{
/* use separating axis theorem to test overlap between triangle and box */
/* need to test for overlap in these directions: */
/* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */
/* we do not even need to test these) */
/* 2) normal of the triangle */
/* 3) crossproduct(edge from tri, {x,y,z}-directin) */
/* this gives 3x3=9 more tests */
Point3F v0,v1,v2;
F32 min,max,p0,p1,p2,rad,fex,fey,fez; // -NJMP- "d" local variable removed
Point3F normal,e0,e1,e2;
/* This is the fastest branch on Sun */
/* move everything so that the boxcenter is in (0,0,0) */
v0 = triverts[0] - boxcenter;
v1 = triverts[1] - boxcenter;
v2 = triverts[2] - boxcenter;
/* compute triangle edges */
e0 = v1 - v0; /* tri edge 0 */
e1 = v2 - v1; /* tri edge 1 */
e2 = v0 - v2; /* tri edge 2 */
/* Bullet 3: */
/* test the 9 tests first (this was faster) */
fex = mFabs(e0.x);
fey = mFabs(e0.y);
fez = mFabs(e0.z);
AXISTEST_X01(e0.z, e0.y, fez, fey);
AXISTEST_Y02(e0.z, e0.x, fez, fex);
AXISTEST_Z12(e0.y, e0.x, fey, fex);
fex = mFabs(e1.x);
fey = mFabs(e1.y);
fez = mFabs(e1.z);
AXISTEST_X01(e1.z, e1.y, fez, fey);
AXISTEST_Y02(e1.z, e1.x, fez, fex);
AXISTEST_Z0(e1.y, e1.x, fey, fex);
fex = mFabs(e2.x);
fey = mFabs(e2.y);
fez = mFabs(e2.z);
AXISTEST_X2(e2.z, e2.y, fez, fey);
AXISTEST_Y1(e2.z, e2.x, fez, fex);
AXISTEST_Z12(e2.y, e2.x, fey, fex);
/* Bullet 1: */
/* first test overlap in the {x,y,z}-directions */
/* find min, max of the triangle each direction, and test for overlap in */
/* that direction -- this is equivalent to testing a minimal AABB around */
/* the triangle against the AABB */
/* test in X-direction */
FINDMINMAX(v0.x,v1.x,v2.x,min,max);
if(min>boxhalfsize.x || max<-boxhalfsize.x) return false;
/* test in Y-direction */
FINDMINMAX(v0.y,v1.y,v2.y,min,max);
if(min>boxhalfsize.y || max<-boxhalfsize.y) return false;
/* test in Z-direction */
FINDMINMAX(v0.z,v1.z,v2.z,min,max);
if(min>boxhalfsize.z || max<-boxhalfsize.z) return false;
/* Bullet 2: */
/* test if the box intersects the plane of the triangle */
/* compute plane equation of triangle: normal*x+d=0 */
normal = mCross(e0, e1);
if(!planeBoxOverlap(normal,v0,boxhalfsize)) return false;
return true; /* box and triangle overlaps */
}

35
engine/util/triBoxCheck.h Executable file
View File

@ -0,0 +1,35 @@
//-----------------------------------------------------------------------------
// Torque Shader Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// AABB-triangle overlap test code originally by Tomas Akenine-M<>ller
// Assisted by Pierre Terdiman and David Hunt
// http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/
// Ported to TSE by BJG, 2005-4-14
//-----------------------------------------------------------------------------
#ifndef _TRIBOXCHECK_H_
#define _TRIBOXCHECK_H_
#include "math/mPoint.h"
#include "math/mBox.h"
bool triBoxOverlap(Point3F boxcenter, Point3F boxhalfsize, Point3F triverts[3]);
/// Massage stuff into right format for triBoxOverlap test. This is really
/// just a helper function - use the other version if you want to be fast!
inline bool triBoxOverlap(Box3F box, Point3F a, Point3F b, Point3F c)
{
Point3F halfSize(box.len_x() / 2.f, box.len_y() / 2.f, box.len_z() / 2.f);
Point3F center;
box.getCenter(&center);
Point3F verts[3] = {a,b,c};
return triBoxOverlap(center, halfSize, verts);
}
#endif