Initial commit
This commit is contained in:
97
engine/collision/abstractPolyList.cc
Executable file
97
engine/collision/abstractPolyList.cc
Executable 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;
|
||||
}
|
||||
|
241
engine/collision/abstractPolyList.h
Executable file
241
engine/collision/abstractPolyList.h
Executable 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
199
engine/collision/boxConvex.cc
Executable 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"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
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 },
|
||||
};
|
||||
|
||||
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
46
engine/collision/boxConvex.h
Executable 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
|
268
engine/collision/clippedPolyList.cc
Executable file
268
engine/collision/clippedPolyList.cc
Executable file
@ -0,0 +1,268 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
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);
|
||||
}
|
84
engine/collision/clippedPolyList.h
Executable file
84
engine/collision/clippedPolyList.h
Executable file
@ -0,0 +1,84 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// 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;
|
||||
};
|
||||
|
||||
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
|
65
engine/collision/collision.h
Executable file
65
engine/collision/collision.h
Executable file
@ -0,0 +1,65 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
};
|
||||
|
||||
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
|
149
engine/collision/concretePolyList.cc
Executable file
149
engine/collision/concretePolyList.cc
Executable 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);
|
||||
}
|
66
engine/collision/concretePolyList.h
Executable file
66
engine/collision/concretePolyList.h
Executable 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_
|
641
engine/collision/convex.cc
Executable file
641
engine/collision/convex.cc
Executable file
@ -0,0 +1,641 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
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
249
engine/collision/convex.h
Executable 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
1290
engine/collision/convexBrush.cc
Executable file
File diff suppressed because it is too large
Load Diff
127
engine/collision/convexBrush.h
Executable file
127
engine/collision/convexBrush.h
Executable 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
|
903
engine/collision/depthSortList.cc
Executable file
903
engine/collision/depthSortList.cc
Executable file
@ -0,0 +1,903 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
S32 j;
|
||||
for (j=0; j<sourceList->size(); j++)
|
||||
{
|
||||
Poly & toCut = (*sourceList)[j];
|
||||
cookieCutter(cutter,toCut,*scraps,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);
|
||||
|
||||
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
108
engine/collision/depthSortList.h
Executable 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
|
267
engine/collision/earlyOutPolyList.cc
Executable file
267
engine/collision/earlyOutPolyList.cc
Executable 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++;
|
||||
}
|
||||
|
80
engine/collision/earlyOutPolyList.h
Executable file
80
engine/collision/earlyOutPolyList.h
Executable 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_
|
492
engine/collision/extrudedPolyList.cc
Executable file
492
engine/collision/extrudedPolyList.cc
Executable file
@ -0,0 +1,492 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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"
|
||||
|
||||
|
||||
const F32 sgFrontEpsilon = 0.01;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// use table for U64 shifts of the form: 1 << N
|
||||
// because compiler makes it a function call if done directly...
|
||||
U32 U32leftShift[33] =
|
||||
{
|
||||
U32(1) << 0,
|
||||
U32(1) << 1,
|
||||
U32(1) << 2,
|
||||
U32(1) << 3,
|
||||
U32(1) << 4,
|
||||
U32(1) << 5,
|
||||
U32(1) << 6,
|
||||
U32(1) << 7,
|
||||
U32(1) << 8,
|
||||
U32(1) << 9,
|
||||
|
||||
U32(1) << 10,
|
||||
U32(1) << 11,
|
||||
U32(1) << 12,
|
||||
U32(1) << 13,
|
||||
U32(1) << 14,
|
||||
U32(1) << 15,
|
||||
U32(1) << 16,
|
||||
U32(1) << 17,
|
||||
U32(1) << 18,
|
||||
U32(1) << 19,
|
||||
|
||||
U32(1) << 20,
|
||||
U32(1) << 21,
|
||||
U32(1) << 22,
|
||||
U32(1) << 23,
|
||||
U32(1) << 24,
|
||||
U32(1) << 25,
|
||||
U32(1) << 26,
|
||||
U32(1) << 27,
|
||||
U32(1) << 28,
|
||||
U32(1) << 29,
|
||||
|
||||
U32(1) << 30,
|
||||
U32(1) << 31,
|
||||
U32(0) // one more for good measure
|
||||
};
|
||||
|
||||
// Minimum distance from a face
|
||||
F32 ExtrudedPolyList::FaceEpsilon = 0.01;
|
||||
|
||||
// Value used to compare collision times
|
||||
F32 ExtrudedPolyList::EqualEpsilon = 0.0001;
|
||||
|
||||
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,0,0);
|
||||
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();
|
||||
mFaceShift = 0;
|
||||
|
||||
// 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.faceShift = FaceEpsilon / dot;
|
||||
eface.maxDistance = dot;
|
||||
eface.plane = face;
|
||||
eface.planeMask = U32leftShift[mPlaneList.size()]; // U64(1) << 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 = U32leftShift[mPlaneList.size()-2]; // U64(1) << (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()
|
||||
{
|
||||
// Called after all the polys have been added.
|
||||
// There was a reason for doing it here instead of subtracting
|
||||
// the face Epsilon (faceShift) when the closest point is calculated,
|
||||
// but I can't remember what it is...
|
||||
if (mCollisionList->count) {
|
||||
mCollisionList->t -= mFaceShift;
|
||||
if (mCollisionList->t > 1)
|
||||
mCollisionList->t = 1;
|
||||
else
|
||||
if (mCollisionList->t < 0)
|
||||
mCollisionList->t = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
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 += 2)
|
||||
{
|
||||
F32 dist = mPlaneList[i].distToPlane(v.point);
|
||||
if (dist >= sgFrontEpsilon)
|
||||
v.mask |= U32leftShift[i]; // U64(1) << i;
|
||||
else
|
||||
v.mask |= U32leftShift[i+1]; // U64(2) << 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);
|
||||
}
|
||||
|
||||
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
|
||||
if (mCollisionList->count >= CollisionList::MaxCollisions ||
|
||||
mDot(mPoly.plane, mNormalVelocity) > 0)
|
||||
return;
|
||||
|
||||
// Test the mPoly against the planes each extruded face.
|
||||
U32 cFaceCount = 0;
|
||||
ExtrudedFace* cFace[30];
|
||||
bool cEdgeColl[30];
|
||||
ExtrudedFace* face = mExtrudedList.begin();
|
||||
ExtrudedFace* end = mExtrudedList.end();
|
||||
for (; face != end; face++)
|
||||
{
|
||||
if (face->active && (face->faceDot = -mDot(face->plane,mPoly.plane)) > 0)
|
||||
{
|
||||
if (testPoly(*face)) {
|
||||
cFace[cFaceCount] = face;
|
||||
cEdgeColl[cFaceCount++] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!cFaceCount)
|
||||
{
|
||||
face = mExtrudedList.begin();
|
||||
end = mExtrudedList.end();
|
||||
for (; face != end; face++)
|
||||
{
|
||||
if (face->active && (face->faceDot = -mDot(face->plane,mPoly.plane)) <= 0)
|
||||
{
|
||||
if (testPoly(*face)) {
|
||||
cFace[cFaceCount] = face;
|
||||
cEdgeColl[cFaceCount++] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!cFaceCount)
|
||||
return;
|
||||
|
||||
// Pick the best collision face
|
||||
face = cFace[0];
|
||||
bool edge = cEdgeColl[0];
|
||||
for (U32 f = 1; f < cFaceCount; f++)
|
||||
{
|
||||
if (cFace[f]->faceDot > face->faceDot)
|
||||
{
|
||||
face = cFace[f];
|
||||
edge = cEdgeColl[f];
|
||||
}
|
||||
}
|
||||
// Add it to the collision list if it's better and/or equal
|
||||
// to our current best.
|
||||
if (face->time <= mCollisionList->t + EqualEpsilon) {
|
||||
if (face->time < mCollisionList->t - EqualEpsilon) {
|
||||
mFaceShift = face->faceShift;
|
||||
mCollisionList->t = face->time;
|
||||
mCollisionList->count = 0;
|
||||
mCollisionList->maxHeight = face->height;
|
||||
}
|
||||
else {
|
||||
if (face->height > mCollisionList->maxHeight)
|
||||
mCollisionList->maxHeight = face->height;
|
||||
if (face->faceShift > mFaceShift)
|
||||
mFaceShift = face->faceShift;
|
||||
}
|
||||
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 = U32leftShift[p]; // U64(1) << p;
|
||||
U32 newStart = mIndexList.size();
|
||||
|
||||
// Only test against this plane if we have something
|
||||
// on both sides
|
||||
if (face.planeMask & crossMask & pmask) {
|
||||
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++) {
|
||||
U32 mask = U32leftShift[i]; // U64(1) << i;
|
||||
if (face.planeMask & mask &&
|
||||
mPlaneList[i].distToPlane(iv.point) > 0) {
|
||||
iv.mask = mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 = 1E30;
|
||||
F32 height = -1E30;
|
||||
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)? 0: 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;
|
||||
}
|
||||
|
||||
// Remove temporary data
|
||||
mVertexList.setSize(oVertexSize);
|
||||
mIndexList.setSize(oIndexSize);
|
||||
|
||||
// Add it to the collision list if it's better and/or equal
|
||||
// to our current best.
|
||||
if (bd < face.maxDistance + FaceEpsilon) {
|
||||
face.time = bd / face.maxDistance;
|
||||
face.height = height;
|
||||
face.point = bp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ExtrudedPolyList::render()
|
||||
{
|
||||
if (!mCollisionList)
|
||||
return;
|
||||
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(1,1,0);
|
||||
|
||||
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, 0, 0);
|
||||
clearInterestNormal();
|
||||
}
|
||||
}
|
||||
|
||||
|
107
engine/collision/extrudedPolyList.h
Executable file
107
engine/collision/extrudedPolyList.h
Executable 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
422
engine/collision/gjk.cc
Executable 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
66
engine/collision/gjk.h
Executable 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
|
286
engine/collision/optimizedPolyList.cc
Executable file
286
engine/collision/optimizedPolyList.cc
Executable 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;
|
||||
}
|
128
engine/collision/optimizedPolyList.h
Executable file
128
engine/collision/optimizedPolyList.h
Executable 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::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_
|
117
engine/collision/planeExtractor.cc
Executable file
117
engine/collision/planeExtractor.cc
Executable 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()
|
||||
{
|
||||
}
|
||||
|
62
engine/collision/planeExtractor.h
Executable file
62
engine/collision/planeExtractor.h
Executable 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
150
engine/collision/polyhedron.cc
Executable 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 == 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++;
|
||||
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
45
engine/collision/polyhedron.h
Executable 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
433
engine/collision/polytope.cc
Executable 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] = ≠
|
||||
}
|
||||
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
96
engine/collision/polytope.h
Executable 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
|
Reference in New Issue
Block a user