added everything

This commit is contained in:
Metario
2017-04-17 06:17:10 -06:00
commit 9c6ff74f19
6121 changed files with 1625704 additions and 0 deletions

View File

@ -0,0 +1,97 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "collision/abstractPolyList.h"
//----------------------------------------------------------------------------
AbstractPolyList::~AbstractPolyList()
{
mInterestNormalRegistered = false;
}
static U32 PolyFace[6][4] = {
{ 3, 2, 1, 0 },
{ 7, 4, 5, 6 },
{ 0, 5, 4, 3 },
{ 6, 5, 0, 1 },
{ 7, 6, 1, 2 },
{ 4, 7, 2, 3 },
};
void AbstractPolyList::addBox(const Box3F &box)
{
Point3F pos = box.min;
F32 dx = box.max.x - box.min.x;
F32 dy = box.max.y - box.min.y;
F32 dz = box.max.z - box.min.z;
U32 base = addPoint(pos);
pos.y += dy; addPoint(pos);
pos.x += dx; addPoint(pos);
pos.y -= dy; addPoint(pos);
pos.z += dz; addPoint(pos);
pos.x -= dx; addPoint(pos);
pos.y += dy; addPoint(pos);
pos.x += dx; addPoint(pos);
for (S32 i = 0; i < 6; i++) {
begin(0,i);
S32 v1 = base + PolyFace[i][0];
S32 v2 = base + PolyFace[i][1];
S32 v3 = base + PolyFace[i][2];
S32 v4 = base + PolyFace[i][3];
vertex(v1);
vertex(v2);
vertex(v3);
vertex(v4);
plane(v1,v2,v3);
end();
}
}
bool AbstractPolyList::getMapping(MatrixF *, Box3F *)
{
// return list transform and bounds in list space...optional
return false;
}
bool AbstractPolyList::isInterestedInPlane(const PlaneF& plane)
{
if (mInterestNormalRegistered == false) {
return true;
}
else {
PlaneF xformed;
mPlaneTransformer.transform(plane, xformed);
if (mDot(xformed, mInterestNormal) >= 0.0f)
return false;
else
return true;
}
}
bool AbstractPolyList::isInterestedInPlane(const U32 index)
{
if (mInterestNormalRegistered == false) {
return true;
}
else {
const PlaneF& rPlane = getIndexedPlane(index);
if (mDot(rPlane, mInterestNormal) >= 0.0f)
return false;
else
return true;
}
}
void AbstractPolyList::setInterestNormal(const Point3F& normal)
{
mInterestNormalRegistered = true;
mInterestNormal = normal;
}

View File

@ -0,0 +1,241 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _ABSTRACTPOLYLIST_H_
#define _ABSTRACTPOLYLIST_H_
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _MPLANETRANSFORMER_H_
#include "math/mPlaneTransformer.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
class SceneObject;
//----------------------------------------------------------------------------
/// A polygon filtering interface.
///
/// The various AbstractPolyList subclasses are used in Torque as an interface to
/// handle spatial queries. SceneObject::buildPolyList() takes an implementor of
/// AbstractPolyList (such as ConcretePolyList, ClippedPolyList, etc.) and a
/// bounding volume. The function runs geometry data from all the objects in the
/// bounding volume through the passed PolyList.
///
/// This interface only provides a method to get data INTO your implementation. Different
/// subclasses provide different interfaces to get data back out, depending on their
/// specific quirks.
///
/// The physics engine now uses convex hulls for collision detection.
///
/// @see Convex
class AbstractPolyList
{
protected:
// User set state
SceneObject* mCurrObject;
MatrixF mBaseMatrix; // Base transform
MatrixF mTransformMatrix; // Current object transform
MatrixF mMatrix; // Base * current transform
Point3F mScale;
PlaneTransformer mPlaneTransformer;
bool mInterestNormalRegistered;
Point3F mInterestNormal;
public:
AbstractPolyList();
virtual ~AbstractPolyList();
/// @name Common Interface
/// @{
void setBaseTransform(const MatrixF& mat);
/// Sets the transform applying to the current stream of
/// vertices.
///
/// @param mat Transformation of the object. (in)
/// @param scale Scaling of the object. (in)
void setTransform(const MatrixF* mat, const Point3F& scale);
/// Gets the transform applying to the current stream of
/// vertices.
///
/// @param mat Transformation of the object. (out)
/// @param scale Scaling of the object. (out)
void getTransform(MatrixF* mat, Point3F * scale);
/// This is called by the object which is currently feeding us
/// vertices, to tell us who it is.
void setObject(SceneObject*);
/// Add a box via the query interface (below). This wraps some calls
/// to addPoint and addPlane.
void addBox(const Box3F &box);
void doConstruct();
/// @}
/// @name Query Interface
///
/// It is through this interface that geometry data is fed to the
/// PolyList. The order of calls are:
/// - begin()
/// - One or more calls to vertex() and one call to plane(), in any order.
/// - end()
///
/// @code
/// // Example code that adds data to a PolyList.
/// // See AbstractPolyList::addBox() for the function this was taken from.
///
/// // First, we add points... (note that we use base to track the start of adding.)
/// U32 base = addPoint(pos);
/// pos.y += dy; addPoint(pos);
/// pos.x += dx; addPoint(pos);
/// pos.y -= dy; addPoint(pos);
/// pos.z += dz; addPoint(pos);
/// pos.x -= dx; addPoint(pos);
/// pos.y += dy; addPoint(pos);
/// pos.x += dx; addPoint(pos);
///
/// // Now we add our surfaces. (there are six, as we are adding a cube here)
/// for (S32 i = 0; i < 6; i++) {
/// // Begin a surface
/// begin(0,i);
///
/// // Calculate the vertex ids; we have a lookup table to tell us offset from base.
/// // In your own code, you might use a different method.
/// S32 v1 = base + PolyFace[i][0];
/// S32 v2 = base + PolyFace[i][1];
/// S32 v3 = base + PolyFace[i][2];
/// S32 v4 = base + PolyFace[i][3];
///
/// // Reference the four vertices that make up this surface.
/// vertex(v1);
/// vertex(v2);
/// vertex(v3);
/// vertex(v4);
///
/// // Indicate the plane of this surface.
/// plane(v1,v2,v3);
///
/// // End the surface.
/// end();
/// }
/// @endcode
/// @{
/// Are we empty of data?
virtual bool isEmpty() const = 0;
/// Adds a point to the poly list, and returns
/// an ID number for that point.
virtual U32 addPoint(const Point3F& p) = 0;
/// Adds a plane to the poly list, and returns
/// an ID number for that point.
virtual U32 addPlane(const PlaneF& plane) = 0;
/// Start a surface.
///
/// @param material A material ID for this surface.
/// @param surfaceKey A key value to associate with this surface.
virtual void begin(U32 material,U32 surfaceKey) = 0;
/// Indicate the plane of the surface.
virtual void plane(U32 v1,U32 v2,U32 v3) = 0;
/// Indicate the plane of the surface.
virtual void plane(const PlaneF& p) = 0;
/// Indicate the plane of the surface.
virtual void plane(const U32 index) = 0;
/// Reference a vertex which is in this surface.
virtual void vertex(U32 vi) = 0;
/// Mark the end of a surface.
virtual void end() = 0;
/// Return list transform and bounds in list space.
///
/// @returns False if no data is available.
virtual bool getMapping(MatrixF *, Box3F *);
/// @}
/// @name Interest
///
/// This is a mechanism to let you specify interest in a specific normal.
/// If you set a normal you're interested in, then any planes facing "away"
/// from that normal are culled from the results.
///
/// This is handy if you're using this to do a physics check, as you're not
/// interested in polygons facing away from you (since you don't collide with
/// the backsides/insides of things).
/// @{
void setInterestNormal(const Point3F& normal);
void clearInterestNormal() { mInterestNormalRegistered = false; }
virtual bool isInterestedInPlane(const PlaneF& plane);
virtual bool isInterestedInPlane(const U32 index);
/// @}
protected:
/// A helper function to convert a plane index to a PlaneF structure.
virtual const PlaneF& getIndexedPlane(const U32 index) = 0;
};
inline AbstractPolyList::AbstractPolyList()
{
doConstruct();
}
inline void AbstractPolyList::doConstruct()
{
mCurrObject = NULL;
mBaseMatrix.identity();
mMatrix.identity();
mScale.set(1, 1, 1);
mPlaneTransformer.setIdentity();
mInterestNormalRegistered = false;
}
inline void AbstractPolyList::setBaseTransform(const MatrixF& mat)
{
mBaseMatrix = mat;
}
inline void AbstractPolyList::setTransform(const MatrixF* mat, const Point3F& scale)
{
mMatrix = mBaseMatrix;
mTransformMatrix = *mat;
mMatrix.mul(*mat);
mScale = scale;
mPlaneTransformer.set(mMatrix, mScale);
}
inline void AbstractPolyList::getTransform(MatrixF* mat, Point3F * scale)
{
*mat = mTransformMatrix;
*scale = mScale;
}
inline void AbstractPolyList::setObject(SceneObject* obj)
{
mCurrObject = obj;
}
#endif

199
engine/collision/boxConvex.cc Executable file
View File

@ -0,0 +1,199 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "math/mMath.h"
#include "game/gameBase.h"
#include "collision/boxConvex.h"
//----------------------------------------------------------------------------
static struct Corner {
S32 a,b,c;
S32 ab,ac,bc;
} sCorner[] =
{
{ 1,2,4, 4,0,1 },
{ 0,3,5, 4,0,3 },
{ 0,3,6, 4,1,2 },
{ 1,2,7, 4,3,2 },
{ 0,5,6, 0,1,5 },
{ 1,4,7, 0,3,5 },
{ 2,4,7, 1,2,5 },
{ 3,5,6, 3,2,5 },
};
static struct Face {
S32 vertex[4];
S32 axis;
bool flip;
} sFace[] =
{
{ 0,4,5,1, 1,true },
{ 0,2,6,4, 0,true },
{ 3,7,6,2, 1,false },
{ 3,1,5,7, 0,false },
{ 0,1,3,2, 2,true },
{ 4,6,7,5, 2,false },
};
Point3F BoxConvex::support(const VectorF& v) const
{
Point3F p = mCenter;
p.x += (v.x >= 0)? mSize.x: -mSize.x;
p.y += (v.y >= 0)? mSize.y: -mSize.y;
p.z += (v.z >= 0)? mSize.z: -mSize.z;
return p;
}
Point3F BoxConvex::getVertex(S32 v)
{
Point3F p = mCenter;
p.x += (v & 1)? mSize.x: -mSize.x;
p.y += (v & 2)? mSize.y: -mSize.y;
p.z += (v & 4)? mSize.z: -mSize.z;
return p;
}
inline bool isOnPlane(Point3F p,PlaneF& plane)
{
F32 dist = mDot(plane,p) + plane.d;
return dist < 0.1 && dist > -0.1;
}
void BoxConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf)
{
cf->material = 0;
cf->object = mObject;
S32 v = 0;
v += (n.x >= 0)? 1: 0;
v += (n.y >= 0)? 2: 0;
v += (n.z >= 0)? 4: 0;
PlaneF plane;
plane.set(getVertex(v),n);
// Emit vertex and edge
S32 mask = 0;
Corner& corner = sCorner[v];
mask |= isOnPlane(getVertex(corner.a),plane)? 1: 0;
mask |= isOnPlane(getVertex(corner.b),plane)? 2: 0;
mask |= isOnPlane(getVertex(corner.c),plane)? 4: 0;
switch(mask) {
case 0: {
cf->mVertexList.increment();
mat.mulP(getVertex(v),&cf->mVertexList.last());
break;
}
case 1:
emitEdge(v,corner.a,mat,cf);
break;
case 2:
emitEdge(v,corner.b,mat,cf);
break;
case 4:
emitEdge(v,corner.c,mat,cf);
break;
case 1 | 2:
emitFace(corner.ab,mat,cf);
break;
case 2 | 4:
emitFace(corner.bc,mat,cf);
break;
case 1 | 4:
emitFace(corner.ac,mat,cf);
break;
}
}
void BoxConvex::getPolyList(AbstractPolyList* list)
{
list->setTransform(&getTransform(), getScale());
list->setObject(getObject());
U32 base = list->addPoint(mCenter + Point3F(-mSize.x, -mSize.y, -mSize.z));
list->addPoint(mCenter + Point3F( mSize.x, -mSize.y, -mSize.z));
list->addPoint(mCenter + Point3F(-mSize.x, mSize.y, -mSize.z));
list->addPoint(mCenter + Point3F( mSize.x, mSize.y, -mSize.z));
list->addPoint(mCenter + Point3F(-mSize.x, -mSize.y, mSize.z));
list->addPoint(mCenter + Point3F( mSize.x, -mSize.y, mSize.z));
list->addPoint(mCenter + Point3F(-mSize.x, mSize.y, mSize.z));
list->addPoint(mCenter + Point3F( mSize.x, mSize.y, mSize.z));
for (U32 i = 0; i < 6; i++) {
list->begin(0, i);
list->vertex(base + sFace[i].vertex[0]);
list->vertex(base + sFace[i].vertex[1]);
list->vertex(base + sFace[i].vertex[2]);
list->vertex(base + sFace[i].vertex[3]);
list->plane(base + sFace[i].vertex[0],
base + sFace[i].vertex[1],
base + sFace[i].vertex[2]);
list->end();
}
}
void BoxConvex::emitEdge(S32 v1,S32 v2,const MatrixF& mat,ConvexFeature* cf)
{
S32 vc = cf->mVertexList.size();
cf->mVertexList.increment(2);
Point3F *vp = cf->mVertexList.begin();
mat.mulP(getVertex(v1),&vp[vc]);
mat.mulP(getVertex(v2),&vp[vc + 1]);
cf->mEdgeList.increment();
ConvexFeature::Edge& edge = cf->mEdgeList.last();
edge.vertex[0] = vc;
edge.vertex[1] = vc + 1;
}
void BoxConvex::emitFace(S32 fi,const MatrixF& mat,ConvexFeature* cf)
{
Face& face = sFace[fi];
// Emit vertices
S32 vc = cf->mVertexList.size();
cf->mVertexList.increment(4);
Point3F *vp = cf->mVertexList.begin();
for (S32 v = 0; v < 4; v++)
mat.mulP(getVertex(face.vertex[v]),&vp[vc + v]);
// Emit edges
cf->mEdgeList.increment(4);
ConvexFeature::Edge* edge = cf->mEdgeList.end() - 4;
for (S32 e = 0; e < 4; e++) {
edge[e].vertex[0] = vc + e;
edge[e].vertex[1] = vc + ((e + 1) & 3);
}
// Emit 2 triangle faces
cf->mFaceList.increment(2);
ConvexFeature::Face* ef = cf->mFaceList.end() - 2;
mat.getColumn(face.axis,&ef->normal);
if (face.flip)
ef[0].normal.neg();
ef[1].normal = ef[0].normal;
ef[1].vertex[0] = ef[0].vertex[0] = vc;
ef[1].vertex[1] = ef[0].vertex[2] = vc + 2;
ef[0].vertex[1] = vc + 1;
ef[1].vertex[2] = vc + 3;
}
const MatrixF& OrthoBoxConvex::getTransform() const
{
Point3F translation;
Parent::getTransform().getColumn(3, &translation);
mOrthoMatrixCache.setColumn(3, translation);
return mOrthoMatrixCache;
}

46
engine/collision/boxConvex.h Executable file
View File

@ -0,0 +1,46 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _BOXCONVEX_H_
#define _BOXCONVEX_H_
#ifndef _CONVEX_H_
#include "collision/convex.h"
#endif
//----------------------------------------------------------------------------
class BoxConvex: public Convex
{
Point3F getVertex(S32 v);
void emitEdge(S32 v1,S32 v2,const MatrixF& mat,ConvexFeature* cf);
void emitFace(S32 fi,const MatrixF& mat,ConvexFeature* cf);
public:
//
Point3F mCenter;
VectorF mSize;
BoxConvex() { mType = BoxConvexType; }
void init(SceneObject* obj) { mObject = obj; }
Point3F support(const VectorF& v) const;
void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf);
void getPolyList(AbstractPolyList* list);
};
class OrthoBoxConvex: public BoxConvex
{
typedef BoxConvex Parent;
mutable MatrixF mOrthoMatrixCache;
public:
OrthoBoxConvex() { mOrthoMatrixCache.identity(); }
virtual const MatrixF& getTransform() const;
};
#endif

View File

@ -0,0 +1,275 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "dgl/dgl.h"
#include "math/mMath.h"
#include "console/console.h"
#include "collision/clippedPolyList.h"
bool ClippedPolyList::allowClipping = true;
//----------------------------------------------------------------------------
ClippedPolyList::ClippedPolyList()
{
VECTOR_SET_ASSOCIATION(mPolyList);
VECTOR_SET_ASSOCIATION(mVertexList);
VECTOR_SET_ASSOCIATION(mIndexList);
VECTOR_SET_ASSOCIATION(mPolyPlaneList);
VECTOR_SET_ASSOCIATION(mPlaneList);
mNormal.set(0,0,0);
mIndexList.reserve(100);
}
ClippedPolyList::~ClippedPolyList()
{
}
//----------------------------------------------------------------------------
void ClippedPolyList::clear()
{
// Only clears internal data
mPolyList.clear();
mVertexList.clear();
mIndexList.clear();
mPolyPlaneList.clear();
}
bool ClippedPolyList::isEmpty() const
{
return mPolyList.size() == 0;
}
//----------------------------------------------------------------------------
U32 ClippedPolyList::addPoint(const Point3F& p)
{
mVertexList.increment();
Vertex& v = mVertexList.last();
v.point.x = p.x * mScale.x;
v.point.y = p.y * mScale.y;
v.point.z = p.z * mScale.z;
mMatrix.mulP(v.point);
// Build the plane mask
register U32 mask = 1;
register S32 count = mPlaneList.size();
register PlaneF * plane = mPlaneList.address();
v.mask = 0;
while(--count >= 0) {
if (plane++->distToPlane(v.point) > 0)
v.mask |= mask;
mask <<= 1;
}
return mVertexList.size() - 1;
}
U32 ClippedPolyList::addPlane(const PlaneF& plane)
{
mPolyPlaneList.increment();
mPlaneTransformer.transform(plane, mPolyPlaneList.last());
return mPolyPlaneList.size() - 1;
}
//----------------------------------------------------------------------------
void ClippedPolyList::begin(U32 material,U32 surfaceKey)
{
mPolyList.increment();
Poly& poly = mPolyList.last();
poly.object = mCurrObject;
poly.material = material;
poly.vertexStart = mIndexList.size();
poly.surfaceKey = surfaceKey;
poly.polyFlags = 0;
if(ClippedPolyList::allowClipping)
poly.polyFlags = CLIPPEDPOLYLIST_FLAG_ALLOWCLIPPING;
}
//----------------------------------------------------------------------------
void ClippedPolyList::plane(U32 v1,U32 v2,U32 v3)
{
mPolyList.last().plane.set(mVertexList[v1].point,
mVertexList[v2].point,mVertexList[v3].point);
}
void ClippedPolyList::plane(const PlaneF& p)
{
mPlaneTransformer.transform(p, mPolyList.last().plane);
}
void ClippedPolyList::plane(const U32 index)
{
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
mPolyList.last().plane = mPolyPlaneList[index];
}
const PlaneF& ClippedPolyList::getIndexedPlane(const U32 index)
{
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
return mPolyPlaneList[index];
}
//----------------------------------------------------------------------------
void ClippedPolyList::vertex(U32 vi)
{
mIndexList.push_back(vi);
}
//----------------------------------------------------------------------------
void ClippedPolyList::end()
{
Poly& poly = mPolyList.last();
// Anything facing away from the mNormal is rejected
if (mDot(poly.plane,mNormal) > 0) {
mIndexList.setSize(poly.vertexStart);
mPolyList.decrement();
return;
}
// Build initial inside/outside plane masks
U32 indexStart = poly.vertexStart;
U32 vertexCount = mIndexList.size() - indexStart;
U32 frontMask = 0,backMask = 0;
U32 i;
for (i = indexStart; i < mIndexList.size(); i++) {
U32 mask = mVertexList[mIndexList[i]].mask;
frontMask |= mask;
backMask |= ~mask;
}
// Trivial accept if all the vertices are on the backsides of
// all the planes.
if (!frontMask) {
poly.vertexCount = vertexCount;
return;
}
// Trivial reject if any plane not crossed has all it's points
// on the front.
U32 crossMask = frontMask & backMask;
if (~crossMask & frontMask) {
mIndexList.setSize(poly.vertexStart);
mPolyList.decrement();
return;
}
// Need to do some clipping
for (U32 p = 0; p < mPlaneList.size(); p++) {
U32 pmask = 1 << p;
// Only test against this plane if we have something
// on both sides
if (crossMask & pmask) {
U32 indexEnd = mIndexList.size();
U32 i1 = indexEnd - 1;
U32 mask1 = mVertexList[mIndexList[i1]].mask;
for (U32 i2 = indexStart; i2 < indexEnd; i2++) {
U32 mask2 = mVertexList[mIndexList[i2]].mask;
if ((mask1 ^ mask2) & pmask) {
//
mVertexList.increment();
VectorF& v1 = mVertexList[mIndexList[i1]].point;
VectorF& v2 = mVertexList[mIndexList[i2]].point;
VectorF vv = v2 - v1;
F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv);
mIndexList.push_back(mVertexList.size() - 1);
Vertex& iv = mVertexList.last();
iv.point.x = v1.x + vv.x * t;
iv.point.y = v1.y + vv.y * t;
iv.point.z = v1.z + vv.z * t;
iv.mask = 0;
// Test against the remaining planes
for (U32 i = p + 1; i < mPlaneList.size(); i++)
if (mPlaneList[i].distToPlane(iv.point) > 0) {
iv.mask = 1 << i;
break;
}
}
if (!(mask2 & pmask)) {
U32 index = mIndexList[i2];
mIndexList.push_back(index);
}
mask1 = mask2;
i1 = i2;
}
// Check for degenerate
indexStart = indexEnd;
if (mIndexList.size() - indexStart < 3) {
mIndexList.setSize(poly.vertexStart);
mPolyList.decrement();
return;
}
}
}
// Emit what's left and compress the index list.
poly.vertexCount = mIndexList.size() - indexStart;
memcpy(&mIndexList[poly.vertexStart],
&mIndexList[indexStart],poly.vertexCount);
mIndexList.setSize(poly.vertexStart + poly.vertexCount);
}
//----------------------------------------------------------------------------
void ClippedPolyList::memcpy(U32* dst, U32* src,U32 size)
{
U32* end = src + size;
while (src != end)
*dst++ = *src++;
}
//----------------------------------------------------------------------------
void ClippedPolyList::render()
{
glVertexPointer(3,GL_FLOAT,sizeof(Vertex),mVertexList.address());
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(1,0,0,0.25);
glEnable(GL_BLEND);
glDisable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
Poly * p;
for (p = mPolyList.begin(); p < mPolyList.end(); p++) {
glDrawElements(GL_POLYGON,p->vertexCount,
GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
}
glColor3f(0.6,0.6,0.6);
glDisable(GL_BLEND);
for (p = mPolyList.begin(); p < mPolyList.end(); p++) {
glDrawElements(GL_LINE_LOOP,p->vertexCount,
GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
}
glDisableClientState(GL_VERTEX_ARRAY);
}

View File

@ -0,0 +1,90 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _CLIPPEDPOLYLIST_H_
#define _CLIPPEDPOLYLIST_H_
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _ABSTRACTPOLYLIST_H_
#include "collision/abstractPolyList.h"
#endif
#define CLIPPEDPOLYLIST_FLAG_ALLOWCLIPPING 0x01
//----------------------------------------------------------------------------
/// Clipped PolyList
///
/// This takes the geometry passed to it and clips against the PlaneList set
/// by the caller.
//
/// @see AbstractPolyList
class ClippedPolyList: public AbstractPolyList
{
void memcpy(U32* d, U32* s,U32 size);
public:
struct Vertex {
Point3F point;
U32 mask;
};
struct Poly {
PlaneF plane;
SceneObject* object;
U32 material;
U32 vertexStart;
U32 vertexCount;
U32 surfaceKey;
U32 polyFlags;
};
static bool allowClipping;
typedef Vector<PlaneF> PlaneList;
typedef Vector<Vertex> VertexList;
typedef Vector<Poly> PolyList;
typedef Vector<U32> IndexList;
// Internal data
PolyList mPolyList;
VertexList mVertexList;
IndexList mIndexList;
PlaneList mPolyPlaneList;
// Data set by caller
PlaneList mPlaneList;
VectorF mNormal;
//
ClippedPolyList();
~ClippedPolyList();
void clear();
void render();
// Virtual methods
bool isEmpty() const;
U32 addPoint(const Point3F& p);
U32 addPlane(const PlaneF& plane);
void begin(U32 material,U32 surfaceKey);
void plane(U32 v1,U32 v2,U32 v3);
void plane(const PlaneF& p);
void plane(const U32 index);
void vertex(U32 vi);
void end();
protected:
const PlaneF& getIndexedPlane(const U32 index);
};
#endif

67
engine/collision/collision.h Executable file
View File

@ -0,0 +1,67 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _COLLISION_H_
#define _COLLISION_H_
#ifndef _DATACHUNKER_H_
#include "core/dataChunker.h"
#endif
#ifndef _MPLANE_H_
#include "math/mPlane.h"
#endif
class SceneObject;
//----------------------------------------------------------------------------
struct Collision
{
SceneObject* object;
Point3F point;
VectorF normal;
U32 material;
// Face and Face dot are currently only set by the extrudedPolyList
// clipper. Values are otherwise undefined.
U32 face; // Which face was hit
F32 faceDot; // -Dot of face with poly normal
F32 distance;
Collision() { material = 0; }
};
struct CollisionList
{
enum {
MaxCollisions = 64
};
int count;
Collision collision[MaxCollisions];
F32 t;
// MaxHeight is currently only set by the extrudedPolyList
// clipper. It represents the maximum vertex z value of
// the returned collision surfaces.
F32 maxHeight;
};
//----------------------------------------------------------------------------
// BSP Collision tree
// Solid nodes are represented by structures with NULL frontNode and
// backNode pointers. The material field is only valid on a solid node.
// There is no structure for empty nodes, frontNode or backNode
// should be set to NULL to represent empty half-spaces.
struct BSPNode
{
U32 material;
PlaneF plane;
BSPNode *frontNode, *backNode;
};
typedef Chunker<BSPNode> BSPTree;
#endif

View File

@ -0,0 +1,149 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "dgl/dgl.h"
#include "math/mMath.h"
#include "console/console.h"
#include "collision/concretePolyList.h"
//----------------------------------------------------------------------------
ConcretePolyList::ConcretePolyList()
{
VECTOR_SET_ASSOCIATION(mPolyList);
VECTOR_SET_ASSOCIATION(mVertexList);
VECTOR_SET_ASSOCIATION(mIndexList);
VECTOR_SET_ASSOCIATION(mPolyPlaneList);
mIndexList.reserve(100);
}
ConcretePolyList::~ConcretePolyList()
{
}
//----------------------------------------------------------------------------
void ConcretePolyList::clear()
{
// Only clears internal data
mPolyList.clear();
mVertexList.clear();
mIndexList.clear();
mPolyPlaneList.clear();
}
//----------------------------------------------------------------------------
U32 ConcretePolyList::addPoint(const Point3F& p)
{
mVertexList.increment();
Point3F& v = mVertexList.last();
v.x = p.x * mScale.x;
v.y = p.y * mScale.y;
v.z = p.z * mScale.z;
mMatrix.mulP(v);
return mVertexList.size() - 1;
}
U32 ConcretePolyList::addPlane(const PlaneF& plane)
{
mPolyPlaneList.increment();
mPlaneTransformer.transform(plane, mPolyPlaneList.last());
return mPolyPlaneList.size() - 1;
}
//----------------------------------------------------------------------------
void ConcretePolyList::begin(U32 material,U32 surfaceKey)
{
mPolyList.increment();
Poly& poly = mPolyList.last();
poly.object = mCurrObject;
poly.material = material;
poly.vertexStart = mIndexList.size();
poly.surfaceKey = surfaceKey;
}
//----------------------------------------------------------------------------
void ConcretePolyList::plane(U32 v1,U32 v2,U32 v3)
{
mPolyList.last().plane.set(mVertexList[v1],
mVertexList[v2],mVertexList[v3]);
}
void ConcretePolyList::plane(const PlaneF& p)
{
mPlaneTransformer.transform(p, mPolyList.last().plane);
}
void ConcretePolyList::plane(const U32 index)
{
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
mPolyList.last().plane = mPolyPlaneList[index];
}
const PlaneF& ConcretePolyList::getIndexedPlane(const U32 index)
{
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
return mPolyPlaneList[index];
}
//----------------------------------------------------------------------------
void ConcretePolyList::vertex(U32 vi)
{
mIndexList.push_back(vi);
}
//----------------------------------------------------------------------------
bool ConcretePolyList::isEmpty() const
{
return false;
}
void ConcretePolyList::end()
{
Poly& poly = mPolyList.last();
poly.vertexCount = mIndexList.size() - poly.vertexStart;
}
void ConcretePolyList::render()
{
glVertexPointer(3,GL_FLOAT,sizeof(Point3F),mVertexList.address());
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(1,0,0,0.25);
glEnable(GL_BLEND);
glDisable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
Poly * p;
for (p = mPolyList.begin(); p < mPolyList.end(); p++) {
if(p->material != 0xFFFFFFFF)
glDrawElements(GL_POLYGON,p->vertexCount,
GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
}
glColor3f(0.6,0.6,0.6);
glDisable(GL_BLEND);
for (p = mPolyList.begin(); p < mPolyList.end(); p++) {
glDrawElements(GL_LINE_LOOP,p->vertexCount,
GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
}
glDisableClientState(GL_VERTEX_ARRAY);
}

View File

@ -0,0 +1,66 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _CONCRETEPOLYLIST_H_
#define _CONCRETEPOLYLIST_H_
#ifndef _ABSTRACTPOLYLIST_H_
#include "collision/abstractPolyList.h"
#endif
/// A concrete, renderable PolyList
///
/// This class is used to store geometry from a PolyList query.
///
/// It allows you to render this data, as well.
///
/// @see AbstractPolyList
class ConcretePolyList : public AbstractPolyList
{
public:
struct Poly {
PlaneF plane;
SceneObject* object;
U32 material;
U32 vertexStart;
U32 vertexCount;
U32 surfaceKey;
};
typedef Vector<PlaneF> PlaneList;
typedef Vector<Point3F> VertexList;
typedef Vector<Poly> PolyList;
typedef Vector<U32> IndexList;
PolyList mPolyList;
VertexList mVertexList;
IndexList mIndexList;
PlaneList mPolyPlaneList;
public:
ConcretePolyList();
~ConcretePolyList();
void clear();
// Virtual methods
U32 addPoint(const Point3F& p);
U32 addPlane(const PlaneF& plane);
void begin(U32 material,U32 surfaceKey);
void plane(U32 v1,U32 v2,U32 v3);
void plane(const PlaneF& p);
void plane(const U32 index);
void vertex(U32 vi);
void end();
void render();
bool isEmpty() const;
protected:
const PlaneF& getIndexedPlane(const U32 index);
};
#endif // _H_EARLYOUTPOLYLIST_

644
engine/collision/convex.cc Executable file
View File

@ -0,0 +1,644 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "dgl/dgl.h"
#include "core/dataChunker.h"
#include "collision/collision.h"
#include "sceneGraph/sceneGraph.h"
#include "sim/sceneObject.h"
#include "terrain/terrData.h"
#include "collision/convex.h"
#include "collision/gjk.h"
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
static DataChunker sChunker;
CollisionStateList CollisionStateList::sFreeList;
CollisionWorkingList CollisionWorkingList::sFreeList;
F32 sqrDistanceEdges(const Point3F& start0,
const Point3F& end0,
const Point3F& start1,
const Point3F& end1,
Point3F* is,
Point3F* it);
//----------------------------------------------------------------------------
// Collision State
//----------------------------------------------------------------------------
CollisionState::CollisionState()
{
mLista = mListb = 0;
}
CollisionState::~CollisionState()
{
if (mLista)
mLista->free();
if (mListb)
mListb->free();
}
void CollisionState::swap()
{
}
void CollisionState::set(Convex* a,Convex* b,const MatrixF& a2w, const MatrixF& b2w)
{
}
F32 CollisionState::distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist,
const MatrixF* w2a, const MatrixF* _w2b)
{
return 0;
}
void CollisionState::render()
{
}
//----------------------------------------------------------------------------
// Feature Collision
//----------------------------------------------------------------------------
bool ConvexFeature::collide(ConvexFeature& cf,CollisionList* cList, F32 tol)
{
// Our vertices vs. other faces
const Point3F* vert = mVertexList.begin();
const Point3F* vend = mVertexList.end();
while (vert != vend) {
cf.testVertex(*vert,cList,false, tol);
vert++;
}
// Other vertices vs. our faces
vert = cf.mVertexList.begin();
vend = cf.mVertexList.end();
while (vert != vend) {
U32 storeCount = cList->count;
testVertex(*vert,cList,true, tol);
// Fix up last reference. material and object are copied from this rather
// than the object we're colliding against.
if (storeCount != cList->count) {
cList->collision[cList->count - 1].material = cf.material;
cList->collision[cList->count - 1].object = cf.object;
}
vert++;
}
// Edge vs. Edge
const Edge* edge = mEdgeList.begin();
const Edge* eend = mEdgeList.end();
while (edge != eend) {
cf.testEdge(this,mVertexList[edge->vertex[0]],
mVertexList[edge->vertex[1]],cList, tol);
edge++;
}
return true;
}
inline bool isInside(const Point3F& p, const Point3F& a, const Point3F& b, const VectorF& n)
{
VectorF v;
mCross(n,b - a,&v);
return mDot(v,p - a) < 0.0f;
}
void ConvexFeature::testVertex(const Point3F& v,CollisionList* cList,bool flip, F32 tol)
{
// Test vertex against all faces
const Face* face = mFaceList.begin();
const Face* end = mFaceList.end();
for (; face != end; face++) {
if (cList->count >= CollisionList::MaxCollisions)
return;
const Point3F& p0 = mVertexList[face->vertex[0]];
const Point3F& p1 = mVertexList[face->vertex[1]];
const Point3F& p2 = mVertexList[face->vertex[2]];
// Point near the plane?
F32 distance = mDot(face->normal,v - p0);
if (distance > tol || distance < -tol)
continue;
// Make sure it's within the bounding edges
if (isInside(v,p0,p1,face->normal) && isInside(v,p1,p2,face->normal) &&
isInside(v,p2,p0,face->normal)) {
// Add collision to this face
Collision& info = cList->collision[cList->count++];
info.point = v;
info.normal = face->normal;
if (flip)
info.normal.neg();
info.material = material;
info.object = object;
info.distance = distance;
}
}
}
void ConvexFeature::testEdge(ConvexFeature* cf,const Point3F& s1, const Point3F& e1, CollisionList* cList, F32 tol)
{
F32 tolSquared = tol*tol;
// Test edges against edges
const Edge* edge = mEdgeList.begin();
const Edge* end = mEdgeList.end();
for (; edge != end; edge++) {
if (cList->count >= CollisionList::MaxCollisions)
return;
const Point3F& s2 = mVertexList[edge->vertex[0]];
const Point3F& e2 = mVertexList[edge->vertex[1]];
// Get the distance and closest points
Point3F i1,i2;
F32 distance = sqrDistanceEdges(s1, e1, s2, e2, &i1, &i2);
if (distance > tolSquared)
continue;
distance = mSqrt(distance);
// Need to figure out how to orient the collision normal.
// The current test involves checking to see whether the collision
// points are contained within the convex volumes, which is slow.
if (inVolume(i1) || cf->inVolume(i2))
distance = -distance;
// Contact normal
VectorF normal = i1 - i2;
if ( mIsZero( distance ) )
normal.zero();
else
normal *= 1 / distance;
// Return a collision
Collision& info = cList->collision[cList->count++];
info.point = i1;
info.normal = normal;
info.distance = distance;
info.material = material;
info.object = object;
}
}
bool ConvexFeature::inVolume(const Point3F& v)
{
// Test the point to see if it's inside the volume
const Face* face = mFaceList.begin();
const Face* end = mFaceList.end();
for (; face != end; face++) {
const Point3F& p0 = mVertexList[face->vertex[0]];
if (mDot(face->normal,v - p0) > 0)
return false;
}
return true;
}
//----------------------------------------------------------------------------
// Collision State management
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
CollisionStateList::CollisionStateList()
{
mPrev = mNext = this;
mState = NULL;
}
void CollisionStateList::linkAfter(CollisionStateList* ptr)
{
mPrev = ptr;
mNext = ptr->mNext;
ptr->mNext = this;
mNext->mPrev = this;
}
void CollisionStateList::unlink()
{
mPrev->mNext = mNext;
mNext->mPrev = mPrev;
mPrev = mNext = this;
}
CollisionStateList* CollisionStateList::alloc()
{
if (!sFreeList.isEmpty()) {
CollisionStateList* nxt = sFreeList.mNext;
nxt->unlink();
nxt->mState = NULL;
return nxt;
}
return constructInPlace((CollisionStateList*)sChunker.alloc(sizeof(CollisionStateList)));
}
void CollisionStateList::free()
{
unlink();
linkAfter(&sFreeList);
}
//----------------------------------------------------------------------------
CollisionWorkingList::CollisionWorkingList()
{
wLink.mPrev = wLink.mNext = this;
rLink.mPrev = rLink.mNext = this;
}
void CollisionWorkingList::wLinkAfter(CollisionWorkingList* ptr)
{
wLink.mPrev = ptr;
wLink.mNext = ptr->wLink.mNext;
ptr->wLink.mNext = this;
wLink.mNext->wLink.mPrev = this;
}
void CollisionWorkingList::rLinkAfter(CollisionWorkingList* ptr)
{
rLink.mPrev = ptr;
rLink.mNext = ptr->rLink.mNext;
ptr->rLink.mNext = this;
rLink.mNext->rLink.mPrev = this;
}
void CollisionWorkingList::unlink()
{
wLink.mPrev->wLink.mNext = wLink.mNext;
wLink.mNext->wLink.mPrev = wLink.mPrev;
wLink.mPrev = wLink.mNext = this;
rLink.mPrev->rLink.mNext = rLink.mNext;
rLink.mNext->rLink.mPrev = rLink.mPrev;
rLink.mPrev = rLink.mNext = this;
}
CollisionWorkingList* CollisionWorkingList::alloc()
{
if (sFreeList.wLink.mNext != &sFreeList) {
CollisionWorkingList* nxt = sFreeList.wLink.mNext;
nxt->unlink();
return nxt;
}
return constructInPlace((CollisionWorkingList*)sChunker.alloc(sizeof(CollisionWorkingList)));
}
void CollisionWorkingList::free()
{
unlink();
wLinkAfter(&sFreeList);
}
//----------------------------------------------------------------------------
// Convex Base Class
//----------------------------------------------------------------------------
U32 Convex::sTag = (U32)-1;
//----------------------------------------------------------------------------
Convex::Convex()
{
mNext = mPrev = this;
mTag = 0;
}
Convex::~Convex()
{
// Unlink from Convex Database
unlink();
// Delete collision states
while (mList.mNext != &mList)
delete mList.mNext->mState;
// Free up working list
while (mWorking.wLink.mNext != &mWorking)
mWorking.wLink.mNext->free();
// Free up references
while (mReference.rLink.mNext != &mReference)
mReference.rLink.mNext->free();
}
//----------------------------------------------------------------------------
void Convex::collectGarbage()
{
// Delete unreferenced Convex Objects
for (Convex* itr = mNext; itr != this; itr = itr->mNext) {
if (itr->mReference.rLink.mNext == &itr->mReference) {
Convex* ptr = itr;
itr = itr->mPrev;
delete ptr;
}
}
}
void Convex::nukeList()
{
// Delete all Convex Objects
for (Convex* itr = mNext; itr != this; itr = itr->mNext) {
Convex* ptr = itr;
itr = itr->mPrev;
delete ptr;
}
}
void Convex::registerObject(Convex *convex)
{
convex->linkAfter(this);
}
//----------------------------------------------------------------------------
void Convex::linkAfter(Convex* ptr)
{
mPrev = ptr;
mNext = ptr->mNext;
ptr->mNext = this;
mNext->mPrev = this;
}
void Convex::unlink()
{
mPrev->mNext = mNext;
mNext->mPrev = mPrev;
mPrev = mNext = this;
}
//----------------------------------------------------------------------------
Point3F Convex::support(const VectorF&) const
{
return Point3F(0,0,0);
}
void Convex::getFeatures(const MatrixF&,const VectorF&,ConvexFeature* f)
{
f->object = NULL;
}
const MatrixF& Convex::getTransform() const
{
return mObject->getTransform();
}
const Point3F& Convex::getScale() const
{
return mObject->getScale();
}
Box3F Convex::getBoundingBox() const
{
return mObject->getWorldBox();
}
Box3F Convex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const
{
Box3F wBox = mObject->getObjBox();
wBox.min.convolve(scale);
wBox.max.convolve(scale);
mat.mul(wBox);
return wBox;
}
void Convex::render()
{
for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext)
if (itr->mState->mLista == itr)
itr->mState->render();
}
//----------------------------------------------------------------------------
void Convex::addToWorkingList(Convex* ptr)
{
CollisionWorkingList* cl = CollisionWorkingList::alloc();
cl->wLinkAfter(&mWorking);
cl->rLinkAfter(&ptr->mReference);
cl->mConvex = ptr;
};
//----------------------------------------------------------------------------
void Convex::updateWorkingList(const Box3F& box, const U32 colMask)
{
sTag++;
// Clear objects off the working list that are no longer intersecting
for (CollisionWorkingList* itr = mWorking.wLink.mNext; itr != &mWorking; itr = itr->wLink.mNext) {
itr->mConvex->mTag = sTag;
if ((!box.isOverlapped(itr->mConvex->getBoundingBox())) || (!itr->mConvex->getObject()->isCollisionEnabled())) {
CollisionWorkingList* cl = itr;
itr = itr->wLink.mPrev;
cl->free();
}
}
// Special processing for the terrain and interiors...
AssertFatal(mObject->getContainer(), "Must be in a container!");
SimpleQueryList sql;
mObject->getContainer()->findObjects(box, colMask,SimpleQueryList::insertionCallback, &sql);
for (U32 i = 0; i < sql.mList.size(); i++)
sql.mList[i]->buildConvex(box, this);
}
// ---------------------------------------------------------------------------
void Convex::updateStateList(const MatrixF& mat, const Point3F& scale, const Point3F* displacement)
{
Box3F box1 = getBoundingBox(mat, scale);
box1.min -= Point3F(1, 1, 1);
box1.max += Point3F(1, 1, 1);
if (displacement) {
Point3F oldMin = box1.min;
Point3F oldMax = box1.max;
box1.min.setMin(oldMin + *displacement);
box1.min.setMin(oldMax + *displacement);
box1.max.setMax(oldMin + *displacement);
box1.max.setMax(oldMax + *displacement);
}
sTag++;
// Destroy states which are no longer intersecting
for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext) {
Convex* cv = (itr->mState->a == this)? itr->mState->b: itr->mState->a;
cv->mTag = sTag;
if (!box1.isOverlapped(cv->getBoundingBox())) {
CollisionState* cs = itr->mState;
itr = itr->mPrev;
delete cs;
}
}
// Add collision states for new overlapping objects
for (CollisionWorkingList* itr0 = mWorking.wLink.mNext; itr0 != &mWorking; itr0 = itr0->wLink.mNext) {
register Convex* cv = itr0->mConvex;
if (cv->mTag != sTag && box1.isOverlapped(cv->getBoundingBox())) {
CollisionState* state = new GjkCollisionState;
state->set(this,cv,mat,cv->getTransform());
state->mLista->linkAfter(&mList);
state->mListb->linkAfter(&cv->mList);
}
}
}
//----------------------------------------------------------------------------
CollisionState* Convex::findClosestState(const MatrixF& mat, const Point3F& scale, const F32 dontCareDist)
{
updateStateList(mat, scale);
F32 dist = +1E30;
CollisionState *st = 0;
// Prepare scaled version of transform
MatrixF axform = mat;
axform.scale(scale);
MatrixF axforminv(true);
MatrixF temp(mat);
axforminv.scale(Point3F(1.0f/scale.x,1.0f/scale.y,1.0f/scale.z));
temp.affineInverse();
axforminv.mul(temp);
for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext) {
CollisionState* state = itr->mState;
if (state->mLista != itr)
state->swap();
// Prepare scaled version of transform
MatrixF bxform = state->b->getTransform();
temp = bxform;
Point3F bscale = state->b->getScale();
bxform.scale(bscale);
MatrixF bxforminv(true);
bxforminv.scale(Point3F(1.0f/bscale.x,1.0f/bscale.y,1.0f/bscale.z));
temp.affineInverse();
bxforminv.mul(temp);
//
F32 dd = state->distance(axform, bxform, dontCareDist, &axforminv, &bxforminv);
if (dd < dist) {
dist = dd;
st = state;
}
}
if (dist < dontCareDist)
return st;
else
return NULL;
}
//----------------------------------------------------------------------------
bool Convex::getCollisionInfo(const MatrixF& mat, const Point3F& scale, CollisionList* cList,F32 tol)
{
for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext) {
CollisionState* state = itr->mState;
if (state->mLista != itr)
state->swap();
if (state->dist <= tol) {
ConvexFeature fa,fb;
VectorF v;
// The idea is that we need to scale the matrix, so we need to
// make a copy of it, before we can pass it in to getFeatures.
// This is used to scale us for comparison against the other
// convex, which is correctly scaled.
MatrixF omat = mat;
omat.scale(scale);
MatrixF imat = omat;
imat.inverse();
imat.mulV(-state->v,&v);
getFeatures(omat,v,&fa);
imat = state->b->getTransform();
imat.scale(state->b->getScale());
MatrixF bxform = imat;
imat.inverse();
imat.mulV(state->v,&v);
state->b->getFeatures(bxform,v,&fb);
fa.collide(fb,cList,tol);
}
}
return (cList->count != 0);
}
void Convex::getPolyList(AbstractPolyList*)
{
}
//-----------------------------------------------------------------------------
// This function based on code orignally written for the book:
// 3D Game Engine Design, by David H. Eberly
//
F32 sqrDistanceEdges(const Point3F& start0, const Point3F& end0,
const Point3F& start1, const Point3F& end1,
Point3F* is, Point3F* it)
{
Point3F direction0 = end0 - start0;
F32 fA00 = direction0.lenSquared();
Point3F direction1 = end1 - start1;
F32 fA11 = direction1.lenSquared();
F32 fA01 = -mDot(direction0, direction1);
Point3F kDiff = start0 - start1;
F32 fC = kDiff.lenSquared();
F32 fB0 = mDot(kDiff, direction0);
F32 fDet = mAbs((S32)(fA00*fA11 - fA01*fA01));
// Since the endpoints are tested as vertices, we're not interested
// in parallel lines, and intersections that don't involve end-points.
if (fDet >= 0.00001) {
// Calculate time of intersection for each line
F32 fB1 = -mDot(kDiff, direction1);
F32 fS = fA01*fB1-fA11*fB0;
F32 fT = fA01*fB0-fA00*fB1;
// Only interested in collisions that don't involve the end points
if (fS >= 0.0 && fS <= fDet && fT >= 0.0 && fT <= fDet) {
F32 fInvDet = 1.0 / fDet;
fS *= fInvDet;
fT *= fInvDet;
F32 fSqrDist = (fS*(fA00*fS + fA01*fT + 2.0*fB0) +
fT*(fA01*fS + fA11*fT + 2.0*fB1) + fC);
// Intersection points.
*is = start0 + direction0 * fS;
*it = start1 + direction1 * fT;
return mFabs(fSqrDist);
}
}
// Return a large number in the cases where endpoints are involved.
return 1e10f;
}

249
engine/collision/convex.h Executable file
View File

@ -0,0 +1,249 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _CONVEX_H_
#define _CONVEX_H_
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
struct Collision;
struct CollisionList;
struct CollisionStateList;
class AbstractPolyList;
class SceneObject;
class Convex;
//----------------------------------------------------------------------------
class ConvexFeature
{
public:
struct Edge {
S32 vertex[2];
};
struct Face {
VectorF normal;
S32 vertex[3];
};
Vector<Point3F> mVertexList;
Vector<Edge> mEdgeList;
Vector<Face> mFaceList;
S32 material;
SceneObject* object;
ConvexFeature() : mVertexList(64), mEdgeList(128), mFaceList(64) {
VECTOR_SET_ASSOCIATION(mVertexList);
VECTOR_SET_ASSOCIATION(mEdgeList);
VECTOR_SET_ASSOCIATION(mFaceList);
}
bool collide(ConvexFeature& cf,CollisionList* cList, F32 tol = 0.1);
void testVertex(const Point3F& v,CollisionList* cList,bool,F32 tol);
void testEdge(ConvexFeature* cf,const Point3F& s1, const Point3F& e1, CollisionList* cList, F32 tol);
bool inVolume(const Point3F& v);
};
//----------------------------------------------------------------------------
enum ConvexType {
TSConvexType,
BoxConvexType,
TerrainConvexType,
InteriorConvexType,
ShapeBaseConvexType,
TSStaticConvexType,
InteriorMapConvexType
};
//----------------------------------------------------------------------------
struct CollisionState
{
CollisionStateList* mLista;
CollisionStateList* mListb;
Convex* a;
Convex* b;
F32 dist; // Current estimated distance
VectorF v; // Vector between closest points
//
CollisionState();
virtual ~CollisionState();
virtual void swap();
virtual void set(Convex* a,Convex* b,const MatrixF& a2w, const MatrixF& b2w);
virtual F32 distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist,
const MatrixF* w2a = NULL, const MatrixF* _w2b = NULL);
void render();
};
//----------------------------------------------------------------------------
struct CollisionStateList
{
static CollisionStateList sFreeList;
CollisionStateList* mNext;
CollisionStateList* mPrev;
CollisionState* mState;
CollisionStateList();
void linkAfter(CollisionStateList* next);
void unlink();
bool isEmpty() { return mNext == this; }
static CollisionStateList* alloc();
void free();
};
//----------------------------------------------------------------------------
struct CollisionWorkingList
{
static CollisionWorkingList sFreeList;
struct WLink {
CollisionWorkingList* mNext;
CollisionWorkingList* mPrev;
} wLink;
struct RLink {
CollisionWorkingList* mNext;
CollisionWorkingList* mPrev;
} rLink;
Convex* mConvex;
void wLinkAfter(CollisionWorkingList* next);
void rLinkAfter(CollisionWorkingList* next);
void unlink();
CollisionWorkingList();
static CollisionWorkingList* alloc();
void free();
};
//----------------------------------------------------------------------------
class Convex {
/// @name Linked list managent
/// @{
Convex* mNext; ///< Next item in linked list of Convex's
Convex* mPrev; ///< Previous item in linked list of Convex's
/// Insert this Convex after the provided convex
/// @param next
void linkAfter(Convex* next);
/// Remove this Convex from the linked list
void unlink();
/// @}
U32 mTag;
static U32 sTag;
protected:
CollisionStateList mList; ///< Objects we're testing against
CollisionWorkingList mWorking; ///< Objects within our bounds
CollisionWorkingList mReference; ///< Other convex testing against us
SceneObject* mObject; ///< Object this Convex is built around
ConvexType mType; ///< Type of Convex this is @see ConvexType
//
public:
/// Constructor
Convex();
/// Destructor
virtual ~Convex();
/// Registers another Convex by linking it after this one
void registerObject(Convex *convex);
/// Runs through the linked list of Convex objects and removes the ones
/// with no references
void collectGarbage();
/// Deletes all convex objects in the list
void nukeList();
/// Returns the type of this Convex
ConvexType getType() { return mType; }
/// Returns the object this Convex is built from
SceneObject* getObject() { return mObject; }
/// Traverses mList and renders all collision states
void render();
/// Adds the provided Convex to the list of objects within the bounds of this Convex
/// @param ptr Convex to add to the working list of this object
void addToWorkingList(Convex* ptr);
/// Returns the list of objects currently inside the bounds of this Convex
CollisionWorkingList& getWorkingList() { return mWorking; }
/// Finds the closest
CollisionState* findClosestState(const MatrixF& mat, const Point3F& scale, const F32 dontCareDist = 1);
/// Returns the list of objects this object is testing against
CollisionStateList* getStateList() { return mList.mNext; }
/// Updates the CollisionStateList (mList) with new collision states and removing
/// ones no longer under consideration
/// @param mat Used as the matrix to create a bounding box for updating the list
/// @param scale Used to scale the bounding box
/// @param displacement Bounding box displacement (optional)
void updateStateList(const MatrixF& mat, const Point3F& scale, const Point3F* displacement = NULL);
/// Updates the working collision list of objects which are currently colliding with
/// (inside the bounds of) this Convex.
///
/// @param box Used as the bounding box.
/// @param colMask Mask of objects to check against.
void updateWorkingList(const Box3F& box, const U32 colMask);
/// Returns the transform of the object this is built around
virtual const MatrixF& getTransform() const;
/// Returns the scale of the object this is built around
virtual const Point3F& getScale() const;
/// Returns the bounding box for the object this is built around in world space
virtual Box3F getBoundingBox() const;
/// Returns the object space bounding box for the object this is built around
/// transformed and scaled
/// @param mat Matrix to transform the object-space box by
/// @param scale Scaling factor to scale the bounding box by
virtual Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const;
/// Returns the farthest point, along a vector, still bound by the convex
/// @param v Vector
virtual Point3F support(const VectorF& v) const;
///
virtual void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf);
/// Builds a collision poly list out of this convex
/// @param list (Out) Poly list built
virtual void getPolyList(AbstractPolyList* list);
///
bool getCollisionInfo(const MatrixF& mat, const Point3F& scale, CollisionList* cList,F32 tol);
};
#endif

1290
engine/collision/convexBrush.cc Executable file

File diff suppressed because it is too large Load Diff

127
engine/collision/convexBrush.h Executable file
View File

@ -0,0 +1,127 @@
#ifndef _CONVEXBRUSH_H_
#define _CONVEXBRUSH_H_
#include "platform/platform.h"
#include "core/tVector.h"
#include "math/mPoint.h"
#include "math/mBox.h"
#include "math/mPlane.h"
#include "collision/abstractPolyList.h"
#include "collision/optimizedPolyList.h"
#include "sim/sceneObject.h"
#include "interior/interiorMapRes.h"
#define WINDINGSCALE 10000
class ConvexBrush
{
public:
// Constructor
ConvexBrush();
// Destructor
~ConvexBrush();
enum
{
Front = 0,
Back = 1,
On = 2,
Split = 3,
Unknown = 4
};
enum
{
Unprocessed = 0,
Good,
BadWinding,
ShortPlanes,
BadFaces,
Malformed,
Concave,
Deleted
};
// Supporting structures
struct TexInfo
{
StringTableEntry texture;
PlaneF texGens[2];
F32 scale[2];
F32 rot;
F32 texDiv[2];
};
// The brushes owner entity
InteriorMapResource::Entity* mOwner;
// Bounding box
Box3F mBounds;
Point3F mCentroid;
MatrixF mTransform;
Point3F mScale;
QuatF mRotation;
// lighting info...
MatrixF mLightingTransform;
// Some useful values
F32 mBrushScale;
S32 mID;
U32 mType;
U32 mStatus;
U32 mPrevStatus;
bool mSelected;
StringTableEntry mDebugInfo;
// Scratch buffer storage
MatrixF mTransformScratch;
QuatF mRotationScratch;
// Data
OptimizedPolyList mFaces;
Vector<TexInfo> mTexInfos;
// Setup functions
bool addPlane(PlaneF pln);
bool addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], char* texture);
bool addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], char* texture, U32 matIdx);
bool addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], U32 matIdx);
bool processBrush();
bool setScale(F32 scale);
// Utility functions
bool selfClip();
bool intersectPlanes(const PlaneF& plane1, const PlaneF& plane2, const PlaneF& plane3, Point3D* pOutput);
bool createWinding(const Vector<U32>& rPoints, const Point3F& normal, Vector<U32>& pWinding);
void addEdge(U16 zero, U16 one, U32 face);
bool generateEdgelist();
void validateWindings();
bool validateEdges();
void addIndex(Vector<U32>& indices, U32 index);
void calcCentroid();
void setupTransform();
void resetBrush();
U32 getPolySide(S32 side);
U32 whichSide(PlaneF pln);
U32 whichSide(PlaneF pln, U32 faceIndex);
bool isInsideBox(PlaneF left, PlaneF right, PlaneF top, PlaneF bottom);
bool splitBrush(PlaneF pln, ConvexBrush* fbrush, ConvexBrush* bbrush);
bool calcBounds();
bool castRay(const Point3F& s, const Point3F& e, RayInfo* info);
OptimizedPolyList getIntersectingPolys(OptimizedPolyList* list);
OptimizedPolyList getNonIntersectingPolys(OptimizedPolyList* list);
bool isPolyInside(OptimizedPolyList* list, U32 pdx);
bool getPolyList(AbstractPolyList* list);
// Debug render functions
bool render(bool genColors);
bool renderFace(U32 face, bool renderLighting);
bool renderEdges(ColorF color);
};
#endif

917
engine/collision/depthSortList.cc Executable file
View File

@ -0,0 +1,917 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "dgl/dgl.h"
#include "math/mMath.h"
#include "console/console.h"
#include "collision/depthSortList.h"
#include "core/color.h"
#include "core/fileStream.h" // TODO, remove this
//----------------------------------------------------------------------------
// some defines and global parameters that affect poly split routine
F32 SPLIT_TOL = 0.0005f;
bool ALWAYS_RETURN_FRONT_AND_BACK = false; // if false, split routine will return polys only if a split occurs
// more global parameters
F32 XZ_TOL = 0.0f;
F32 DEPTH_TOL = 0.01f;
#define MIN_Y_DOT 0.05f
DepthSortList * gCurrentSort = NULL;
S32 gForceOverlap = -1; // force an overlap test to result in an overlap
S32 gNoOverlapCount;
S32 gBadSpots = 0;
// if polys are correctly sorted then writing depth values should result in no
// overlap of polys when looking down from camera...otoh, if polys are out of
// order, we should see overlap
bool DepthSortList::renderWithDepth = false;
//----------------------------------------------------------------------------
// following copied from shapeBase.cc because I didn't want to reference
// something from the test program in the core. This should really be a
// stand-alone function, but...
static ColorF cubeColors[8] = {
ColorF(0, 0, 0), ColorF(1, 0, 0), ColorF(0, 1, 0), ColorF(0, 0, 1),
ColorF(1, 1, 0), ColorF(1, 0, 1), ColorF(0, 1, 1), ColorF(1, 1, 1)
};
static Point3F cubePoints[8] = {
Point3F(-1, -1, -1), Point3F(-1, -1, 1), Point3F(-1, 1, -1), Point3F(-1, 1, 1),
Point3F( 1, -1, -1), Point3F( 1, -1, 1), Point3F( 1, 1, -1), Point3F( 1, 1, 1)
};
static U32 cubeFaces[6][4] = {
{ 0, 2, 6, 4 }, { 0, 2, 3, 1 }, { 0, 1, 5, 4 },
{ 3, 2, 6, 7 }, { 7, 6, 4, 5 }, { 3, 7, 5, 1 }
};
void DepthSortList::wireCube(const Point3F& size, const Point3F& pos)
{
glDisable(GL_CULL_FACE);
for(int i = 0; i < 6; i++) {
glBegin(GL_LINE_LOOP);
for(int vert = 0; vert < 4; vert++) {
int idx = cubeFaces[i][vert];
glColor3f(cubeColors[idx].red, cubeColors[idx].green, cubeColors[idx].blue);
glVertex3f(cubePoints[idx].x * size.x + pos.x, cubePoints[idx].y * size.y + pos.y, cubePoints[idx].z * size.z + pos.z);
}
glEnd();
}
}
//----------------------------------------------------------------------------
DepthSortList::DepthSortList()
{
VECTOR_SET_ASSOCIATION(mPolyExtentsList);
VECTOR_SET_ASSOCIATION(mPolyIndexList);
}
DepthSortList::~DepthSortList()
{
}
//----------------------------------------------------------------------------
void DepthSortList::clear()
{
Parent::clear();
mPolyExtentsList.clear();
mPolyIndexList.clear();
clearSort();
}
void DepthSortList::clearSort()
{
mBase = -1;
mMaxTouched = 0;
gNoOverlapCount = 0;
}
//----------------------------------------------------------------------------
void DepthSortList::end()
{
S32 numPoly = mPolyList.size();
if (mPolyList.last().plane.y >= -MIN_Y_DOT)
{
mIndexList.setSize(mPolyList.last().vertexStart);
mPolyList.decrement();
return;
}
Parent::end();
// we deleted this poly, be sure not to add anything more...
if (mPolyList.size()!=numPoly)
return;
AssertFatal(mPolyList.last().vertexCount>2,"DepthSortList::end: only two vertices in poly");
mPolyExtentsList.increment();
setExtents(mPolyList.last(),mPolyExtentsList.last());
mPolyIndexList.push_back(numPoly-1);
}
//----------------------------------------------------------------------------
bool DepthSortList::getMapping(MatrixF * mat, Box3F * box)
{
// return list transform and bounds in list space...optional
*mat = mMatrix;
mat->inverse();
box->min.set(-mExtent.x, 0.0f, -mExtent.z);
box->max.set( mExtent.x, 2.0f * mExtent.y, mExtent.z);
return true;
}
//----------------------------------------------------------------------------
void DepthSortList::render()
{
glPushMatrix();
glPushAttrib(GL_DEPTH_BUFFER_BIT);
MatrixF mat = mBaseMatrix;
mat.inverse();
dglMultMatrix(&mat);
glVertexPointer(3,GL_FLOAT,sizeof(Vertex),mVertexList.address());
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(1,0,0,0.25);
glEnable(GL_BLEND);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
if (renderWithDepth)
glDepthMask(GL_TRUE);
else
glDepthMask(GL_FALSE);
glDisable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
Poly * p;
S32 only = Con::getIntVariable("$only",-1);
for (S32 i=0; i<mPolyIndexList.size(); i++)
{
if (only>=0 && only!=i)
continue;
p = &mPolyList[mPolyIndexList[i]];
glDrawElements(GL_POLYGON,p->vertexCount,
GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
}
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
glDisableClientState(GL_VERTEX_ARRAY);
// draw outline around clip zone...
wireCube(mExtent,Point3F(0,mExtent.y*0.5f,0));
glPopMatrix();
glPopAttrib();
}
//----------------------------------------------------------------------------
void DepthSortList::setExtents(Poly & poly, PolyExtents & polyExtents)
{
Point3F p = mVertexList[mIndexList[poly.vertexStart]].point;
polyExtents.xMin = polyExtents.xMax = p.x;
polyExtents.yMin = polyExtents.yMax = p.y;
polyExtents.zMin = polyExtents.zMax = p.z;
for (S32 i=poly.vertexStart+1; i<poly.vertexStart+poly.vertexCount; i++)
{
Point3F p = mVertexList[mIndexList[i]].point;
// x
if (p.x < polyExtents.xMin)
polyExtents.xMin = p.x;
else if (p.x > polyExtents.xMax)
polyExtents.xMax = p.x;
// y
if (p.y < polyExtents.yMin)
polyExtents.yMin = p.y;
else if (p.y > polyExtents.yMax)
polyExtents.yMax = p.y;
// z
if (p.z < polyExtents.zMin)
polyExtents.zMin = p.z;
else if (p.z > polyExtents.zMax)
polyExtents.zMax = p.z;
}
}
//----------------------------------------------------------------------------
// function for comparing two poly indices
S32 FN_CDECL compareYExtents( const void* e1, const void* e2)
{
DepthSortList::PolyExtents & poly1 = gCurrentSort->getExtents(*(U32*)e1);
DepthSortList::PolyExtents & poly2 = gCurrentSort->getExtents(*(U32*)e2);
if (poly1.yMin < poly2.yMin)
return -1;
if (poly2.yMin < poly1.yMin)
return 1;
return 0;
}
//----------------------------------------------------------------------------
void DepthSortList::sortByYExtents()
{
gCurrentSort = this;
dQsort(mPolyIndexList.address(),mPolyIndexList.size(),sizeof(U32),compareYExtents);
}
//----------------------------------------------------------------------------
void DepthSortList::set(const MatrixF & mat, Point3F & extents)
{
setBaseTransform(mat);
mNormal.set(0,1,0); // ignore polys not facing up...
mExtent = extents;
mExtent *= 0.5f;
// set clip planes
mPlaneList.clear();
mPlaneList.increment();
mPlaneList.last().set(-1.0f, 0.0f, 0.0f);
mPlaneList.last().d = -mExtent.x;
mPlaneList.increment();
mPlaneList.last().set( 1.0f, 0.0f, 0.0f);
mPlaneList.last().d = -mExtent.x;
mPlaneList.increment();
mPlaneList.last().set( 0.0f,-1.0f, 0.0f);
mPlaneList.last().d = 0;
mPlaneList.increment();
mPlaneList.last().set( 0.0f, 1.0f, 0.0f);
mPlaneList.last().d = -2.0f * mExtent.y;
mPlaneList.increment();
mPlaneList.last().set( 0.0f, 0.0f,-1.0f);
mPlaneList.last().d = -mExtent.z;
mPlaneList.increment();
mPlaneList.last().set( 0.0f, 0.0f, 1.0f);
mPlaneList.last().d = -mExtent.z;
}
//----------------------------------------------------------------------------
void DepthSortList::setBase(S32 base)
{
mBase = base;
getOrderedPoly(mBase, &mBasePoly, &mBaseExtents);
mBaseNormal = &mBasePoly->plane;
mBaseDot = -mBasePoly->plane.d;
mBaseYMax = mBaseExtents->yMax;
}
//----------------------------------------------------------------------------
bool DepthSortList::sortNext()
{
// find the next poly in the depth order
// NOTE: a closer poly may occur before a farther away poly so long as
// they don't overlap in the x-z plane...
if (++mBase>=mPolyIndexList.size())
return false;
setBase(mBase);
gBadSpots = 0;
ALWAYS_RETURN_FRONT_AND_BACK = false; // split routine will return polys only if a split occurs
bool switched = false; // haven't switched base poly yet
S32 i = 0; // currently comparing base to base+i
Poly * testPoly;
PolyExtents * testExtents;
while (mBase+i+1<mPolyIndexList.size())
{
i++;
// get test poly...
getOrderedPoly(mBase+i,&testPoly,&testExtents);
Point3F & testNormal = testPoly->plane;
F32 testDot = -testPoly->plane.d;
// if base poly's y extents don't overlap test poly's, base poly can stay where it is...
if (mBase+i>mMaxTouched && mBaseYMax<=testExtents->yMin+DEPTH_TOL)
break;
// if base poly and test poly don't have overlapping x & z extents, then order doesn't matter...stay the same
if (mBaseExtents->xMin>=testExtents->xMax-XZ_TOL || mBaseExtents->xMax<=testExtents->xMin+XZ_TOL ||
mBaseExtents->zMin>=testExtents->zMax-XZ_TOL || mBaseExtents->zMax<=testExtents->zMin+XZ_TOL)
continue;
// is test poly completely behind base poly? if so, order is fine as it is
S32 v;
for (v=0; v<testPoly->vertexCount; v++)
if (mDot(mVertexList[mIndexList[testPoly->vertexStart+v]].point,*mBaseNormal)>mBaseDot+DEPTH_TOL)
break;
if (v==testPoly->vertexCount)
// test behind base
continue;
// is base poly completely in front of test poly? if so, order is fine as it is
for (v=0; v<mBasePoly->vertexCount; v++)
if (mDot(mVertexList[mIndexList[mBasePoly->vertexStart+v]].point,testNormal)<testDot-DEPTH_TOL)
break;
if (v==mBasePoly->vertexCount)
// base in front of test
continue;
// if the polys don't overlap in the x-z plane, then order doesn't matter, stay as we are
if (!overlap(mBasePoly,testPoly))
{
gNoOverlapCount++;
if (gNoOverlapCount!=gForceOverlap)
continue;
}
// handle switching/splitting of polys due to overlap
handleOverlap(testPoly,testNormal,testDot,i,switched);
}
return true;
}
//----------------------------------------------------------------------------
void DepthSortList::sort()
{
// depth sort mPolyIndexList -- entries are indices into mPolyList (where poly is found) & mPolyExtentsList
// sort by min y extent
sortByYExtents();
mBase = -1;
while (sortNext())
;
}
//----------------------------------------------------------------------------
void DepthSortList::handleOverlap(Poly * testPoly, Point3F & testNormal, F32 testDot, S32 & testOffset, bool & switched)
{
// first reverse the plane tests (i.e., test to see if basePoly behind testPoly or testPoly in front of basePoly...
// if either succeeds, switch poly
// if they both fail, split base poly
// But split anyway if basePoly has already been switched...
bool doSwitch = false;
if (!switched)
{
S32 v;
for (v=0; v<mBasePoly->vertexCount; v++)
if (mDot(mVertexList[mIndexList[mBasePoly->vertexStart+v]].point,testNormal)>testDot+DEPTH_TOL)
break;
if (v==mBasePoly->vertexCount)
doSwitch = true;
else
{
for (v=0; v<testPoly->vertexCount; v++)
if (mDot(mVertexList[mIndexList[testPoly->vertexStart+v]].point,*mBaseNormal)<mBaseDot-DEPTH_TOL)
break;
if (v==testPoly->vertexCount)
doSwitch = true;
}
}
// try to split base poly along plane of test poly
Poly frontPoly, backPoly;
bool splitBase = false, splitTest = false;
if (!doSwitch)
{
splitBase = splitPoly(*mBasePoly,testNormal,testDot,frontPoly,backPoly);
if (!splitBase)
// didn't take...no splitting happened...try splitting test poly by base poly
splitTest = splitPoly(*testPoly,*mBaseNormal,mBaseDot,frontPoly,backPoly);
}
U32 testIdx = mPolyIndexList[mBase+testOffset];
// should we switch order of test and base poly? Might have to even if we
// don't want to if there's no splitting to do...
// Note: possibility that infinite loop can be introduced here...if that happens,
// then we need to split along edges of polys
if (doSwitch || (!splitTest && !splitBase))
{
if (!doSwitch && gBadSpots++ > (mPolyIndexList.size()-mBase)<<1)
// got here one too many times...just leave and don't touch poly -- avoid infinite loop
return;
// move test poly to the front of the order
dMemmove(&mPolyIndexList[mBase+1],&mPolyIndexList[mBase],testOffset*sizeof(U32));
mPolyIndexList[mBase] = testIdx;
// base poly changed...
setBase(mBase);
if (mBase+testOffset>mMaxTouched)
mMaxTouched=mBase+testOffset;
testOffset=1; // don't need to compare against old base
switched=true;
return;
}
if (splitBase)
{
// need one more spot...frontPoly and backPoly replace basePoly
setExtents(frontPoly,mPolyExtentsList[mPolyIndexList[mBase]]);
mPolyExtentsList.increment();
setExtents(backPoly,mPolyExtentsList.last());
mPolyList[mPolyIndexList[mBase]] = frontPoly;
mPolyIndexList.insert(mBase+1);
mPolyIndexList[mBase+1] = mPolyList.size();
mPolyList.push_back(backPoly);
// new base poly...
setBase(mBase);
// increase tsetOffset & mMaxTouched because of insertion of back poly
testOffset++;
mMaxTouched++;
//
switched=false;
return;
}
// splitTest -- no other way to get here
AssertFatal(splitTest,"DepthSortList::handleOverlap: how did we get here like this?");
// put frontPoly in front of basePoly, leave backPoly where testPoly was
// we need one more spot (testPoly broken into front and back)
// and we need to shift everything from base up to test down one spot
mPolyIndexList.insert(mBase);
// need one more poly for front poly
mPolyIndexList[mBase] = mPolyList.size();
mPolyList.push_back(frontPoly);
mPolyExtentsList.increment();
setExtents(mPolyList.last(),mPolyExtentsList.last());
// set up back poly
mPolyList[testIdx] = backPoly;
setExtents(mPolyList[testIdx],mPolyExtentsList[testIdx]);
// new base poly...
setBase(mBase);
// we inserted an element, increase mMaxTouched...
mMaxTouched++;
testOffset=0;
switched = false;
}
//----------------------------------------------------------------------------
bool DepthSortList::overlap(Poly * poly1, Poly * poly2)
{
// check for overlap without any shortcuts
S32 sz1 = poly1->vertexCount;
S32 sz2 = poly2->vertexCount;
Point3F * a1, * b1;
Point3F * a2, * b2;
Point2F norm;
F32 dot;
b1 = &mVertexList[mIndexList[poly1->vertexStart+sz1-1]].point;
S32 i;
for (i=0; i<sz1; i++)
{
a1 = b1;
b1 = &mVertexList[mIndexList[poly1->vertexStart+i]].point;
// get the mid-point of this edge
Point3F mid1 = *a1+*b1;
mid1 *= 0.5f;
bool midOutside = false;
b2 = &mVertexList[mIndexList[poly2->vertexStart+sz2-1]].point;
for (S32 j=0; j<sz2; j++)
{
a2 = b2;
b2 = &mVertexList[mIndexList[poly2->vertexStart+j]].point;
// test for intersection of a1-b1 and a2-b2 (on x-z plane)
// they intersect if a1 & b1 are on opp. sides of line a2-b2
// and a2 & b2 are on opp. sides of line a1-b1
norm.set(a2->z - b2->z, b2->x - a2->x); // normal to line a2-b2
dot = norm.x * a2->x + norm.y * a2->z; // dot of a2 and norm
if (norm.x * mid1.x + norm.y * mid1.z - dot >= 0) // special check for midpoint of line
midOutside = true;
if ( ((norm.x * a1->x + norm.y * a1->z) - dot) * ((norm.x * b1->x + norm.y * b1->z) - dot) >= 0 )
// a1 & b1 are on the same side of the line a2-b2...edges don't overlap
continue;
norm.set(a1->z - b1->z, b1->x - a1->x); // normal to line a1-b1
dot = norm.x * a1->x + norm.y * a1->z; // dot of a1 and norm
if ( ((norm.x * a2->x + norm.y * a2->z) - dot) * ((norm.x * b2->x + norm.y * b2->z) - dot) >= 0 )
// a2 & b2 are on the same side of the line a1-b1...edges don't overlap
continue;
return true; // edges overlap, so polys overlap
}
if (!midOutside)
return true; // midpoint of a1-b1 is inside the poly
}
// edges don't overlap...but one poly might be contained inside the other
Point3F center = mVertexList[mIndexList[poly2->vertexStart]].point;
for (i=1; i<sz2; i++)
center += mVertexList[mIndexList[poly2->vertexStart+i]].point;
center *= 1.0f / (F32)poly2->vertexCount;
b1 = &mVertexList[mIndexList[poly1->vertexStart+sz1-1]].point;
for (i=0; i<sz1; i++)
{
a1 = b1;
b1 = &mVertexList[mIndexList[poly1->vertexStart+i]].point;
norm.set(a1->z - b1->z, b1->x - a1->x); // normal to line a1-b1
dot = norm.x * a1->x + norm.y * a1->z; // dot of a1 and norm
if (center.x * norm.x + center.z * norm.y > dot)
// center is outside this edge, poly2 is not inside poly1
break;
}
if (i==sz1)
return true; // first vert of poly2 inside poly1 (so all of poly2 inside poly1)
center = mVertexList[mIndexList[poly1->vertexStart]].point;
for (i=1; i<sz1; i++)
center += mVertexList[mIndexList[poly1->vertexStart+i]].point;
center *= 1.0f / (F32)poly1->vertexCount;
b2 = &mVertexList[mIndexList[poly2->vertexStart+sz2-1]].point;
for (i=0; i<sz2; i++)
{
a2 = b2;
b2 = &mVertexList[mIndexList[poly2->vertexStart+i]].point;
norm.set(a2->z - b2->z, b2->x - a2->x); // normal to line a2-b2
dot = norm.x * a2->x + norm.y * a2->z; // dot of a1 and norm
if (center.x * norm.x + center.z * norm.y > dot)
// v is outside this edge, poly1 is not inside poly2
break;
}
if (i==sz2)
return true; // first vert of poly1 inside poly2 (so all of poly1 inside poly2)
return false; // we survived, no overlap
}
//----------------------------------------------------------------------------
Vector<U32> frontVerts(__FILE__, __LINE__);
Vector<U32> backVerts(__FILE__, __LINE__);
// Split source poly into front and back. If either front or back is degenerate, don't do anything.
// If we have a front and a back, then add the verts to our vertex list and fill out the poly structures.
bool DepthSortList::splitPoly(const Poly & src, Point3F & normal, F32 k, Poly & frontPoly, Poly & backPoly)
{
frontVerts.clear();
backVerts.clear();
// already degenerate...
AssertFatal(src.vertexCount>=3,"DepthSortList::splitPoly - Don't need to split a triangle!");
S32 startSize = mVertexList.size();
// Assume back and front are degenerate polygons until proven otherwise.
bool backDegen = true, frontDegen = true;
U32 bIdx;
Point3F * a, * b;
F32 dota, dotb;
S32 signA, signB;
F32 splitTolSq = SPLIT_TOL * SPLIT_TOL * mDot(normal,normal);
bIdx = mIndexList[src.vertexStart+src.vertexCount-1];
b = &mVertexList[bIdx].point;
dotb = mDot(normal,*b)-k;
// Sign variable coded as follows: 1 for outside, 0 on the plane and -1 for inside.
if (dotb*dotb > splitTolSq)
signB = dotb > 0.0f ? 1 : -1;
else
signB = 0;
S32 i;
for (i = 0; i<src.vertexCount; i++)
{
a = b;
bIdx = mIndexList[src.vertexStart+i];
b = &mVertexList[bIdx].point;
dota = dotb;
dotb = mDot(normal,*b)-k;
signA = signB;
if (dotb*dotb > splitTolSq)
signB = dotb > 0.0f ? 1 : -1;
else
signB = 0;
switch(signA*3 + signB + 4) // +4 is to make values go from 0 up...hopefully enticing compiler to make a jump-table
{
case 0: // A-, B-
case 3: // A., B-
backVerts.push_back(bIdx);
backDegen = false;
break;
case 8: // A+, B+
case 5: // A., B+
frontVerts.push_back(bIdx);
frontDegen = false;
break;
case 1: // A-, B.
case 4: // A., B.
case 7: // A+, B.
backVerts.push_back(bIdx);
frontVerts.push_back(bIdx);
break;
case 2: // A-, B+
{
// intersect line A-B with plane
F32 dotA = mDot(*a,normal);
F32 dotB = mDot(*b,normal);
Vertex v;
v.point = *a-*b;
v.point *= (k-dotB)/(dotA-dotB);
v.point += *b;
frontVerts.push_back(mVertexList.size());
backVerts.push_back(mVertexList.size());
frontVerts.push_back(bIdx);
mVertexList.push_back(v);
b = &mVertexList[bIdx].point; // better get this pointer again since we just incremented vector
frontDegen = false;
break;
}
case 6: // A+, B-
{
// intersect line A-B with plane
F32 dotA = mDot(*a,normal);
F32 dotB = mDot(*b,normal);
Vertex v;
v.point = *a-*b;
v.point *= (k-dotB)/(dotA-dotB);
v.point += *b;
frontVerts.push_back(mVertexList.size());
backVerts.push_back(mVertexList.size());
backVerts.push_back(bIdx);
mVertexList.push_back(v);
b = &mVertexList[bIdx].point; // better get this pointer again since we just incremented vector
backDegen = false;
break;
}
}
}
// Check for degeneracy.
if (!ALWAYS_RETURN_FRONT_AND_BACK)
{
if (frontVerts.size()<3 || backVerts.size()<3 || frontDegen || backDegen)
{
// didn't need to be split...return two empty polys
// and restore vertex list to how it was
mVertexList.setSize(startSize);
frontPoly.vertexCount = backPoly.vertexCount = 0;
return false;
}
}
else
{
if (frontDegen)
frontVerts.clear();
if (backDegen)
backVerts.clear();
}
// front poly
frontPoly.plane = src.plane;
frontPoly.object = src.object;
frontPoly.material = src.material;
frontPoly.vertexStart = mIndexList.size();
frontPoly.vertexCount = frontVerts.size();
frontPoly.surfaceKey = src.surfaceKey;
frontPoly.polyFlags = src.polyFlags;
// back poly
backPoly.plane = src.plane;
backPoly.object = src.object;
backPoly.material = src.material;
backPoly.vertexStart = frontPoly.vertexStart + frontPoly.vertexCount;
backPoly.vertexCount = backVerts.size();
backPoly.surfaceKey = src.surfaceKey;
backPoly.polyFlags = src.polyFlags;
// add indices
mIndexList.setSize(backPoly.vertexStart+backPoly.vertexCount);
if( frontPoly.vertexCount ) {
dMemcpy(&mIndexList[frontPoly.vertexStart],
frontVerts.address(),
sizeof(U32)*frontPoly.vertexCount);
}
if( backPoly.vertexCount ) {
dMemcpy(&mIndexList[backPoly.vertexStart],
backVerts.address(),
sizeof(U32)*backPoly.vertexCount);
}
return true;
}
//----------------------------------------------------------------------------
Vector<DepthSortList::Poly> gWorkListA(256);
Vector<DepthSortList::Poly> gWorkListB(256);
Vector<DepthSortList::Poly> gWorkListJunkBin(256);
void DepthSortList::depthPartition(const Point3F * sourceVerts, U32 numVerts, Vector<Poly> & partition, Vector<Point3F> & partitionVerts)
{
// create the depth partition of the passed poly
// a depth partition is a partition of the poly on the
// x-z plane so that each sub-poly in the partition can be
// mapped onto exactly one plane in the depth list (i.e.,
// those polys found in mPolyIndexList... the ones that are
// depth sorted). The plane the sub-polys are mapped onto
// is the plane of the closest facing poly.
//
// y-coord of input polys are ignored, and are remapped
// on output to put the output polys on the
// corresponding planes.
// This routine is confusing because there are three lists of polys.
//
// The source list (passed in as a single poly, but becomes a list as
// it is split up) comprises the poly to be partitioned. Verts for sourcePoly
// are held in sourceVerts when passed to this routine, but immediately copied
// to mVertexList (and indices are added for each vert to mIndexList).
//
// The scraps list is generated from the source poly (it contains the outside
// piece of each cut that is made). Indices for polys in the scraps list are
// found in mIndexList and verts are found in mVerts list. Note that the depthPartition
// routine will add verts and indices to the member lists, but not polys.
//
// Finally, the partition list is the end result -- the depth partition. These
// polys are not indexed polys. The vertexStart field indexes directly into partitionVerts
// array.
if (mBase<0)
// begin the depth sort
sortByYExtents();
// apply cookie cutter to these polys
Vector<Poly> * sourceList = &gWorkListA;
sourceList->clear();
// add source poly for to passed verts
sourceList->increment();
sourceList->last().vertexStart = mIndexList.size();
sourceList->last().vertexCount = numVerts;
// add verts of source poly to mVertexList and mIndexList
mVertexList.setSize(mVertexList.size()+numVerts);
mIndexList.setSize(mIndexList.size()+numVerts);
for (S32 v=0; v<numVerts; v++)
{
mVertexList[mVertexList.size()-numVerts+v].point = sourceVerts[v];
mIndexList[mIndexList.size()-numVerts+v] = mVertexList.size()-numVerts+v;
}
// put scraps from cookie cutter in this list
Vector<Poly> * scraps = &gWorkListB;
scraps->clear();
gWorkListJunkBin.clear();
S32 i=0;
while (sourceList->size())
{
if (i>=mBase && !sortNext())
// that's it, no more polys to sort
break;
AssertFatal(i<=mBase,"DepthSortList::depthPartition - exceeded mBase.");
// use the topmost poly as the cookie cutter
Poly & cutter = mPolyList[mPolyIndexList[i]];
S32 startVert = partitionVerts.size();
bool allowclipping = cutter.polyFlags & CLIPPEDPOLYLIST_FLAG_ALLOWCLIPPING;
S32 j;
for (j=0; j<sourceList->size(); j++)
{
Poly toCut = (*sourceList)[j];
if(allowclipping)
cookieCutter(cutter,toCut,*scraps,partition,partitionVerts);
else
cookieCutter(cutter,toCut,gWorkListJunkBin,partition,partitionVerts);
}
// project all the new verts onto the cutter's plane
AssertFatal(mFabs(cutter.plane.y)>=MIN_Y_DOT,"DepthSortList::depthPartition - below MIN_Y_DOT.");
F32 invY = -1.0f / cutter.plane.y;
for (j=startVert; j<partitionVerts.size(); j++)
partitionVerts[j].y = invY * (cutter.plane.d + cutter.plane.x * partitionVerts[j].x + cutter.plane.z * partitionVerts[j].z);
if(allowclipping)
{
sourceList->clear();
// swap work lists -- scraps become source for next closest poly
Vector<Poly> * tmpListPtr = sourceList;
sourceList = scraps;
scraps = tmpListPtr;
}
i++;
}
}
//----------------------------------------------------------------------------
void DepthSortList::cookieCutter(Poly & cutter, Poly & cuttee,
Vector<Poly> & scraps, // outsides
Vector<Poly> & cookies, Vector<Point3F> & cookieVerts) // insides
{
// perhaps going too far with the cookie cutter analogy, but...
// cutter is used to cut cuttee
//
// the part that is inside of all cutter edges (on x-z plane)
// is put into the "cookie" list, parts that are outside are put
// onto the "scraps" list. "scraps" are indexed polys with indices
// and vertices in mIndexList and mVertexList. Cookies aren't indexed
// and points go into cookieVerts list.
ALWAYS_RETURN_FRONT_AND_BACK = true; // split routine will return polys even if no split occurs
// save off current state in case nothing inside all the edges of cutter (i.e., no "cookie")
S32 vsStart = cuttee.vertexStart;
S32 vcStart = cuttee.vertexCount;
S32 milStart = mIndexList.size();
S32 mvlStart = mVertexList.size();
S32 scStart = scraps.size();
Point3F a, b;
Poly scrap;
a = mVertexList[mIndexList[cutter.vertexStart+cutter.vertexCount-1]].point;
for (S32 i=0; i<cutter.vertexCount; i++)
{
b = mVertexList[mIndexList[cutter.vertexStart+i]].point;
Point3F n(a.z-b.z,0.0f,b.x-a.x);
F32 k = mDot(n,a);
splitPoly(cuttee,n,k,scrap,cuttee);
if (scrap.vertexCount)
scraps.push_back(scrap);
if (!cuttee.vertexCount)
// cut down to nothing...no need to keep cutting
break;
a = b;
}
if (cuttee.vertexCount)
{
// cuttee is non-degenerate, add it to cookies
cookies.push_back(cuttee);
cookies.last().vertexStart = cookieVerts.size();
for (S32 i=0; i<cuttee.vertexCount; i++)
cookieVerts.push_back(mVertexList[mIndexList[cuttee.vertexStart+i]].point);
}
else
{
// no cookie -- leave things as they were (except add cuttee to scraps)
cuttee.vertexStart = vsStart;
cuttee.vertexCount = vcStart;
mIndexList.setSize(milStart);
mVertexList.setSize(mvlStart);
scraps.setSize(scStart);
scraps.push_back(cuttee);
}
}
//----------------------------------------------------------------------------

108
engine/collision/depthSortList.h Executable file
View File

@ -0,0 +1,108 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _DEPTHSORTLIST_H_
#define _DEPTHSORTLIST_H_
#ifndef _CLIPPEDPOLYLIST_H_
#include "collision/clippedPolyList.h"
#endif
//----------------------------------------------------------------------------
class DepthSortList : public ClippedPolyList
{
typedef ClippedPolyList Parent;
public:
struct PolyExtents
{
// extents of poly on each coordinate axis
F32 xMin;
F32 xMax;
F32 yMin;
F32 yMax;
F32 zMin;
F32 zMax;
};
typedef Vector<PolyExtents> PolyExtentsList;
typedef Vector<U32> PolyIndexList;
// Internal data
PolyExtentsList mPolyExtentsList;
PolyIndexList mPolyIndexList;
Point3F mExtent; // dimensions of the sort area
S32 mBase; // base position in the list...everything before this is sorted correctly
Poly * mBasePoly; // poly currently in base position
Point3F * mBaseNormal; // normal of poly currently in base position
F32 mBaseDot; // dot of basePoly with baseNormal
F32 mBaseYMax; // max y extent of base poly
S32 mMaxTouched; // highest index swapped into thus far...y-extents may be improperly sorted before this index
PolyExtents * mBaseExtents; // x,y,z extents of basePoly
// set the base position -- everything before this point should be correctly sorted
void setBase(S32);
// some utilities used for sorting
bool splitPoly(const Poly & sourcePoly, Point3F & normal, F32 k, Poly & front, Poly & back);
bool overlap(Poly *, Poly *);
void handleOverlap(Poly * testPoly, Point3F & testNormal, F32 testDot, S32 & testOffset, bool & switched);
void sortByYExtents();
void setExtents(Poly &, PolyExtents &);
// one iteration of the sort routine -- finds a poly to fill current base position
bool sortNext();
// used by depthPartition
void cookieCutter(Poly & cutter, Poly & cuttee,
Vector<Poly> & scraps,
Vector<Poly> & cookies, Vector<Point3F> & cookieVerts);
void wireCube(const Point3F & size, const Point3F & pos);
public:
//
DepthSortList();
~DepthSortList();
void set(const MatrixF & mat, Point3F & extents);
void clear();
void clearSort();
// the point of this class
void sort();
void depthPartition(const Point3F * sourceVerts, U32 numVerts, Vector<Poly> & partition, Vector<Point3F> & partitionVerts);
// Virtual methods
void end();
// U32 addPoint(const Point3F& p);
// bool isEmpty() const;
// void begin(U32 material,U32 surfaceKey);
// void plane(U32 v1,U32 v2,U32 v3);
// void plane(const PlaneF& p);
// void vertex(U32 vi);
bool getMapping(MatrixF *, Box3F *);
// access to the polys in order (note: returned pointers are volatile, may change if polys added).
void getOrderedPoly(U32 ith, Poly ** poly, PolyExtents ** polyExtent);
// unordered access
PolyExtents & getExtents(U32 idx) { return mPolyExtentsList[idx]; }
Poly & getPoly(U32 idx) { return mPolyList[idx]; }
//
void render();
static bool renderWithDepth;
};
inline void DepthSortList::getOrderedPoly(U32 ith, Poly ** poly, PolyExtents ** polyExtent)
{
*poly = &mPolyList[mPolyIndexList[ith]];
*polyExtent = &mPolyExtentsList[mPolyIndexList[ith]];
}
#endif

View File

@ -0,0 +1,267 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "dgl/dgl.h"
#include "math/mMath.h"
#include "console/console.h"
#include "collision/earlyOutPolyList.h"
//----------------------------------------------------------------------------
EarlyOutPolyList::EarlyOutPolyList()
{
VECTOR_SET_ASSOCIATION(mPolyList);
VECTOR_SET_ASSOCIATION(mVertexList);
VECTOR_SET_ASSOCIATION(mIndexList);
VECTOR_SET_ASSOCIATION(mPolyPlaneList);
VECTOR_SET_ASSOCIATION(mPlaneList);
mNormal.set(0, 0, 0);
mIndexList.reserve(100);
mEarlyOut = false;
}
EarlyOutPolyList::~EarlyOutPolyList()
{
}
//----------------------------------------------------------------------------
void EarlyOutPolyList::clear()
{
// Only clears internal data
mPolyList.clear();
mVertexList.clear();
mIndexList.clear();
mPolyPlaneList.clear();
mEarlyOut = false;
}
bool EarlyOutPolyList::isEmpty() const
{
return mEarlyOut == false;
}
//----------------------------------------------------------------------------
U32 EarlyOutPolyList::addPoint(const Point3F& p)
{
if (mEarlyOut == true)
return 0;
mVertexList.increment();
Vertex& v = mVertexList.last();
v.point.x = p.x * mScale.x;
v.point.y = p.y * mScale.y;
v.point.z = p.z * mScale.z;
mMatrix.mulP(v.point);
// Build the plane mask
v.mask = 0;
for (U32 i = 0; i < mPlaneList.size(); i++)
if (mPlaneList[i].distToPlane(v.point) > 0)
v.mask |= 1 << i;
// If the point is inside all the planes, then we're done!
if (v.mask == 0)
mEarlyOut = true;
return mVertexList.size() - 1;
}
U32 EarlyOutPolyList::addPlane(const PlaneF& plane)
{
mPolyPlaneList.increment();
mPlaneTransformer.transform(plane, mPolyPlaneList.last());
return mPolyPlaneList.size() - 1;
}
//----------------------------------------------------------------------------
void EarlyOutPolyList::begin(U32 material,U32 surfaceKey)
{
if (mEarlyOut == true)
return;
mPolyList.increment();
Poly& poly = mPolyList.last();
poly.object = mCurrObject;
poly.material = material;
poly.vertexStart = mIndexList.size();
poly.surfaceKey = surfaceKey;
}
//----------------------------------------------------------------------------
void EarlyOutPolyList::plane(U32 v1,U32 v2,U32 v3)
{
if (mEarlyOut == true)
return;
mPolyList.last().plane.set(mVertexList[v1].point,
mVertexList[v2].point,mVertexList[v3].point);
}
void EarlyOutPolyList::plane(const PlaneF& p)
{
if (mEarlyOut == true)
return;
mPlaneTransformer.transform(p, mPolyList.last().plane);
}
void EarlyOutPolyList::plane(const U32 index)
{
if (mEarlyOut == true)
return;
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
mPolyList.last().plane = mPolyPlaneList[index];
}
const PlaneF& EarlyOutPolyList::getIndexedPlane(const U32 index)
{
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
return mPolyPlaneList[index];
}
//----------------------------------------------------------------------------
void EarlyOutPolyList::vertex(U32 vi)
{
if (mEarlyOut == true)
return;
mIndexList.push_back(vi);
}
//----------------------------------------------------------------------------
void EarlyOutPolyList::end()
{
if (mEarlyOut == true)
return;
Poly& poly = mPolyList.last();
// Anything facing away from the mNormal is rejected
if (mDot(poly.plane,mNormal) > 0) {
mIndexList.setSize(poly.vertexStart);
mPolyList.decrement();
return;
}
// Build intial inside/outside plane masks
U32 indexStart = poly.vertexStart;
U32 vertexCount = mIndexList.size() - indexStart;
U32 frontMask = 0,backMask = 0;
U32 i;
for (i = indexStart; i < mIndexList.size(); i++) {
U32 mask = mVertexList[mIndexList[i]].mask;
frontMask |= mask;
backMask |= ~mask;
}
// Trivial accept if all the vertices are on the backsides of
// all the planes.
if (!frontMask) {
poly.vertexCount = vertexCount;
mEarlyOut = true;
return;
}
// Trivial reject if any plane not crossed has all it's points
// on the front.
U32 crossMask = frontMask & backMask;
if (~crossMask & frontMask) {
mIndexList.setSize(poly.vertexStart);
mPolyList.decrement();
return;
}
// Need to do some clipping
for (U32 p = 0; p < mPlaneList.size(); p++) {
U32 pmask = 1 << p;
// Only test against this plane if we have something
// on both sides
if (crossMask & pmask) {
U32 indexEnd = mIndexList.size();
U32 i1 = indexEnd - 1;
U32 mask1 = mVertexList[mIndexList[i1]].mask;
for (U32 i2 = indexStart; i2 < indexEnd; i2++) {
U32 mask2 = mVertexList[mIndexList[i2]].mask;
if ((mask1 ^ mask2) & pmask) {
//
mVertexList.increment();
VectorF& v1 = mVertexList[mIndexList[i1]].point;
VectorF& v2 = mVertexList[mIndexList[i2]].point;
VectorF vv = v2 - v1;
F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv);
mIndexList.push_back(mVertexList.size() - 1);
Vertex& iv = mVertexList.last();
iv.point.x = v1.x + vv.x * t;
iv.point.y = v1.y + vv.y * t;
iv.point.z = v1.z + vv.z * t;
iv.mask = 0;
// Test against the remaining planes
for (U32 i = p + 1; i < mPlaneList.size(); i++)
if (mPlaneList[i].distToPlane(iv.point) > 0) {
iv.mask = 1 << i;
break;
}
}
if (!(mask2 & pmask)) {
U32 index = mIndexList[i2];
mIndexList.push_back(index);
}
mask1 = mask2;
i1 = i2;
}
// Check for degenerate
indexStart = indexEnd;
if (mIndexList.size() - indexStart < 3) {
mIndexList.setSize(poly.vertexStart);
mPolyList.decrement();
return;
}
}
}
// If we reach here, then there's a poly!
mEarlyOut = true;
// Emit what's left and compress the index list.
poly.vertexCount = mIndexList.size() - indexStart;
memcpy(&mIndexList[poly.vertexStart],
&mIndexList[indexStart],poly.vertexCount);
mIndexList.setSize(poly.vertexStart + poly.vertexCount);
}
//----------------------------------------------------------------------------
void EarlyOutPolyList::memcpy(U32* dst, U32* src,U32 size)
{
U32* end = src + size;
while (src != end)
*dst++ = *src++;
}

View File

@ -0,0 +1,80 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _EARLYOUTPOLYLIST_H_
#define _EARLYOUTPOLYLIST_H_
#ifndef _ABSTRACTPOLYLIST_H_
#include "collision/abstractPolyList.h"
#endif
/// Early out check PolyList
///
/// This class is used primarily for triggers and similar checks. It checks to see
/// if any of the geometry you feed it is inside its area, and if it is, it stops
/// checking for any more data and returns a true value. This is good if you want
/// to know if anything is in your "trigger" area, for instance.
///
/// @see AbstractPolyList
class EarlyOutPolyList : public AbstractPolyList
{
void memcpy(U32* d, U32* s,U32 size);
// Internal data
struct Vertex {
Point3F point;
U32 mask;
};
struct Poly {
PlaneF plane;
SceneObject* object;
U32 material;
U32 vertexStart;
U32 vertexCount;
U32 surfaceKey;
};
public:
typedef Vector<PlaneF> PlaneList;
private:
typedef Vector<Vertex> VertexList;
typedef Vector<Poly> PolyList;
typedef Vector<U32> IndexList;
PolyList mPolyList;
VertexList mVertexList;
IndexList mIndexList;
bool mEarlyOut;
PlaneList mPolyPlaneList;
public:
// Data set by caller
PlaneList mPlaneList;
VectorF mNormal;
public:
EarlyOutPolyList();
~EarlyOutPolyList();
void clear();
// Virtual methods
bool isEmpty() const;
U32 addPoint(const Point3F& p);
U32 addPlane(const PlaneF& plane);
void begin(U32 material,U32 surfaceKey);
void plane(U32 v1,U32 v2,U32 v3);
void plane(const PlaneF& p);
void plane(const U32 index);
void vertex(U32 vi);
void end();
protected:
const PlaneF& getIndexedPlane(const U32 index);
};
#endif // _H_EARLYOUTPOLYLIST_

View File

@ -0,0 +1,508 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "dgl/dgl.h"
#include "math/mMath.h"
#include "console/console.h"
#include "collision/extrudedPolyList.h"
#include "collision/polyhedron.h"
#include "collision/collision.h"
// Minimum distance from a face
F32 ExtrudedPolyList::FaceEpsilon = 0.01f;
// Value used to compare collision times
F32 ExtrudedPolyList::EqualEpsilon = 0.0001f;
ExtrudedPolyList::ExtrudedPolyList()
{
VECTOR_SET_ASSOCIATION(mVertexList);
VECTOR_SET_ASSOCIATION(mIndexList);
VECTOR_SET_ASSOCIATION(mExtrudedList);
VECTOR_SET_ASSOCIATION(mPlaneList);
VECTOR_SET_ASSOCIATION(mPolyPlaneList);
mVelocity.set(0.0f,0.0f,0.0f);
mIndexList.reserve(128);
mVertexList.reserve(64);
mPolyPlaneList.reserve(64);
mPlaneList.reserve(64);
mCollisionList = 0;
}
ExtrudedPolyList::~ExtrudedPolyList()
{
}
//----------------------------------------------------------------------------
bool ExtrudedPolyList::isEmpty() const
{
return mCollisionList->count == 0;
}
//----------------------------------------------------------------------------
void ExtrudedPolyList::extrude(const Polyhedron& pt, const VectorF& vector)
{
// Clear state
mIndexList.clear();
mVertexList.clear();
mPlaneList.clear();
mPolyPlaneList.clear();
// Determine which faces will be extruded.
mExtrudedList.setSize(pt.planeList.size());
for (U32 f = 0; f < pt.planeList.size(); f++)
{
const PlaneF& face = pt.planeList[f];
ExtrudedFace& eface = mExtrudedList[f];
F32 dot = mDot(face,vector);
eface.active = dot > EqualEpsilon;
if (eface.active)
{
eface.maxDistance = dot;
eface.plane = face;
eface.planeMask = BIT(mPlaneList.size());
// Add the face as a plane to clip against.
mPlaneList.increment(2);
PlaneF* plane = mPlaneList.end() - 2;
plane[0] = plane[1] = face;
plane[0].invert();
}
}
// Produce extruded planes for bounding and internal edges
for (U32 e = 0; e < pt.edgeList.size(); e++)
{
Polyhedron::Edge const& edge = pt.edgeList[e];
ExtrudedFace& ef1 = mExtrudedList[edge.face[0]];
ExtrudedFace& ef2 = mExtrudedList[edge.face[1]];
if (ef1.active || ef2.active)
{
// Assumes that the edge points are clockwise
// for face[0].
const Point3F& p1 = pt.pointList[edge.vertex[1]];
const Point3F &p2 = pt.pointList[edge.vertex[0]];
Point3F p3 = p2 + vector;
mPlaneList.increment(2);
PlaneF* plane = mPlaneList.end() - 2;
plane[0].set(p3,p2,p1);
plane[1] = plane[0];
plane[1].invert();
U32 pmask = BIT(mPlaneList.size()-2);
ef1.planeMask |= pmask;
ef2.planeMask |= pmask << 1;
}
}
}
//----------------------------------------------------------------------------
void ExtrudedPolyList::setCollisionList(CollisionList* info)
{
mCollisionList = info;
mCollisionList->count = 0;
mCollisionList->t = 2;
}
//----------------------------------------------------------------------------
void ExtrudedPolyList::adjustCollisionTime()
{
if (!mCollisionList->count)
return;
mCollisionList->t = mClampF(mCollisionList->t, 0.f, 1.f);
}
//----------------------------------------------------------------------------
U32 ExtrudedPolyList::addPoint(const Point3F& p)
{
mVertexList.increment();
Vertex& v = mVertexList.last();
v.point.x = p.x * mScale.x;
v.point.y = p.y * mScale.y;
v.point.z = p.z * mScale.z;
mMatrix.mulP(v.point);
// Build the plane mask, planes come in pairs
v.mask = 0;
for (U32 i = 0; i < mPlaneList.size(); i ++)
if (mPlaneList[i].distToPlane(v.point) >= 0.f)
v.mask |= BIT(i);
return mVertexList.size() - 1;
}
U32 ExtrudedPolyList::addPlane(const PlaneF& plane)
{
mPolyPlaneList.increment();
mPlaneTransformer.transform(plane, mPolyPlaneList.last());
return mPolyPlaneList.size() - 1;
}
//----------------------------------------------------------------------------
void ExtrudedPolyList::begin(U32 material, U32 /*surfaceKey*/)
{
mPoly.object = mCurrObject;
mPoly.material = material;
mIndexList.clear();
}
void ExtrudedPolyList::plane(U32 v1, U32 v2, U32 v3)
{
mPoly.plane.set(mVertexList[v1].point,
mVertexList[v2].point,
mVertexList[v3].point);
// We hope this isn't needed but we're leaving it in anyway -- BJG/EGH
mPoly.plane.normalizeSafe();
}
void ExtrudedPolyList::plane(const PlaneF& p)
{
mPlaneTransformer.transform(p, mPoly.plane);
}
void ExtrudedPolyList::plane(const U32 index)
{
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
mPoly.plane = mPolyPlaneList[index];
}
const PlaneF& ExtrudedPolyList::getIndexedPlane(const U32 index)
{
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
return mPolyPlaneList[index];
}
void ExtrudedPolyList::vertex(U32 vi)
{
mIndexList.push_back(vi);
}
void ExtrudedPolyList::end()
{
// Anything facing away from the mVelocity is rejected (and also
// cap to max collisions)
if (mDot(mPoly.plane, mNormalVelocity) > 0.f ||
mCollisionList->count >= CollisionList::MaxCollisions)
return;
// Test the built up poly (stored in mPoly) against all our extruded
// faces.
U32 cFaceCount = 0;
ExtrudedFace* cFace[30];
bool cEdgeColl[30];
ExtrudedFace* face = mExtrudedList.begin();
ExtrudedFace* end = mExtrudedList.end();
for (; face != end; face++)
{
// Skip inactive..
if (!face->active)
continue;
// Update the dot product.
face->faceDot = -mDot(face->plane,mPoly.plane);
// Skip it if we're facing towards...
if(face->faceDot <= 0.f)
continue;
// Test, and skip if colliding.
if (!testPoly(*face))
continue;
// Note collision.
cFace[cFaceCount] = face;
cEdgeColl[cFaceCount++] = false;
}
if (!cFaceCount)
{
face = mExtrudedList.begin();
end = mExtrudedList.end();
for (; face != end; face++)
{
// Don't need to do dot product second time, so just check if it's
// active (we already did the dot product in the previous loop).
if (!face->active)
continue;
// Skip it if we're facing away...
if(face->faceDot > 0.f)
continue;
// Do collision as above.
if (!testPoly(*face))
continue;
// Note the collision.
cFace[cFaceCount] = face;
cEdgeColl[cFaceCount++] = true;
}
}
// If we STILL don't have any collisions, just skip out.
if (!cFaceCount)
return;
// Pick the best collision face based on best alignment with respective
// face.
face = cFace[0];
bool edge = cEdgeColl[0];
for (U32 f = 1; f < cFaceCount; f++)
{
if (cFace[f]->faceDot <= face->faceDot)
continue;
face = cFace[f];
edge = cEdgeColl[f];
}
// Add it to the collision list if it's better and/or equal
// to our current best.
// Don't add it to the collision list if it's too far away.
if (face->time > mCollisionList->t + EqualEpsilon || face->time >= 1.0f)
return;
if (face->time < mCollisionList->t - EqualEpsilon)
{
// If this is significantly closer than before, then clear out the
// list, as it's a better match than the old stuff.
mCollisionList->t = face->time;
mCollisionList->count = 0;
mCollisionList->maxHeight = face->height;
}
else
{
// Otherwise, just update some book-keeping stuff.
if (face->height > mCollisionList->maxHeight)
mCollisionList->maxHeight = face->height;
}
// Note the collision in our collision list.
Collision& collision = mCollisionList->collision[mCollisionList->count++];
collision.point = face->point;
collision.faceDot = face->faceDot;
collision.face = face - mExtrudedList.begin();
collision.object = mPoly.object;
collision.normal = mPoly.plane;
collision.material = mPoly.material;
}
//----------------------------------------------------------------------------
bool ExtrudedPolyList::testPoly(ExtrudedFace& face)
{
// Build intial inside/outside plane masks
U32 indexStart = 0;
U32 indexEnd = mIndexList.size();
U32 oVertexSize = mVertexList.size();
U32 oIndexSize = mIndexList.size();
U32 frontMask = 0,backMask = 0;
for (U32 i = indexStart; i < indexEnd; i++)
{
U32 mask = mVertexList[mIndexList[i]].mask & face.planeMask;
frontMask |= mask;
backMask |= ~mask;
}
// Clip the mPoly against the planes that bound the face...
// Trivial accept if all the vertices are on the backsides of
// all the planes.
if (frontMask)
{
// Trivial reject if any plane not crossed has all it's points
// on the front.
U32 crossMask = frontMask & backMask;
if (~crossMask & frontMask)
return false;
// Need to do some clipping
for (U32 p=0; p < mPlaneList.size(); p++)
{
U32 pmask = BIT(p);
U32 newStart = mIndexList.size();
// Only test against this plane if we have something
// on both sides - otherwise skip.
if (!(face.planeMask & crossMask & pmask))
continue;
U32 i1 = indexEnd - 1;
U32 mask1 = mVertexList[mIndexList[i1]].mask;
for (U32 i2 = indexStart; i2 < indexEnd; i2++)
{
const U32 mask2 = mVertexList[mIndexList[i2]].mask;
if ((mask1 ^ mask2) & pmask)
{
// Clip the edge against the plane.
mVertexList.increment();
VectorF& v1 = mVertexList[mIndexList[i1]].point;
VectorF& v2 = mVertexList[mIndexList[i2]].point;
VectorF vv = v2 - v1;
F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv);
mIndexList.push_back(mVertexList.size() - 1);
Vertex& iv = mVertexList.last();
iv.point.x = v1.x + vv.x * t;
iv.point.y = v1.y + vv.y * t;
iv.point.z = v1.z + vv.z * t;
iv.mask = 0;
// Test against the remaining planes
for (U32 i = p+1; i < mPlaneList.size(); i ++)
{
if (mPlaneList[i].distToPlane(iv.point) > 0.f)
iv.mask |= BIT(i);
}
}
if (!(mask2 & pmask))
{
U32 index = mIndexList[i2];
mIndexList.push_back(index);
}
mask1 = mask2;
i1 = i2;
}
// Check for degenerate
indexStart = newStart;
indexEnd = mIndexList.size();
if (mIndexList.size() - indexStart < 3)
{
mVertexList.setSize(oVertexSize);
mIndexList.setSize(oIndexSize);
return false;
}
}
}
// Find closest point on the mPoly
Point3F bp;
F32 bd = 1E30f;
F32 height = -1E30f;
for (U32 b = indexStart; b < indexEnd; b++)
{
Vertex& vertex = mVertexList[mIndexList[b]];
F32 dist = face.plane.distToPlane(vertex.point);
if (dist <= bd)
{
bd = (dist < 0.0f)? 0.0f: dist;
bp = vertex.point;
}
// Since we don't clip against the back plane, we'll
// only include vertex heights that are within range.
if (vertex.point.z > height && dist < face.maxDistance)
height = vertex.point.z;
}
// Do extruded points for back-off.
F32 oldBd=bd;
for (U32 b = indexStart; b < indexEnd; b++)
{
Vertex& vertex = mVertexList[mIndexList[b]];
// Extrude out just a tad to make sure we don't end up getting too close to the
// geometry and getting stuck - but cap it so we don't introduce error into long
// sweeps.
F32 dist = face.plane.distToPlane( vertex.point
+ Point3F(mPoly.plane) * getMin(face.maxDistance * 0.2f, 0.01f));
if (dist <= bd)
{
bd = (dist < 0.0f)? 0.0f: dist;
bp = vertex.point;
}
}
// Remove temporary data
mVertexList.setSize(oVertexSize);
mIndexList.setSize(oIndexSize);
// Don't add it to the collision list if it's worse then our current best.
if (oldBd >= face.maxDistance)
return false;
// Update our info and indicate we should add to the model.
F32 oldT = oldBd / face.maxDistance;
F32 pushBackT = bd / face.maxDistance;
if(oldT - pushBackT > 0.1f)
face.time = oldT - 0.1f;
else
face.time = pushBackT;
face.height = height;
face.point = bp;
return true;
}
//----------------------------------------------------------------------------
void ExtrudedPolyList::render()
{
if (!mCollisionList)
return;
glBegin(GL_LINES);
glColor3f(1.0f,1.0f,0.0f);
for (U32 d = 0; d < mCollisionList->count; d++)
{
Collision& face = mCollisionList->collision[d];
Point3F ep = face.point;
ep += face.normal;
glVertex3fv(face.point);
glVertex3fv(ep);
}
glEnd();
}
//--------------------------------------------------------------------------
void ExtrudedPolyList::setVelocity(const VectorF& velocity)
{
mVelocity = velocity;
if (velocity.isZero() == false)
{
mNormalVelocity = velocity;
mNormalVelocity.normalize();
setInterestNormal(mNormalVelocity);
}
else
{
mNormalVelocity.set(0.0f, 0.0f, 0.0f);
clearInterestNormal();
}
}

View File

@ -0,0 +1,107 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _EXTRUDEDPOLYLIST_H_
#define _EXTRUDEDPOLYLIST_H_
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _ABSTRACTPOLYLIST_H_
#include "collision/abstractPolyList.h"
#endif
struct Polyhedron;
struct CollisionList;
//----------------------------------------------------------------------------
/// Extruded Polytope PolyList
///
/// This class is used primarily for collision detection, by objects which need
/// to check for obstructions along their path. You feed it a polytope to
/// extrude along the direction of movement, and it gives you a list of collisions.
///
/// @see AbstractPolyList
class ExtrudedPolyList: public AbstractPolyList
{
public:
struct Vertex {
Point3F point;
U32 mask;
};
struct Poly {
PlaneF plane;
SceneObject* object;
U32 material;
};
struct ExtrudedFace {
bool active;
PlaneF plane;
F32 maxDistance;
U32 planeMask;
F32 faceDot;
F32 faceShift;
F32 time;
Point3F point;
F32 height;
};
typedef Vector<ExtrudedFace> ExtrudedList;
typedef Vector<PlaneF> PlaneList;
typedef Vector<Vertex> VertexList;
typedef Vector<U32> IndexList;
static F32 EqualEpsilon;
static F32 FaceEpsilon;
// Internal data
VertexList mVertexList;
IndexList mIndexList;
ExtrudedList mExtrudedList;
PlaneList mPlaneList;
VectorF mVelocity;
VectorF mNormalVelocity;
F32 mFaceShift;
Poly mPoly;
// Returned info
CollisionList* mCollisionList;
PlaneList mPolyPlaneList;
//
private:
bool testPoly(ExtrudedFace&);
public:
ExtrudedPolyList();
~ExtrudedPolyList();
void extrude(const Polyhedron&, const VectorF& vec);
void setVelocity(const VectorF& velocity);
void setCollisionList(CollisionList*);
void adjustCollisionTime();
void render();
// Virtual methods
bool isEmpty() const;
U32 addPoint(const Point3F& p);
U32 addPlane(const PlaneF& plane);
void begin(U32 material, U32 surfaceKey);
void plane(U32 v1,U32 v2,U32 v3);
void plane(const PlaneF& p);
void plane(const U32 index);
void vertex(U32 vi);
void end();
protected:
const PlaneF& getIndexedPlane(const U32 index);
};
#endif

422
engine/collision/gjk.cc Executable file
View File

@ -0,0 +1,422 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//
// The core algorithms in this file are based on code written
// by G. van den Bergen for his interference detection library,
// "SOLID 2.0"
//-----------------------------------------------------------------------------
#include "dgl/dgl.h"
#include "core/dataChunker.h"
#include "collision/collision.h"
#include "sceneGraph/sceneGraph.h"
#include "sim/sceneObject.h"
#include "terrain/terrData.h"
#include "collision/convex.h"
#include "collision/gjk.h"
//----------------------------------------------------------------------------
static F32 rel_error = 1E-5; // relative error in the computed distance
static F32 sTolerance = 1E-3; // Distance tolerance
static F32 sEpsilon2 = 1E-20; // Zero length vector
static U32 sIteration = 15; // Stuck in a loop?
S32 num_iterations = 0;
S32 num_irregularities = 0;
//----------------------------------------------------------------------------
GjkCollisionState::GjkCollisionState()
{
a = b = 0;
}
GjkCollisionState::~GjkCollisionState()
{
}
//----------------------------------------------------------------------------
void GjkCollisionState::swap()
{
Convex* t = a; a = b; b = t;
CollisionStateList* l = mLista; mLista = mListb; mListb = l;
v.neg();
}
//----------------------------------------------------------------------------
void GjkCollisionState::compute_det()
{
// Dot new point with current set
for (int i = 0, bit = 1; i < 4; ++i, bit <<=1)
if (bits & bit)
dp[i][last] = dp[last][i] = mDot(y[i], y[last]);
dp[last][last] = mDot(y[last], y[last]);
// Calulate the determinent
det[last_bit][last] = 1;
for (int j = 0, sj = 1; j < 4; ++j, sj <<= 1) {
if (bits & sj) {
int s2 = sj | last_bit;
det[s2][j] = dp[last][last] - dp[last][j];
det[s2][last] = dp[j][j] - dp[j][last];
for (int k = 0, sk = 1; k < j; ++k, sk <<= 1) {
if (bits & sk) {
int s3 = sk | s2;
det[s3][k] = det[s2][j] * (dp[j][j] - dp[j][k]) +
det[s2][last] * (dp[last][j] - dp[last][k]);
det[s3][j] = det[sk | last_bit][k] * (dp[k][k] - dp[k][j]) +
det[sk | last_bit][last] * (dp[last][k] - dp[last][j]);
det[s3][last] = det[sk | sj][k] * (dp[k][k] - dp[k][last]) +
det[sk | sj][j] * (dp[j][k] - dp[j][last]);
}
}
}
}
if (all_bits == 15) {
det[15][0] = det[14][1] * (dp[1][1] - dp[1][0]) +
det[14][2] * (dp[2][1] - dp[2][0]) +
det[14][3] * (dp[3][1] - dp[3][0]);
det[15][1] = det[13][0] * (dp[0][0] - dp[0][1]) +
det[13][2] * (dp[2][0] - dp[2][1]) +
det[13][3] * (dp[3][0] - dp[3][1]);
det[15][2] = det[11][0] * (dp[0][0] - dp[0][2]) +
det[11][1] * (dp[1][0] - dp[1][2]) +
det[11][3] * (dp[3][0] - dp[3][2]);
det[15][3] = det[7][0] * (dp[0][0] - dp[0][3]) +
det[7][1] * (dp[1][0] - dp[1][3]) +
det[7][2] * (dp[2][0] - dp[2][3]);
}
}
//----------------------------------------------------------------------------
inline void GjkCollisionState::compute_vector(int bits, VectorF& v)
{
F32 sum = 0;
v.set(0, 0, 0);
for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) {
if (bits & bit) {
sum += det[bits][i];
v += y[i] * det[bits][i];
}
}
v *= 1 / sum;
}
//----------------------------------------------------------------------------
inline bool GjkCollisionState::valid(int s)
{
for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) {
if (all_bits & bit) {
if (s & bit) {
if (det[s][i] <= 0)
return false;
}
else
if (det[s | bit][i] > 0)
return false;
}
}
return true;
}
//----------------------------------------------------------------------------
inline bool GjkCollisionState::closest(VectorF& v)
{
compute_det();
for (int s = bits; s; --s) {
if ((s & bits) == s) {
if (valid(s | last_bit)) {
bits = s | last_bit;
if (bits != 15)
compute_vector(bits, v);
return true;
}
}
}
if (valid(last_bit)) {
bits = last_bit;
v = y[last];
return true;
}
return false;
}
//----------------------------------------------------------------------------
inline bool GjkCollisionState::degenerate(const VectorF& w)
{
for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1)
if ((all_bits & bit) && y[i] == w)
return true;
return false;
}
//----------------------------------------------------------------------------
inline void GjkCollisionState::nextBit()
{
last = 0;
last_bit = 1;
while (bits & last_bit) {
++last;
last_bit <<= 1;
}
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
void GjkCollisionState::set(Convex* aa, Convex* bb,
const MatrixF& a2w, const MatrixF& b2w)
{
a = aa;
b = bb;
bits = 0;
all_bits = 0;
reset(a2w,b2w);
// link
mLista = CollisionStateList::alloc();
mLista->mState = this;
mListb = CollisionStateList::alloc();
mListb->mState = this;
}
//----------------------------------------------------------------------------
void GjkCollisionState::reset(const MatrixF& a2w, const MatrixF& b2w)
{
VectorF zero(0,0,0),sa,sb;
a2w.mulP(a->support(zero),&sa);
b2w.mulP(b->support(zero),&sb);
v = sa - sb;
dist = v.len();
}
//----------------------------------------------------------------------------
void GjkCollisionState::getCollisionInfo(const MatrixF& mat, Collision* info)
{
AssertFatal(false, "GjkCollisionState::getCollisionInfo() - There remain scaling problems here.");
// This assumes that the shapes do not intersect
Point3F pa,pb;
if (bits) {
getClosestPoints(pa,pb);
mat.mulP(pa,&info->point);
b->getTransform().mulP(pb,&pa);
info->normal = info->point - pa;
}
else {
mat.mulP(p[last],&info->point);
info->normal = v;
}
info->normal.normalize();
info->object = b->getObject();
}
void GjkCollisionState::getClosestPoints(Point3F& p1, Point3F& p2)
{
F32 sum = 0;
p1.set(0, 0, 0);
p2.set(0, 0, 0);
for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) {
if (bits & bit) {
sum += det[bits][i];
p1 += p[i] * det[bits][i];
p2 += q[i] * det[bits][i];
}
}
F32 s = 1 / sum;
p1 *= s;
p2 *= s;
}
//----------------------------------------------------------------------------
bool GjkCollisionState::intersect(const MatrixF& a2w, const MatrixF& b2w)
{
num_iterations = 0;
MatrixF w2a,w2b;
w2a = a2w;
w2b = b2w;
w2a.inverse();
w2b.inverse();
reset(a2w,b2w);
bits = 0;
all_bits = 0;
do {
nextBit();
VectorF va,sa;
w2a.mulV(-v,&va);
p[last] = a->support(va);
a2w.mulP(p[last],&sa);
VectorF vb,sb;
w2b.mulV(v,&vb);
q[last] = b->support(vb);
b2w.mulP(q[last],&sb);
VectorF w = sa - sb;
if (mDot(v,w) > 0)
return false;
if (degenerate(w)) {
++num_irregularities;
return false;
}
y[last] = w;
all_bits = bits | last_bit;
++num_iterations;
if (!closest(v) || num_iterations > sIteration) {
++num_irregularities;
return false;
}
}
while (bits < 15 && v.lenSquared() > sEpsilon2);
return true;
}
F32 GjkCollisionState::distance(const MatrixF& a2w, const MatrixF& b2w,
const F32 dontCareDist, const MatrixF* _w2a, const MatrixF* _w2b)
{
num_iterations = 0;
MatrixF w2a,w2b;
if (_w2a == NULL || _w2b == NULL) {
w2a = a2w;
w2b = b2w;
w2a.inverse();
w2b.inverse();
}
else {
w2a = *_w2a;
w2b = *_w2b;
}
reset(a2w,b2w);
bits = 0;
all_bits = 0;
F32 mu = 0;
do {
nextBit();
VectorF va,sa;
w2a.mulV(-v,&va);
p[last] = a->support(va);
a2w.mulP(p[last],&sa);
VectorF vb,sb;
w2b.mulV(v,&vb);
q[last] = b->support(vb);
b2w.mulP(q[last],&sb);
VectorF w = sa - sb;
F32 nm = mDot(v, w) / dist;
if (nm > mu)
mu = nm;
if (mu > dontCareDist)
return mu;
if (mFabs(dist - mu) <= dist * rel_error)
return dist;
++num_iterations;
if (degenerate(w) || num_iterations > sIteration) {
++num_irregularities;
return dist;
}
y[last] = w;
all_bits = bits | last_bit;
if (!closest(v)) {
++num_irregularities;
return dist;
}
dist = v.len();
}
while (bits < 15 && dist > sTolerance) ;
if (bits == 15 && mu <= 0)
dist = 0;
return dist;
}
//----------------------------------------------------------------------------
inline void renderCross(const Point3F x)
{
glVertex3fv(x + VectorF(0,0,+0.1));
glVertex3fv(x + VectorF(0,0,-0.1));
glVertex3fv(x + VectorF(0,+0.1,0));
glVertex3fv(x + VectorF(0,-0.1,0));
glVertex3fv(x + VectorF(-0.1,0,0));
glVertex3fv(x + VectorF(+0.1,0,0));
}
void GjkCollisionState::render()
{
glBegin(GL_LINES);
glColor3f(1,1,0);
// Cross for each witness point
for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) {
Point3F x;
if (bits & bit) {
a->getTransform().mulP(p[i],&x);
renderCross(x);
b->getTransform().mulP(q[i],&x);
renderCross(x);
}
}
// Cross and line for closest points
Point3F sp,ep;
if (bits) {
Point3F p1,p2;
getClosestPoints(p1,p2);
a->getTransform().mulP(p1,&sp);
b->getTransform().mulP(p2,&ep);
}
else {
a->getTransform().mulP(p[0],&sp);
b->getTransform().mulP(q[0],&ep);
}
renderCross(sp);
renderCross(ep);
glVertex3fv(sp);
glVertex3fv(ep);
glEnd();
}

66
engine/collision/gjk.h Executable file
View File

@ -0,0 +1,66 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GJK_H_
#define _GJK_H_
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _CONVEX_H_
#include "collision/convex.h"
#endif
class Convex;
struct CollisionStateList;
struct Collision;
//----------------------------------------------------------------------------
struct GjkCollisionState: public CollisionState
{
/// @name Temporary values
/// @{
Point3F p[4]; ///< support points of object A in local coordinates
Point3F q[4]; ///< support points of object B in local coordinates
VectorF y[4]; ///< support points of A - B in world coordinates
S32 bits; ///< identifies current simplex
S32 all_bits; ///< all_bits = bits | last_bit
F32 det[16][4]; ///< cached sub-determinants
F32 dp[4][4]; ///< cached dot products
S32 last; ///< identifies last found support point
S32 last_bit; ///< last_bit = 1<<last
/// @}
///
void compute_det();
bool valid(int s);
void compute_vector(int bits, VectorF& v);
bool closest(VectorF& v);
bool degenerate(const VectorF& w);
void nextBit();
void swap();
void reset(const MatrixF& a2w, const MatrixF& b2w);
GjkCollisionState();
~GjkCollisionState();
void set(Convex* a,Convex* b,const MatrixF& a2w, const MatrixF& b2w);
void getCollisionInfo(const MatrixF& mat, Collision* info);
void getClosestPoints(Point3F& p1, Point3F& p2);
bool intersect(const MatrixF& a2w, const MatrixF& b2w);
F32 distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist,
const MatrixF* w2a = NULL, const MatrixF* _w2b = NULL);
void render();
};
#endif

View File

@ -0,0 +1,286 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "dgl/dgl.h"
#include "math/mMath.h"
#include "console/console.h"
#include "collision/optimizedPolyList.h"
#include "core/color.h"
//----------------------------------------------------------------------------
OptimizedPolyList::OptimizedPolyList()
{
VECTOR_SET_ASSOCIATION(mPolyList);
VECTOR_SET_ASSOCIATION(mVertexList);
VECTOR_SET_ASSOCIATION(mIndexList);
VECTOR_SET_ASSOCIATION(mPlaneList);
VECTOR_SET_ASSOCIATION(mEdgeList);
mIndexList.reserve(100);
}
OptimizedPolyList::~OptimizedPolyList()
{
mPolyList.clear();
mVertexList.clear();
mIndexList.clear();
mPlaneList.clear();
mEdgeList.clear();
}
//----------------------------------------------------------------------------
void OptimizedPolyList::clear()
{
// Only clears internal data
mPolyList.clear();
mVertexList.clear();
mIndexList.clear();
mPlaneList.clear();
mEdgeList.clear();
mPolyPlaneList.clear();
}
//----------------------------------------------------------------------------
U32 OptimizedPolyList::addPoint(const Point3F& p)
{
Point3F v;
v.x = p.x * mScale.x;
v.y = p.y * mScale.y;
v.z = p.z * mScale.z;
mMatrix.mulP(v);
for (U32 i = 0; i < mVertexList.size(); i++)
{
if (isEqual(v, mVertexList[i]))
return i;
}
// If we make it to here then we didn't find the vertex
mVertexList.push_back(v);
return mVertexList.size() - 1;
}
U32 OptimizedPolyList::addPlane(const PlaneF& plane)
{
PlaneF pln;
mPlaneTransformer.transform(plane, pln);
for (U32 i = 0; i < mPlaneList.size(); i++)
{
// The PlaneF == operator uses the Point3F == operator and
// doesn't check the d
if (isEqual(pln, mPlaneList[i]) &&
mFabs(pln.d - mPlaneList[i].d) < DEV)
return i;
}
// If we made it here then we didin't find the vertex
mPlaneList.push_back(pln);
return mPlaneList.size() - 1;
}
//----------------------------------------------------------------------------
void OptimizedPolyList::begin(U32 material,U32 surfaceKey)
{
mPolyList.increment();
Poly& poly = mPolyList.last();
poly.object = mCurrObject;
poly.material = material;
poly.vertexStart = mIndexList.size();
poly.surfaceKey = surfaceKey;
}
//----------------------------------------------------------------------------
void OptimizedPolyList::plane(U32 v1,U32 v2,U32 v3)
{
AssertFatal(v1 < mVertexList.size() && v2 < mVertexList.size() && v3 < mVertexList.size(),
"OptimizedPolyList::plane(): Vertex indices are larger than vertex list size");
mPolyList.last().plane = addPlane(PlaneF(mVertexList[v1], mVertexList[v2],mVertexList[v3]));
}
void OptimizedPolyList::plane(const PlaneF& p)
{
mPolyList.last().plane = addPlane(p);
}
void OptimizedPolyList::plane(const U32 index)
{
AssertFatal(index < mPlaneList.size(), "Out of bounds index!");
mPolyList.last().plane = index;
}
const PlaneF& OptimizedPolyList::getIndexedPlane(const U32 index)
{
AssertFatal(index < mPlaneList.size(), "Out of bounds index!");
return mPlaneList[index];
}
//----------------------------------------------------------------------------
void OptimizedPolyList::vertex(U32 vi)
{
mIndexList.push_back(vi);
}
//----------------------------------------------------------------------------
bool OptimizedPolyList::isEmpty() const
{
return !mPolyList.size();
}
void OptimizedPolyList::end()
{
Poly& poly = mPolyList.last();
poly.vertexCount = mIndexList.size() - poly.vertexStart;
}
void OptimizedPolyList::render()
{
if (mPolyList.size() == 0 || mVertexList.size() == 0)
return;
//glVertexPointer(3, GL_FLOAT,sizeof(Point3F), mVertexList.address());
//glEnableClientState(GL_VERTEX_ARRAY);
//Poly * p;
//for (p = mPolyList.begin(); p < mPolyList.end(); p++)
// glDrawElements(GL_POLYGON,p->vertexCount, GL_UNSIGNED_INT, &mIndexList[p->vertexStart]);
//glDisableClientState(GL_VERTEX_ARRAY);
// If you need normals
for (U32 i = 0; i < mPolyList.size(); i++)
{
if (mPolyList[i].vertexCount < 3)
continue;
ColorF color(gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f));
glColor3f(color.red, color.green, color.blue);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
glBegin(GL_TRIANGLES);
for (U32 j = 1; j < mPolyList[i].vertexCount - 1; j++)
{
U32 i0 = mIndexList[mPolyList[i].vertexStart];
U32 i1 = mIndexList[mPolyList[i].vertexStart + j];
U32 i2 = mIndexList[mPolyList[i].vertexStart + j + 1];
Point3F p0 = mVertexList[i0];
Point3F p1 = mVertexList[i1];
Point3F p2 = mVertexList[i2];
PlaneF normal = mPlaneList[mPolyList[i].plane];
glNormal3f(normal.x, normal.y, normal.z);
glVertex3f(p0.x, p0.y, p0.z);
glVertex3f(p1.x, p1.y, p1.z);
glVertex3f(p2.x, p2.y, p2.z);
}
glEnd();
}
}
void OptimizedPolyList::copyPolyToList(OptimizedPolyList* target, U32 pdx) const
{
target->mPolyList.increment();
OptimizedPolyList::Poly& tpoly = target->mPolyList.last();
tpoly.material = mPolyList[pdx].material;
tpoly.object = mPolyList[pdx].object;
tpoly.surfaceKey = mPolyList[pdx].surfaceKey;
tpoly.vertexCount = mPolyList[pdx].vertexCount;
PlaneF pln = mPlaneList[mPolyList[pdx].plane];
tpoly.plane = target->addPlane(pln);
tpoly.vertexStart = target->mIndexList.size();
for (U32 i = 0; i < mPolyList[pdx].vertexCount; i++)
{
U32 idx = mIndexList[mPolyList[pdx].vertexStart + i];
Point3F pt = mVertexList[idx];
target->mIndexList.increment();
target->mIndexList.last() = target->addPoint(pt);
}
}
void OptimizedPolyList::copyPolyToList(OptimizedPolyList* target, const FullPoly& poly) const
{
target->mPolyList.increment();
OptimizedPolyList::Poly& tpoly = target->mPolyList.last();
tpoly.material = poly.material;
tpoly.object = poly.object;
tpoly.surfaceKey = poly.surfaceKey;
tpoly.vertexCount = poly.indexes.size();
PlaneF pln = poly.plane;
tpoly.plane = target->addPlane(pln);
tpoly.vertexStart = target->mIndexList.size();
for (U32 i = 0; i < poly.indexes.size(); i++)
{
Point3F pt = poly.vertexes[poly.indexes[i]];
target->mIndexList.increment();
target->mIndexList.last() = target->addPoint(pt);
}
}
OptimizedPolyList::FullPoly OptimizedPolyList::getFullPoly(U32 pdx)
{
FullPoly ret;
OptimizedPolyList::Poly& poly = mPolyList[pdx];
ret.material = poly.material;
ret.object = poly.object;
ret.surfaceKey = poly.surfaceKey;
for (U32 i = 0; i < poly.vertexCount; i++)
{
U32 idx = mIndexList[poly.vertexStart + i];
Point3F& pt = mVertexList[idx];
S32 pdx = -1;
for (U32 j = 0; j < ret.vertexes.size(); j++)
{
if (pt == ret.vertexes[j])
{
pdx = j;
break;
}
}
if (pdx == -1)
{
pdx = ret.vertexes.size();
ret.vertexes.push_back(pt);
}
ret.indexes.push_back(pdx);
}
return ret;
}

View File

@ -0,0 +1,128 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _OPTIMIZEDPOLYLIST_H_
#define _OPTIMIZEDPOLYLIST_H_
#ifndef _ABSTRACTPOLYLIST_H_
#include "collision/abstractPolyList.h"
#endif
#define DEV 0.01
/// A concrete, renderable PolyList
///
/// This class is used to store geometry from a PolyList query.
///
/// It allows you to render this data, as well.
///
/// @see AbstractPolyList
class OptimizedPolyList : public AbstractPolyList
{
public:
struct Poly
{
S32 plane;
SceneObject* object;
S32 material;
U32 vertexStart;
U32 vertexCount;
U32 surfaceKey;
U32 triangleLightingStartIndex;
Poly() { plane = -1; vertexCount = 0; material = -1; };
};
struct FullPoly
{
PlaneF plane;
SceneObject* object;
S32 material;
U32 surfaceKey;
Vector<U32> indexes;
Vector<Point3F> vertexes;
};
enum
{
NonShared = 0,
Concave,
Convex
};
struct Edge
{
U32 type;
S32 vertexes[2];
S32 faces[2];
};
struct TriangleLighting
{
U32 lightMapId;
PlaneF lightMapEquationX;
PlaneF lightMapEquationY;
};
Vector<TriangleLighting> mTriangleLightingList;
const TriangleLighting *getTriangleLighting(const U32 index)
{
if(index >= mTriangleLightingList.size())
return NULL;
return &mTriangleLightingList[index];
}
typedef Vector<PlaneF> PlaneList;
typedef Vector<Point3F> VertexList;
typedef Vector<Poly> PolyList;
typedef Vector<U32> IndexList;
typedef Vector<Edge> EdgeList;
PolyList mPolyList;
VertexList mVertexList;
IndexList mIndexList;
PlaneList mPlaneList;
EdgeList mEdgeList;
PlaneList mPolyPlaneList;
public:
OptimizedPolyList();
~OptimizedPolyList();
void clear();
// Virtual methods
U32 addPoint(const Point3F& p);
U32 addPlane(const PlaneF& plane);
void begin(U32 material,U32 surfaceKey);
void plane(U32 v1,U32 v2,U32 v3);
void plane(const PlaneF& p);
void plane(const U32 index);
void vertex(U32 vi);
void end();
inline bool isEqual(Point3F& a, Point3F& b)
{
return( ( mFabs( a.x - b.x ) < DEV ) &&
( mFabs( a.y - b.y ) < DEV ) &&
( mFabs( a.z - b.z ) < DEV ) );
}
void copyPolyToList(OptimizedPolyList* target, U32 pdx) const;
void copyPolyToList(OptimizedPolyList* target, const FullPoly& poly) const;
FullPoly getFullPoly(U32 pdx);
void render();
bool isEmpty() const;
protected:
const PlaneF& getIndexedPlane(const U32 index);
};
#endif // _OPTIMIZEDPOLYLIST_H_

View File

@ -0,0 +1,117 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "dgl/dgl.h"
#include "math/mMath.h"
#include "console/console.h"
#include "collision/planeExtractor.h"
//----------------------------------------------------------------------------
// Plane matching parameters
static F32 NormalEpsilon = 0.93969; // 20 deg.
static F32 DistanceEpsilon = 100;
//----------------------------------------------------------------------------
PlaneExtractorPolyList::PlaneExtractorPolyList()
{
VECTOR_SET_ASSOCIATION(mVertexList);
VECTOR_SET_ASSOCIATION(mPolyPlaneList);
}
PlaneExtractorPolyList::~PlaneExtractorPolyList()
{
}
//----------------------------------------------------------------------------
void PlaneExtractorPolyList::clear()
{
mVertexList.clear();
mPolyPlaneList.clear();
}
U32 PlaneExtractorPolyList::addPoint(const Point3F& p)
{
mVertexList.increment();
Point3F& v = mVertexList.last();
v.x = p.x * mScale.x;
v.y = p.y * mScale.y;
v.z = p.z * mScale.z;
mMatrix.mulP(v);
return mVertexList.size() - 1;
}
U32 PlaneExtractorPolyList::addPlane(const PlaneF& plane)
{
mPolyPlaneList.increment();
mPlaneTransformer.transform(plane, mPolyPlaneList.last());
return mPolyPlaneList.size() - 1;
}
//----------------------------------------------------------------------------
void PlaneExtractorPolyList::plane(U32 v1,U32 v2,U32 v3)
{
mPlaneList->last().set(mVertexList[v1],
mVertexList[v2],mVertexList[v3]);
}
void PlaneExtractorPolyList::plane(const PlaneF& p)
{
mPlaneTransformer.transform(p, mPlaneList->last());
}
void PlaneExtractorPolyList::plane(const U32 index)
{
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
mPlaneList->last() = mPolyPlaneList[index];
}
const PlaneF& PlaneExtractorPolyList::getIndexedPlane(const U32 index)
{
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
return mPolyPlaneList[index];
}
//----------------------------------------------------------------------------
bool PlaneExtractorPolyList::isEmpty() const
{
return true;
}
void PlaneExtractorPolyList::begin(U32,U32)
{
mPlaneList->increment();
}
void PlaneExtractorPolyList::end()
{
// See if there are any duplicate planes
PlaneF &plane = mPlaneList->last();
PlaneF *ptr = mPlaneList->begin();
for (; ptr != &plane; ptr++)
if (mFabs(ptr->d - plane.d) < DistanceEpsilon &&
mDot(*ptr,plane) > NormalEpsilon) {
mPlaneList->decrement();
return;
}
}
void PlaneExtractorPolyList::vertex(U32)
{
}
void PlaneExtractorPolyList::render()
{
}

View File

@ -0,0 +1,62 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _PLANEEXTRACTOR_H_
#define _PLANEEXTRACTOR_H_
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _ABSTRACTPOLYLIST_H_
#include "collision/abstractPolyList.h"
#endif
//----------------------------------------------------------------------------
/// Fill a Vector<PlaneF> with the planes from the geometry passed through this
/// PolyList.
///
/// @see AbstractPolyList
class PlaneExtractorPolyList: public AbstractPolyList
{
void memcpy(U32* d, U32* s,U32 size);
public:
// Internal data
typedef Vector<Point3F> VertexList;
VertexList mVertexList;
Vector<PlaneF> mPolyPlaneList;
// Set by caller
Vector<PlaneF>* mPlaneList;
//
PlaneExtractorPolyList();
~PlaneExtractorPolyList();
void clear();
void render();
// Virtual methods
bool isEmpty() const;
U32 addPoint(const Point3F& p);
U32 addPlane(const PlaneF& plane);
void begin(U32 material,U32 surfaceKey);
void plane(U32 v1,U32 v2,U32 v3);
void plane(const PlaneF& p);
void plane(const U32 index);
void vertex(U32 vi);
void end();
protected:
const PlaneF& getIndexedPlane(const U32 index);
};
#endif

150
engine/collision/polyhedron.cc Executable file
View File

@ -0,0 +1,150 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "dgl/dgl.h"
#include "console/console.h"
#include "collision/polyhedron.h"
//----------------------------------------------------------------------------
void Polyhedron::buildBox(const MatrixF& transform,const Box3F& box)
{
// Box is assumed to be axis aligned in the source space.
// Transform into geometry space
Point3F xvec,yvec,zvec,min;
transform.getColumn(0,&xvec);
xvec *= box.len_x();
transform.getColumn(1,&yvec);
yvec *= box.len_y();
transform.getColumn(2,&zvec);
zvec *= box.len_z();
transform.mulP(box.min,&min);
// Initial vertices
pointList.setSize(8);
pointList[0] = min;
pointList[1] = min + yvec;
pointList[2] = min + xvec + yvec;
pointList[3] = min + xvec;
pointList[4] = pointList[0] + zvec;
pointList[5] = pointList[1] + zvec;
pointList[6] = pointList[2] + zvec;
pointList[7] = pointList[3] + zvec;
// Initial faces
planeList.setSize(6);
planeList[0].set(pointList[0],xvec);
planeList[0].invert();
planeList[1].set(pointList[2],yvec);
planeList[2].set(pointList[2],xvec);
planeList[3].set(pointList[0],yvec);
planeList[3].invert();
planeList[4].set(pointList[0],zvec);
planeList[4].invert();
planeList[5].set(pointList[4],zvec);
// The edges are constructed so that the vertices
// are oriented clockwise for face[0]
edgeList.setSize(12);
Edge* edge = edgeList.begin();
S32 nextEdge = 0;
for (int i = 0; i < 4; i++) {
S32 n = ( i + 1 ) & 0x3;
S32 p = ( i + 3 ) & 0x3;
edge->vertex[0] = i;
edge->vertex[1] = n;
edge->face[0] = i;
edge->face[1] = 4;
edge++;
edge->vertex[0] = 4 + i;
edge->vertex[1] = 4 + n;
edge->face[0] = 5;
edge->face[1] = i;
edge++;
edge->vertex[0] = i;
edge->vertex[1] = 4 + i;
edge->face[0] = p;
edge->face[1] = i;
edge++;
}
}
//----------------------------------------------------------------------------
void Polyhedron::render()
{
glVertexPointer(3,GL_FLOAT,sizeof(Point3F),pointList.address());
glEnableClientState(GL_VERTEX_ARRAY);
glColor3f(1, 0, 1);
for (S32 e = 0; e < edgeList.size(); e++)
glDrawElements(GL_LINES,2,GL_UNSIGNED_INT,&edgeList[e].vertex);
for (U32 i = 0; i < planeList.size(); i++) {
Point3F origin(0, 0, 0);
U32 num = 0;
for (U32 j = 0; j < edgeList.size(); j++) {
if (edgeList[j].face[0] == i || edgeList[j].face[1] == i) {
origin += pointList[edgeList[j].vertex[0]];
origin += pointList[edgeList[j].vertex[1]];
num += 2;
}
}
origin /= F32(num);
glColor3f(1, 1, 1);
glBegin(GL_LINES);
glVertex3fv(origin);
glVertex3f(origin.x + planeList[i].x * 0.2,
origin.y + planeList[i].y * 0.2,
origin.z + planeList[i].z * 0.2);
glEnd();
}
glDisableClientState(GL_VERTEX_ARRAY);
}
void Polyhedron::render(const VectorF& vec,F32 time)
{
bool faceVisible[50];
for (int f = 0; f < planeList.size(); f++)
faceVisible[f] = mDot(planeList[f],vec) > 0;
VectorF tvec = vec;
tvec *= time;
for (int e = 0; e < edgeList.size(); e++) {
Polyhedron::Edge const& edge = edgeList[e];
if (faceVisible[edge.face[0]] || faceVisible[edge.face[1]]) {
Point3F pp;
glBegin(GL_LINE_LOOP);
glColor3f(0.5,0.5,0.5);
const Point3F& p1 = pointList[edge.vertex[0]];
const Point3F& p2 = pointList[edge.vertex[1]];
glVertex3fv(p1);
glVertex3fv(p2);
pp = p2 + vec;
glVertex3fv(pp);
pp = p1 + vec;
glVertex3fv(pp);
glEnd();
if (time <= 1.0) {
glBegin(GL_LINES);
glColor3f(0.5,0.5,0);
pp = pointList[edge.vertex[0]];
pp += tvec;
glVertex3fv(pp);
pp = pointList[edge.vertex[1]];
pp += tvec;
glVertex3fv(pp);
glEnd();
}
}
}
}

45
engine/collision/polyhedron.h Executable file
View File

@ -0,0 +1,45 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _POLYHEDRON_H_
#define _POLYHEDRON_H_
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
//----------------------------------------------------------------------------
struct Polyhedron
{
struct Edge
{
// Edge vertices must be oriented clockwise
// for face[0]
U32 face[2];
U32 vertex[2];
};
Vector<Point3F> pointList;
Vector<PlaneF> planeList;
Vector<Edge> edgeList;
// Misc support methods
Polyhedron() {
VECTOR_SET_ASSOCIATION(pointList);
VECTOR_SET_ASSOCIATION(planeList);
VECTOR_SET_ASSOCIATION(edgeList);
}
void buildBox(const MatrixF& mat, const Box3F& box);
void render();
void render(const VectorF& vec,F32 time);
};
#endif

433
engine/collision/polytope.cc Executable file
View File

@ -0,0 +1,433 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "dgl/dgl.h"
#include "math/mMath.h"
#include "collision/collision.h"
#include "collision/polytope.h"
//----------------------------------------------------------------------------
Polytope::Polytope()
{
VECTOR_SET_ASSOCIATION(mEdgeList);
VECTOR_SET_ASSOCIATION(mFaceList);
VECTOR_SET_ASSOCIATION(mVertexList);
VECTOR_SET_ASSOCIATION(mVolumeList);
mVertexList.reserve(100);
mFaceList.reserve(200);
mEdgeList.reserve(100);
mVolumeList.reserve(20);
sideCount = 0;
}
//----------------------------------------------------------------------------
// Box should be axis aligned in the transform space provided.
void Polytope::buildBox(const MatrixF& transform,const Box3F& box)
{
// Box is assumed to be axis aligned in the source space.
// Transform into geometry space
Point3F xvec,yvec,zvec,min;
transform.getColumn(0,&xvec);
xvec *= box.len_x();
transform.getColumn(1,&yvec);
yvec *= box.len_y();
transform.getColumn(2,&zvec);
zvec *= box.len_z();
transform.mulP(box.min,&min);
// Initial vertices
mVertexList.setSize(8);
mVertexList[0].point = min;
mVertexList[1].point = min + yvec;
mVertexList[2].point = min + xvec + yvec;
mVertexList[3].point = min + xvec;
mVertexList[4].point = mVertexList[0].point + zvec;
mVertexList[5].point = mVertexList[1].point + zvec;
mVertexList[6].point = mVertexList[2].point + zvec;
mVertexList[7].point = mVertexList[3].point + zvec;
S32 i;
for (i = 0; i < 8; i++)
mVertexList[i].side = 0;
// Initial faces
mFaceList.setSize(6);
for (S32 f = 0; f < 6; f++) {
Face& face = mFaceList[f];
face.original = true;
face.vertex = 0;
}
mFaceList[0].plane.set(mVertexList[0].point,xvec);
mFaceList[0].plane.invert();
mFaceList[1].plane.set(mVertexList[2].point,yvec);
mFaceList[2].plane.set(mVertexList[2].point,xvec);
mFaceList[3].plane.set(mVertexList[0].point,yvec);
mFaceList[3].plane.invert();
mFaceList[4].plane.set(mVertexList[0].point,zvec);
mFaceList[4].plane.invert();
mFaceList[5].plane.set(mVertexList[4].point,zvec);
// Initial edges
mEdgeList.setSize(12);
Edge* edge = mEdgeList.begin();
S32 nextEdge = 0;
for (i = 0; i < 4; i++) {
S32 n = (i == 3)? 0: i + 1;
S32 p = (i == 0)? 3: i - 1;
edge->vertex[0] = i;
edge->vertex[1] = n;
edge->face[0] = i;
edge->face[1] = 4;
edge->next = ++nextEdge;
edge++;
edge->vertex[0] = 4 + i;
edge->vertex[1] = 4 + n;
edge->face[0] = i;
edge->face[1] = 5;
edge->next = ++nextEdge;
edge++;
edge->vertex[0] = i;
edge->vertex[1] = 4 + i;
edge->face[0] = i;
edge->face[1] = p;
edge->next = ++nextEdge;
edge++;
}
edge[-1].next = -1;
// Volume
mVolumeList.setSize(1);
Volume& volume = mVolumeList.last();
volume.edgeList = 0;
volume.material = -1;
volume.object = 0;
sideCount = 0;
}
//----------------------------------------------------------------------------
void Polytope::intersect(SimObject* object,const BSPNode* root)
{
AssertFatal(mVolumeList.size() > 0,"Polytope::intersect: Missing initial volume");
// Always clips the first volume against the BSP
VolumeStack stack;
stack.reserve(50);
stack.increment();
stack.last().edgeList = mVolumeList[0].edgeList;
stack.last().node = root;
while (!stack.empty()) {
StackElement volume = stack.last();
stack.pop_back();
// If it's a solid node, export the volume
const BSPNode* node = volume.node;
if (!node->backNode && !node->frontNode) {
mVolumeList.increment();
Volume& vol = mVolumeList.last();
vol.object = object;
vol.material = node->material;
vol.edgeList = volume.edgeList;
continue;
}
// New front and back faces
mFaceList.increment(2);
Face& frontFace = mFaceList.last();
Face& backFace = *(&frontFace - 1);
backFace.original = frontFace.original = false;
backFace.vertex = frontFace.vertex = 0;
backFace.plane = frontFace.plane = node->plane;
backFace.plane.invert();
// New front and back volumes
StackElement frontVolume,backVolume;
frontVolume.edgeList = backVolume.edgeList = -1;
const PlaneF& plane = node->plane;
S32 startVertex = mVertexList.size();
// Test & clip all the edges
S32 sideBase = ++sideCount << 1;
for (S32 i = volume.edgeList; i >= 0; i = mEdgeList[i].next) {
// Copy into tmp first to avoid problems with the array.
Edge edge = mEdgeList[i];
Vertex& v0 = mVertexList[edge.vertex[0]];
if (v0.side < sideBase)
v0.side = sideBase + ((plane.distToPlane(v0.point) >= 0)? 0: 1);
Vertex& v1 = mVertexList[edge.vertex[1]];
if (v1.side < sideBase)
v1.side = sideBase + ((plane.distToPlane(v1.point) >= 0)? 0: 1);
if (v0.side != v1.side) {
S32 s = v0.side - sideBase;
intersect(plane,v0.point,v1.point);
// Split the edge into each volume
mEdgeList.increment(2);
Edge& e0 = mEdgeList.last();
e0.next = frontVolume.edgeList;
frontVolume.edgeList = mEdgeList.size() - 1;
Edge& e1 = *(&e0 - 1);
e1.next = backVolume.edgeList;
backVolume.edgeList = frontVolume.edgeList - 1;
e0.vertex[0] = edge.vertex[s];
e1.vertex[0] = edge.vertex[s ^ 1];
e0.vertex[1] = e1.vertex[1] = mVertexList.size() - 1;
e0.face[0] = e1.face[0] = edge.face[0];
e0.face[1] = e1.face[1] = edge.face[1];
// Add new edges on the plane, one to each volume
for (S32 f = 0; f < 2; f++) {
Face& face = mFaceList[edge.face[f]];
if (face.vertex < startVertex)
face.vertex = mVertexList.size() - 1;
else {
mEdgeList.increment(2);
Edge& e0 = mEdgeList.last();
e0.next = frontVolume.edgeList;
frontVolume.edgeList = mEdgeList.size() - 1;
Edge& e1 = *(&e0 - 1);
e1.next = backVolume.edgeList;
backVolume.edgeList = frontVolume.edgeList - 1;
e1.vertex[0] = e0.vertex[0] = face.vertex;
e1.vertex[1] = e0.vertex[1] = mVertexList.size() - 1;
e1.face[0] = e0.face[0] = edge.face[f];
e1.face[1] = mFaceList.size() - 1;
e0.face[1] = e1.face[1] - 1;
}
}
}
else
if (v0.side == sideBase) {
mEdgeList.push_back(edge);
Edge& ne = mEdgeList.last();
ne.next = frontVolume.edgeList;
frontVolume.edgeList = mEdgeList.size() - 1;
}
else {
mEdgeList.push_back(edge);
Edge& ne = mEdgeList.last();
ne.next = backVolume.edgeList;
backVolume.edgeList = mEdgeList.size() - 1;
}
}
// Push the front and back nodes
if (node->frontNode && frontVolume.edgeList >= 0) {
frontVolume.node = node->frontNode;
stack.push_back(frontVolume);
}
if (node->backNode && backVolume.edgeList >= 0) {
backVolume.node = node->backNode;
stack.push_back(backVolume);
}
}
}
//----------------------------------------------------------------------------
bool Polytope::intersect(const PlaneF& plane,const Point3F& sp,const Point3F& ep)
{
// If den == 0 then the line and plane are parallel.
F32 den;
Point3F dt = ep - sp;
if ((den = plane.x * dt.x + plane.y * dt.y + plane.z * dt.z) == 0)
return false;
mVertexList.increment();
Vertex& v = mVertexList.last();
F32 s = -(plane.x * sp.x + plane.y * sp.y + plane.z * sp.z + plane.d) / den;
v.point.x = sp.x + dt.x * s;
v.point.y = sp.y + dt.y * s;
v.point.z = sp.z + dt.z * s;
v.side = 0;
return true;
}
//----------------------------------------------------------------------------
void Polytope::render()
{
glVertexPointer(3,GL_FLOAT,sizeof(Vertex),mVertexList.address());
glEnableClientState(GL_VERTEX_ARRAY);
glColor3f(0.5, 0.5, 0.5);
for (VolumeList::iterator itr = mVolumeList.begin();
itr < mVolumeList.end(); itr++) {
for (S32 e = itr->edgeList; e >= 0; e = mEdgeList[e].next)
glDrawElements(GL_LINES,2,GL_UNSIGNED_INT,&mEdgeList[e].vertex);
glColor3f(1, 1, 1);
}
glDisableClientState(GL_VERTEX_ARRAY);
}
//----------------------------------------------------------------------------
void Polytope::extrudeFace(int faceIdx,const VectorF& vec,Polytope* out)
{
// Assumes the face belongs to the first volume.
out->mVertexList.clear();
out->mFaceList.clear();
out->mEdgeList.clear();
out->mVolumeList.clear();
sideCount++;
// Front & end faces
Face nface;
nface.original = true;
nface.vertex = 0;
nface.plane = mFaceList[faceIdx].plane;
out->mFaceList.setSize(2);
out->mFaceList[0] = out->mFaceList[1] = nface;
out->mFaceList[0].plane.invert();
for (S32 e = mVolumeList[0].edgeList; e >= 0; e = mEdgeList[e].next) {
Edge& edge = mEdgeList[e];
if (edge.face[0] == faceIdx || edge.face[1] == faceIdx) {
// Build face for this edge
// Should think about calulating the plane
S32 fi = out->mFaceList.size();
out->mFaceList.push_back(nface);
// Reserve 4 entries to make sure the ve[] pointers
// into the list don't get invalidated.
out->mEdgeList.reserve(out->mEdgeList.size() + 4);
Edge* ve[2];
// Build edges for each vertex
for (S32 v = 0; v < 2; v++) {
if (mVertexList[edge.vertex[v]].side < sideCount) {
mVertexList[edge.vertex[v]].side = sideCount + out->mEdgeList.size();
out->mVertexList.increment(2);
out->mVertexList.end()[-1] =
out->mVertexList.end()[-2] =
mVertexList[edge.vertex[v]];
out->mVertexList.last().point += vec;
out->mEdgeList.increment();
Edge& ne = out->mEdgeList.last();
ne.next = out->mEdgeList.size();
ne.vertex[1] = out->mVertexList.size() - 1;
ne.vertex[0] = ne.vertex[1] - 1;
ne.face[0] = ne.face[1] = -1;
ve[v] = &ne;
}
else {
S32 ei = mVertexList[edge.vertex[v]].side - sideCount;
ve[v] = &out->mEdgeList[ei];
}
// Edge should share this face
if (ve[v]->face[0] == -1)
ve[v]->face[0] = fi;
else
ve[v]->face[1] = fi;
}
// Build parallel edges
out->mEdgeList.increment(2);
for (S32 i = 0; i < 2; i++ ) {
Edge& ne = out->mEdgeList.end()[i - 2];
ne.next = out->mEdgeList.size() - 1 + i;
ne.vertex[0] = ve[0]->vertex[i];
ne.vertex[1] = ve[1]->vertex[i];
ne.face[0] = i;
ne.face[1] = fi;
}
}
}
out->mEdgeList.last().next = -1;
out->mVolumeList.increment();
Volume& nv = out->mVolumeList.last();
nv.edgeList = 0;
nv.object = 0;
nv.material = -1;
}
//----------------------------------------------------------------------------
bool Polytope::findCollision(const VectorF& vec,Polytope::Collision *best)
{
if (mVolumeList.size() <= 1)
return false;
if (!best->object)
best->distance = 1.0E30;
int bestVertex = -1;
Volume* bestVolume;
sideCount++;
// Find the closest point
for (Volume* vol = mVolumeList.begin() + 1;
vol < mVolumeList.end(); vol++) {
for (S32 e = vol->edgeList; e >= 0; e = mEdgeList[e].next) {
Edge& edge = mEdgeList[e];
if (mFaceList[edge.face[0]].original &&
mFaceList[edge.face[1]].original)
continue;
for (S32 v = 0; v < 2; v++) {
S32 vi = edge.vertex[v];
Vertex& vr = mVertexList[vi];
if (vr.side != sideCount) {
vr.side = sideCount;
F32 dist = mDot(vr.point,vec);
if (dist < best->distance) {
best->distance = dist;
bestVertex = vi;
bestVolume = vol;
}
}
}
}
}
if (bestVertex == -1)
return false;
// Fill in the return value
best->point = mVertexList[bestVertex].point;
best->object = bestVolume->object;
best->material = bestVolume->material;
// Pick the best face
F32 bestFaceDot = 1;
for (S32 e = bestVolume->edgeList; e >= 0; e = mEdgeList[e].next) {
Edge& edge = mEdgeList[e];
if (edge.vertex[0] == bestVertex || edge.vertex[1] == bestVertex) {
for (S32 f = 0; f < 2; f++) {
Face& tf = mFaceList[edge.face[f]];
F32 fd = mDot(tf.plane,vec);
if (fd < bestFaceDot) {
bestFaceDot = fd;
best->plane = tf.plane;
}
}
}
}
return true;
}

96
engine/collision/polytope.h Executable file
View File

@ -0,0 +1,96 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _POLYTOPE_H_
#define _POLYTOPE_H_
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
//----------------------------------------------------------------------------
class SimObject;
//----------------------------------------------------------------------------
class Polytope
{
// Convex Polyhedron
public:
struct Vertex {
Point3F point;
/// Temp BSP clip info
S32 side;
};
struct Edge {
S32 vertex[2];
S32 face[2];
S32 next;
};
struct Face {
PlaneF plane;
S32 original;
/// Temp BSP clip info
S32 vertex;
};
struct Volume
{
S32 edgeList;
S32 material;
SimObject* object;
};
struct StackElement
{
S32 edgeList;
const BSPNode *node;
};
struct Collision {
SimObject* object;
S32 material;
PlaneF plane;
Point3F point;
F32 distance;
Collision()
{
object = NULL;
distance = 0.0;
}
};
typedef Vector<Edge> EdgeList;
typedef Vector<Face> FaceList;
typedef Vector<Vertex> VertexList;
typedef Vector<Volume> VolumeList;
typedef Vector<StackElement> VolumeStack;
//
S32 sideCount;
EdgeList mEdgeList;
FaceList mFaceList;
VertexList mVertexList;
VolumeList mVolumeList;
private:
bool intersect(const PlaneF& plane,const Point3F& sp,const Point3F& ep);
public:
//
Polytope();
void buildBox(const MatrixF& transform,const Box3F& box);
void intersect(SimObject*, const BSPNode* node);
inline bool didIntersect() { return mVolumeList.size() > 1; }
void extrudeFace(int fi,const VectorF& vec,Polytope* out);
bool findCollision(const VectorF& vec,Polytope::Collision *best);
void render();
};
#endif