tge/engine/collision/convexBrush.cc
2017-04-17 06:17:10 -06:00

1291 lines
37 KiB
C++
Executable File

#include "collision/convexBrush.h"
#include "dgl/dgl.h"
#include "math/mRandom.h"
#include "game/gameConnection.h"
#include "math/mPlaneTransformer.h"
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
ConvexBrush::ConvexBrush()
{
mBounds.min = Point3F(0, 0, 0);
mBounds.max = Point3F(0, 0, 0);
mBrushScale = 1.0f;
mType = 0;
mCentroid = Point3F(0.0f, 0.0f, 0.0f);
mStatus = Unprocessed;
mSelected = false;
// Set our transform to no translation or rotation
mTransform.identity();
mTransformScratch.identity();
mRotationScratch.identity();
mScale.set(1.0f, 1.0f, 1.0f);
mRotation.set(EulerF(0.0f, 0.0f, 0.0f));
// No id assigned yet
mID = -1;
mOwner = NULL;
}
ConvexBrush::~ConvexBrush()
{
}
// Just adds a plane to the list and default values for texture info
bool ConvexBrush::addPlane(PlaneF pln)
{
mFaces.mPolyList.increment();
mFaces.mPolyList.last().material = 0;
mFaces.mPolyList.last().object = NULL;
mFaces.mPolyList.last().surfaceKey = 0;
mFaces.mPolyList.last().vertexCount = 0;
mFaces.mPolyList.last().vertexStart = 0;
mFaces.mPolyList.last().plane = mFaces.addPlane(pln);
// The texgens and the faces increase together
mTexInfos.increment();
mTexInfos.last().texGens[0] = PlaneF(1.0f, 0.0f, 0.0f, 1.0f);
mTexInfos.last().texGens[1] = PlaneF(0.0f, 0.0f, 1.0f, 1.0f);
mTexInfos.last().scale[0] = 1.0f;
mTexInfos.last().scale[1] = 1.0f;
mTexInfos.last().rot = 0.0f;
mTexInfos.last().texDiv[0] = 1.0f;
mTexInfos.last().texDiv[1] = 1.0f;
mTexInfos.last().texture = StringTable->insert("");
return true;
}
bool ConvexBrush::addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], char* texture)
{
addPlane(pln);
mTexInfos.last().texGens[0] = texGen[0];
mTexInfos.last().texGens[1] = texGen[1];
mTexInfos.last().scale[0] = scale[0];
mTexInfos.last().scale[1] = scale[1];
mTexInfos.last().texture = StringTable->insert(texture);
return true;
}
bool ConvexBrush::addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], char* texture, U32 matIdx)
{
addFace(pln, texGen, scale, texture);
mFaces.mPolyList.last().material = matIdx;
return true;
}
bool ConvexBrush::addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], U32 matIdx)
{
return addFace(pln, texGen, scale, "", matIdx);;
}
// Call this after you have added the planes to generate the rest of the data
bool ConvexBrush::processBrush()
{
// It is possible that our status was changed externally by the parser
if (mStatus != Unprocessed)
return false;
// First check to see if we have enough planes
if (mFaces.mPlaneList.size() < 4)
{
mStatus = ShortPlanes;
char debug[512];
dSprintf(debug, 512, "This brush only has %d planes which is insufficent to create a convex volume. Possibly a badly formatted .map?", mFaces.mPlaneList.size());
mDebugInfo = StringTable->insert(debug);
return false;
}
// Generate the geometry
selfClip();
// Verify that all of the faces have at least 3 vertices
for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
{
if (mFaces.mPolyList[i].vertexCount < 3)
{
mStatus = BadFaces;
mDebugInfo = StringTable->insert("There are bad faces on this brush. This is most likely caused by a flat or non-volumetric brush.");
return false;
}
}
// Generate proper windings for the faces
for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
{
// Create a temporary index list
Vector<U32> indexes;
for (U32 j = 0; j < mFaces.mPolyList[i].vertexCount; j++)
indexes.push_back(mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j]);
// Create a buffer to return the winding to
Vector<U32> winding;
if (createWinding(indexes, mFaces.mPlaneList[mFaces.mPolyList[i].plane], winding))
{
// Copy the correctly order indices back
for (U32 j = 0; j < mFaces.mPolyList[i].vertexCount; j++)
mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j] = winding[j];
}
else
{
mStatus = BadWinding;
mDebugInfo = StringTable->insert("Unable to generate windings for some of the faces. This is often caused by having brushes that are too thin.");
return false;
}
indexes.clear();
winding.clear();
}
//validateWindings();
generateEdgelist();
if (!validateEdges())
return false;
calcBounds();
// If we got this far without any errors consider the brush to be good
if (mStatus == Unprocessed)
mStatus = Good;
else
return false;
return true;
}
void ConvexBrush::calcCentroid()
{
// First find the centroid
for (U32 i = 0; i < mFaces.mVertexList.size(); i++)
mCentroid += mFaces.mVertexList[i];
mCentroid /= mFaces.mVertexList.size();
}
void ConvexBrush::setupTransform()
{
// First calculate the centroid
calcCentroid();
mTransform.setColumn(3, mCentroid);
mCentroid.set(0.0f, 0.0f, 0.0f);
// Now untransform the verts and planes
MatrixF invTrans = mTransform;
invTrans.inverse();
// Untransform our verts
for (U32 i = 0; i < mFaces.mVertexList.size(); i++)
invTrans.mulP(mFaces.mVertexList[i]);
// Untransform our bounds
invTrans.mulP(mBounds.min);
invTrans.mulP(mBounds.max);
// Untransform our planes
PlaneTransformer trans;
trans.set(invTrans, Point3F(1.0f, 1.0f, 1.0f));
for (U32 i = 0; i < mFaces.mPlaneList.size(); i++)
{
PlaneF temp;
trans.transform(mFaces.mPlaneList[i], temp);
mFaces.mPlaneList[i] = temp;
}
}
bool ConvexBrush::validateEdges()
{
// Check to make sure that ever edge has two faces
for (U32 i = 0; i < mFaces.mEdgeList.size(); i++)
{
OptimizedPolyList::Edge ed = mFaces.mEdgeList[i];
if (mFaces.mEdgeList[i].faces[1] == -1)
{
mStatus = Concave;
mDebugInfo = StringTable->insert("There are missing faces on this brush. This is caused by having concave edges in the brush.");
return false;
}
}
// Now test for concavity
for (U32 i = 0; i < mFaces.mEdgeList.size(); i++)
{
U32 face0 = mFaces.mEdgeList[i].faces[0];
U32 face1 = mFaces.mEdgeList[i].faces[1];
// Get the indices that make up the edge
U32 i0 = mFaces.mEdgeList[i].vertexes[0];
U32 i1 = mFaces.mEdgeList[i].vertexes[1];
// Now get a point from face0 that isn't on the edge
U32 nsx;
for (U32 j = 0; j < mFaces.mPolyList[face0].vertexCount; j++)
{
nsx = mFaces.mIndexList[mFaces.mPolyList[face0].vertexStart + j];
if (nsx != i0 && nsx != i1)
break;
}
// Get the vertices
Point3F ns = mFaces.mVertexList[nsx];
// Grab the plane of face1
PlaneF pln = mFaces.mPlaneList[mFaces.mPolyList[face1].plane];
// Test to see if the point is in front or behind
if (pln.whichSide(ns) == PlaneF::Back)
mFaces.mEdgeList[i].type = OptimizedPolyList::Convex;
else
{
mFaces.mEdgeList[i].type = OptimizedPolyList::Concave;
mStatus = Concave;
mDebugInfo = StringTable->insert("Found some concave edges on this brush.");
}
}
if (mStatus == Concave)
return false;
else
return true;
}
void ConvexBrush::validateWindings()
{
for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
{
if (mFaces.mPolyList[i].vertexCount < 3)
continue;
U32 i0 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart];
U32 i1 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + 1];
U32 i2 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + 2];
Point3F p0 = mFaces.mVertexList[i0];
Point3F p1 = mFaces.mVertexList[i1];
Point3F p2 = mFaces.mVertexList[i2];
PlaneF wnorm(p0, p1, p2);
if (wnorm.whichSide(mCentroid) == PlaneF::Front)
{
Con::errorf("Face %d's winding is inverted", i);
// Let's reverse the winding
Vector<U32> fixed;
for (U32 j = 0; j < mFaces.mPolyList[i].vertexCount; j++)
fixed.push_front(mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j]);
for (U32 j = 0; j < mFaces.mPolyList[i].vertexCount; j++)
mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j] = fixed[j];
}
}
}
void ConvexBrush::addIndex(Vector<U32>& indices, U32 index)
{
for (U32 i = 0; i < indices.size(); i++)
{
if (indices[i] == index)
return;
}
indices.push_back(index);
}
// This clips the planes against each other
bool ConvexBrush::selfClip()
{
// Used to hold our indices temporarily
Vector<U32>* faceIndices = new Vector<U32>[mFaces.mPolyList.size()];
//Con::errorf("New brush");
//for (U32 i = 0; i < mFaces.mPlaneList.size(); i++)
// Con::printf("plane %d(%g, %g, %g, %g)", i, mFaces.mPlaneList[i].x, mFaces.mPlaneList[i].y, mFaces.mPlaneList[i].z, mFaces.mPlaneList[i].d);
// Clip them against each other to generate the vertexes
for (U32 i = 0; i < mFaces.mPlaneList.size(); i++)
{
for (U32 j = i+1; j < mFaces.mPlaneList.size(); j++)
{
for (U32 k = j+1; k < mFaces.mPlaneList.size(); k++)
{
Point3D interd;
Point3F interf;
bool doesSersect = intersectPlanes(mFaces.mPlaneList[i], mFaces.mPlaneList[j], mFaces.mPlaneList[k], &interd);
interf = Point3F(interd.x, interd.y, interd.z);
if (doesSersect)
{
// Check to make sure that the point is behind or on all of the
// planes
bool inOrOn = true;
for (U32 l = 0; l < mFaces.mPlaneList.size(); l++)
{
if (mFaces.mPlaneList[l].whichSide(interf) == PlaneF::Front)
{
inOrOn = false;
break;
}
}
if (inOrOn == true)
{
U32 vdx = mFaces.addPoint(interf);
addIndex(faceIndices[i], vdx);
addIndex(faceIndices[j], vdx);
addIndex(faceIndices[k], vdx);
}
}
}
}
}
// Add the indices for the faces
for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
{
// Set the starting index
mFaces.mPolyList[i].vertexStart = mFaces.mIndexList.size();
// Set the number of indexes
mFaces.mPolyList[i].vertexCount = faceIndices[i].size();
// Copy the indexes into the index list
for (U32 j = 0; j < faceIndices[i].size(); j++)
{
mFaces.mIndexList.push_back(faceIndices[i][j]);
}
}
for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
faceIndices[i].clear();
delete [] faceIndices;
return true;
}
bool ConvexBrush::intersectPlanes(const PlaneF& plane1, const PlaneF& plane2, const PlaneF& plane3, Point3D* pOutput)
{
F64 bc = (plane2.y * plane3.z) - (plane3.y * plane2.z);
F64 ac = (plane2.x * plane3.z) - (plane3.x * plane2.z);
F64 ab = (plane2.x * plane3.y) - (plane3.x * plane2.y);
F64 det = (plane1.x * bc) - (plane1.y * ac) + (plane1.z * ab);
if (mFabs(det) < 1e-7)
{
// Parallel planes
return false;
}
F64 dc = (plane2.d * plane3.z) - (plane3.d * plane2.z);
F64 db = (plane2.d * plane3.y) - (plane3.d * plane2.y);
F64 ad = (plane3.d * plane2.x) - (plane2.d * plane3.x);
F64 detInv = 1.0 / det;
pOutput->x = ((plane1.y * dc) - (plane1.d * bc) - (plane1.z * db)) * detInv;
pOutput->y = ((plane1.d * ac) - (plane1.x * dc) - (plane1.z * ad)) * detInv;
pOutput->z = ((plane1.y * ad) + (plane1.x * db) - (plane1.d * ab)) * detInv;
return true;
}
bool ConvexBrush::createWinding(const Vector<U32>& rPoints, const Point3F& normal, Vector<U32>& rWinding)
{
if (rPoints.size() < 3)
return false;
Vector<U32> modPoints;
Point3F centroid(0, 0, 0);
Point3F projcent(0, 0, 0);
modPoints = rPoints;
// Create a centroid first...
for (U32 i = 0; i < modPoints.size(); i++)
centroid += mFaces.mVertexList[modPoints[i]];
centroid /= F32(modPoints.size());
// Create a point projected along the normal
projcent = centroid + normal;
// Ok, we have a centroid. We (arbitrarily) start with the last point
rWinding.increment();
rWinding.last() = modPoints[modPoints.size() - 1];
modPoints.erase(modPoints.size() - 1);
while (modPoints.size() > 0)
{
// Get our last winding point
Point3F& currPoint = mFaces.mVertexList[rWinding.last()];
// Calculate the vector between the centroid and the current point
VectorF currVector = currPoint - centroid;
currVector.normalize();
// Create a plane with the winding point, the centroid, and the projected centroid
PlaneF testPlane(currPoint, centroid, projcent);
F32 maxDot = -1e10;
S32 maxIdx = -1;
for (U32 i = 0; i < modPoints.size(); i++)
{
Point3F& testPoint = mFaces.mVertexList[modPoints[i]];
if (testPlane.whichSide(testPoint) == PlaneF::Front)
{
// Get the vector between this point and the centroid
VectorF testVector = testPoint - centroid;
testVector.normalize();
F32 dot = mDot(testVector, currVector);
if (dot > maxDot)
{
maxDot = dot;
maxIdx = i;
}
}
}
if (maxIdx == -1)
{
// Sometimes the last vertex doesn't make it onto the list
if (modPoints.size() == 1)
{
rWinding.increment();
rWinding.last() = modPoints.last();
return true;
}
else
{
Con::errorf("ConvexBrush::createWinding(): was unable to find the next winding point");
// For debugging
U32 pt = 0;
for (U32 i = 0; i < rWinding.size(); i++)
{
Point3F& point = mFaces.mVertexList[rWinding[i]];
Con::printf(" point %d (%g, %g, %g)", pt, point.x, point.y, point.z);
}
for (U32 i = 0; i < modPoints.size(); i++)
{
Point3F& point = mFaces.mVertexList[modPoints[i]];
Con::printf(" point %d (%g, %g, %g)", pt, point.x, point.y, point.z);
}
return false;
}
}
rWinding.increment();
rWinding.last() = modPoints[maxIdx];
modPoints.erase(maxIdx);
}
modPoints.clear();
return true;
}
bool ConvexBrush::render(bool genColors)
{
// Renders it in colors only
if (genColors)
gRandGen.setSeed(1978);
glEnable(GL_CULL_FACE);
// Pick a random color for our brush
if (!genColors)
{
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);
}
for (U32 j = 0; j < mFaces.mPolyList.size(); j++)
{
S32 tx = mFaces.mPolyList[j].material;
if (tx == -2)
continue;
glBegin(GL_TRIANGLES);
for (U32 k = 1; k < mFaces.mPolyList[j].vertexCount - 1; k++)
{
U32 i0 = mFaces.mIndexList[mFaces.mPolyList[j].vertexStart];
U32 i1 = mFaces.mIndexList[mFaces.mPolyList[j].vertexStart + k];
U32 i2 = mFaces.mIndexList[mFaces.mPolyList[j].vertexStart + k + 1];
Point3F p0 = mFaces.mVertexList[i0];
Point3F p1 = mFaces.mVertexList[i1];
Point3F p2 = mFaces.mVertexList[i2];
if (genColors)
{
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);
}
PlaneF normal = mFaces.mPlaneList[mFaces.mPolyList[k].plane];
glNormal3f(normal.x, normal.y, normal.z);
F32 s, t;
PlaneF texGenX = mTexInfos[k].texGens[0];
PlaneF texGenY = mTexInfos[k].texGens[1];
s = texGenX.distToPlane(p0 * mBrushScale);
t = texGenY.distToPlane(p0 * mBrushScale);
glTexCoord2f(s, t);
glVertex3f(p0.x, p0.y, p0.z);
s = texGenX.distToPlane(p1 * mBrushScale);
t = texGenY.distToPlane(p1 * mBrushScale);
glTexCoord2f(s, t);
glVertex3f(p1.x, p1.y, p1.z);
s = texGenX.distToPlane(p2 * mBrushScale);
t = texGenY.distToPlane(p2 * mBrushScale);
glTexCoord2f(s, t);
glVertex3f(p2.x, p2.y, p2.z);
}
glEnd();
}
return true;
}
bool ConvexBrush::renderFace(U32 face, bool renderLighting)
{
if (face > mFaces.mPolyList.size())
{
Con::errorf("ConvexBrush::debugRenderFace(): face is outside range");
return false;
}
if (mFaces.mPolyList[face].vertexCount < 3)
return false;
const OptimizedPolyList::TriangleLighting *lighting = NULL;
Point3F tp;
PlaneF lmGenX, lmGenY;
if(renderLighting)
glActiveTextureARB(GL_TEXTURE1_ARB);
for (U32 k = 1; k < mFaces.mPolyList[face].vertexCount - 1; k++)
{
U32 i0 = mFaces.mIndexList[mFaces.mPolyList[face].vertexStart];
U32 i1 = mFaces.mIndexList[mFaces.mPolyList[face].vertexStart + k];
U32 i2 = mFaces.mIndexList[mFaces.mPolyList[face].vertexStart + k + 1];
Point3F p0 = mFaces.mVertexList[i0];
Point3F p1 = mFaces.mVertexList[i1];
Point3F p2 = mFaces.mVertexList[i2];
if((renderLighting) && ((lighting = mFaces.getTriangleLighting(mFaces.mPolyList[face].triangleLightingStartIndex + (k - 1))) != NULL))
{
lmGenX = lighting->lightMapEquationX;
lmGenY = lighting->lightMapEquationY;
glBindTexture(GL_TEXTURE_2D, lighting->lightMapId);
}
glBegin(GL_TRIANGLES);
PlaneF normal = mFaces.mPlaneList[mFaces.mPolyList[face].plane];
glNormal3f(normal.x, normal.y, normal.z);
F32 s, t;
PlaneF texGenX = mTexInfos[face].texGens[0];
PlaneF texGenY = mTexInfos[face].texGens[1];
tp = p0;
s = texGenX.distToPlane(tp);
t = texGenY.distToPlane(tp);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, s, t);
if(lighting)
{
mLightingTransform.mulP(tp);
s = lmGenX.distToPlane(tp);
t = lmGenY.distToPlane(tp);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, s, t);
}
glVertex3f(p0.x, p0.y, p0.z);
tp = p1;
s = texGenX.distToPlane(tp);
t = texGenY.distToPlane(tp);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, s, t);
if(lighting)
{
mLightingTransform.mulP(tp);
s = lmGenX.distToPlane(tp);
t = lmGenY.distToPlane(tp);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, s, t);
}
glVertex3f(p1.x, p1.y, p1.z);
tp = p2;
s = texGenX.distToPlane(tp);
t = texGenY.distToPlane(tp);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, s, t);
if(lighting)
{
mLightingTransform.mulP(tp);
s = lmGenX.distToPlane(tp);
t = lmGenY.distToPlane(tp);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, s, t);
}
glVertex3f(p2.x, p2.y, p2.z);
glEnd();
}
if(renderLighting)
glActiveTextureARB(GL_TEXTURE0_ARB);
return true;
}
bool ConvexBrush::renderEdges(ColorF color)
{
// Save ModelView Matrix so we can restore Canonical state at exit.
glPushMatrix();
// Transform by the objects' transform e.g move it.
dglMultMatrix(&mTransform);
// Scale
//glScalef(mScale.x, mScale.y, mScale.z);
glBegin(GL_LINES);
for (U32 i = 0; i < mFaces.mEdgeList.size(); i++)
{
OptimizedPolyList::Edge ed = mFaces.mEdgeList[i];
//if (mFaces.mEdgeList[i].type == OptimizedPolyList::Convex)
// glColor3f(0.0f, 1.0f, 0.0f);
//else if (mFaces.mEdgeList[i].type == OptimizedPolyList::Concave)
// glColor3f(1.0f, 0.0f, 0.0f);
//else
// glColor3f(0.0f, 0.0f, 1.0f);
glColor4f(color.red, color.green, color.blue, color.alpha);
U32 i0 = mFaces.mEdgeList[i].vertexes[0];
U32 i1 = mFaces.mEdgeList[i].vertexes[1];
Point3F p0 = mFaces.mVertexList[i0];
Point3F p1 = mFaces.mVertexList[i1];
glVertex3f(p0.x, p0.y, p0.z);
glVertex3f(p1.x, p1.y, p1.z);
}
glEnd();
glPopMatrix();
return true;
}
bool ConvexBrush::calcBounds()
{
// Rebuild our bounds
if (mFaces.mVertexList.size() > 0)
{
mBounds.min.set(1e10, 1e10, 1e10);
mBounds.max.set(-1e10, -1e10, -1e10);
// Build our bounds
for (U32 i = 0; i < mFaces.mVertexList.size(); i++)
{
mBounds.min.setMin(mFaces.mVertexList[i]);
mBounds.max.setMax(mFaces.mVertexList[i]);
}
}
return true;
}
bool ConvexBrush::setScale(F32 scale)
{
mBrushScale = scale;
// Scale our verts
for (U32 i = 0; i < mFaces.mVertexList.size(); i++)
mFaces.mVertexList[i] /= mBrushScale;
// Scale the planes
for (U32 i = 0; i < mFaces.mPlaneList.size(); i++)
mFaces.mPlaneList[i].d /= mBrushScale;
calcBounds();
return true;
}
void ConvexBrush::addEdge(U16 zero, U16 one, U32 face)
{
// Sort the edges
U16 newEdge0, newEdge1;
newEdge0 = getMin(zero, one);
newEdge1 = getMax(zero, one);
// Check the current edges
bool found = false;
U32 index = 0;
for (index = 0; index < mFaces.mEdgeList.size(); index++)
{
if (mFaces.mEdgeList[index].vertexes[0] == newEdge0 && mFaces.mEdgeList[index].vertexes[1] == newEdge1)
{
found = true;
break;
}
}
// If we didn't find it add a new edge
if (!found)
{
index = mFaces.mEdgeList.size();
mFaces.mEdgeList.increment();
mFaces.mEdgeList.last().vertexes[0] = newEdge0;
mFaces.mEdgeList.last().vertexes[1] = newEdge1;
mFaces.mEdgeList.last().faces[0] = -1;
mFaces.mEdgeList.last().faces[1] = -1;
mFaces.mEdgeList.last().type = OptimizedPolyList::NonShared;
}
// Add the face
if (mFaces.mEdgeList[index].faces[0] == -1)
mFaces.mEdgeList[index].faces[0] = face;
else if (mFaces.mEdgeList[index].faces[1] == -1)
mFaces.mEdgeList[index].faces[1] = face;
};
bool ConvexBrush::generateEdgelist()
{
for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
{
// Add all of the edges of the polygon
for (U32 j = 0; j < mFaces.mPolyList[i].vertexCount - 1; j++)
{
U32 i0 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j];
U32 i1 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j + 1];
addEdge(i0, i1, i);
}
// Add the connecting edge from the last vertex to the first
U32 i0 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart];
U32 i1 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + mFaces.mPolyList[i].vertexCount - 1];
addEdge(i0, i1, i);
}
return true;
}
U32 ConvexBrush::getPolySide(S32 side)
{
if (side == PlaneF::Back)
return Back;
else if (side == PlaneF::Front)
return Front;
else if (side == PlaneF::On)
return On;
else
return Unknown;
}
bool ConvexBrush::isInsideBox(PlaneF left, PlaneF right, PlaneF top, PlaneF bottom)
{
if (mStatus == Deleted)
return false;
// If the brush is in front of any of the planes then it is outside
U32 side = whichSide(left);
if (side == Front || side == Unknown)
return false;
side = whichSide(right);
if (side == Front || side == Unknown)
return false;
side = whichSide(top);
if (side == Front || side == Unknown)
return false;
side = whichSide(bottom);
if (side == Front || side == Unknown)
return false;
return true;
}
U32 ConvexBrush::whichSide(PlaneF pln)
{
if (mFaces.mPolyList.size() == 0)
return Unknown;
// Find out which side the first face is on
U32 side = On;
for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
{
U32 nsd = whichSide(pln, i);
if (nsd == Split)
return Split;
else if (nsd != On)
side = nsd;
}
if (side == On)
Con::errorf("ConvexBrush::whichSide(): brush has no volume");
return side;
}
U32 ConvexBrush::whichSide(PlaneF pln, U32 faceIndex)
{
if (faceIndex > mFaces.mPolyList.size())
return Unknown;
OptimizedPolyList::Poly* face = &mFaces.mPolyList[faceIndex];
Point3F currv, nextv;
S32 csd, nsd;
U32 side = On;
// Find out which side the first vert is on
U32 idx = mFaces.mIndexList[face->vertexStart];
currv = mFaces.mVertexList[idx];
csd = pln.whichSide(currv);
if (csd != PlaneF::On)
side = getPolySide(csd);
for (U32 k = 1; k < face->vertexCount; k++)
{
idx = mFaces.mIndexList[face->vertexStart + k];
nextv = mFaces.mVertexList[idx];
nsd = pln.whichSide(nextv);
if ((csd == PlaneF::Back && nsd == PlaneF::Front) ||
(csd == PlaneF::Front && nsd == PlaneF::Back))
{
return Split;
}
else if (nsd != PlaneF::On)
side = getPolySide(nsd);
currv = nextv;
csd = nsd;
}
// Loop back to the first vert
idx = mFaces.mIndexList[face->vertexStart];
nextv = mFaces.mVertexList[idx];
nsd = pln.whichSide(nextv);
if ((csd == PlaneF::Back && nsd == PlaneF::Front) ||
(csd == PlaneF::Front && nsd == PlaneF::Back))
{
return Split;
}
else if (nsd != PlaneF::On)
side = getPolySide(nsd);
return side;
}
bool ConvexBrush::getPolyList(AbstractPolyList* list)
{
if (mFaces.mVertexList.size() == 0 || mFaces.mPolyList.size() == 0)
{
Con::errorf("ConvexBrush::getPolyList(): no verts or faces");
return false;
}
PlaneTransformer trans;
trans.set(mTransform, Point3F(1.0f, 1.0f, 1.0f));
for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
{
if (mFaces.mPolyList[i].vertexCount < 3)
{
Con::errorf("ConvexBrush::getPolyList(): ran into a face with less than 3 verts");
continue;
}
list->begin(0, i);
for (U32 j = 0; j < mFaces.mPolyList[i].vertexCount; j++)
{
U32 idx = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j];
Point3F pt = mFaces.mVertexList[idx];
// Transform the point by the brush transform
mTransform.mulP(pt);
U32 newidx = list->addPoint(pt);
list->vertex(newidx);
}
PlaneF pln = mFaces.mPlaneList[mFaces.mPolyList[i].plane];
//pln.invert();
PlaneF temp;
trans.transform(pln, temp);
U32 pdx = list->addPlane(temp);
list->plane(pdx);
list->end();
}
return true;
}
bool ConvexBrush::splitBrush(PlaneF pln, ConvexBrush* fbrush, ConvexBrush* bbrush)
{
F32 scale = mBrushScale;
// Scale the brush up
setScale(1.0f / scale);
pln.d *= scale;
// Loop through the brush faces and sort them
for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
{
U32 side = whichSide(pln, i);
if (side == Front)
fbrush->addPlane(mFaces.mPlaneList[mFaces.mPolyList[i].plane]);
else if (side == Back)
bbrush->addPlane(mFaces.mPlaneList[mFaces.mPolyList[i].plane]);
else if (side == Split)
{
fbrush->addPlane(mFaces.mPlaneList[mFaces.mPolyList[i].plane]);
bbrush->addPlane(mFaces.mPlaneList[mFaces.mPolyList[i].plane]);
}
else
{
Con::errorf("BspTree::splitBrush(): this brush can not be split");
return false;
}
}
// Next push the split plane into both
bbrush->addPlane(pln);
fbrush->addPlane(PlaneF(-pln.x, -pln.y, -pln.z, -pln.d));
// Now generate the brushes with the planes supplied
fbrush->processBrush();
bbrush->processBrush();
// Now that we've generated the new polys copy over the properties
for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
{
// We can match the new polys up with the old ones using their planes
PlaneF oldpln = mFaces.mPlaneList[mFaces.mPolyList[i].plane];
for (U32 j = 0; j < fbrush->mFaces.mPolyList.size(); j++)
{
PlaneF newpln = fbrush->mFaces.mPlaneList[fbrush->mFaces.mPolyList[j].plane];
if (oldpln == newpln && oldpln.d == newpln.d)
{
// Copy over the properties
fbrush->mFaces.mPolyList[j].object = mFaces.mPolyList[i].object;
fbrush->mFaces.mPolyList[j].material = mFaces.mPolyList[i].material;
fbrush->mFaces.mPolyList[j].material = mFaces.mPolyList[i].surfaceKey;
}
}
for (U32 j = 0; j < bbrush->mFaces.mPolyList.size(); j++)
{
PlaneF newpln = bbrush->mFaces.mPlaneList[bbrush->mFaces.mPolyList[j].plane];
if (oldpln == newpln && oldpln.d == newpln.d)
{
// Copy over the properties
bbrush->mFaces.mPolyList[j].object = mFaces.mPolyList[i].object;
bbrush->mFaces.mPolyList[j].material = mFaces.mPolyList[i].material;
bbrush->mFaces.mPolyList[j].material = mFaces.mPolyList[i].surfaceKey;
}
}
}
// Now find the new poly and flag it as a non-solid face
for (U32 i = 0; i < fbrush->mFaces.mPolyList.size(); i++)
{
PlaneF newpln = fbrush->mFaces.mPlaneList[fbrush->mFaces.mPolyList[i].plane];
if (pln.x == -newpln.x && pln.y == -newpln.y && pln.z == -newpln.z && pln.d == -newpln.d)
fbrush->mFaces.mPolyList[i].material = -2;
}
for (U32 i = 0; i < bbrush->mFaces.mPolyList.size(); i++)
{
PlaneF newpln = bbrush->mFaces.mPlaneList[bbrush->mFaces.mPolyList[i].plane];
if (pln == newpln && pln.d == newpln.d)
bbrush->mFaces.mPolyList[i].material = -2;
}
// scale everything back down
fbrush->setScale(scale);
bbrush->setScale(scale);
setScale(scale);
return true;
}
bool ConvexBrush::castRay(const Point3F& s, const Point3F& e, RayInfo* info)
{
if (mStatus == Deleted)
return false;
// First transform the start and end points into the brushes space
Point3F start, end;
MatrixF invTrans = mTransform;
invTrans.inverse();
invTrans.mulP(s, &start);
invTrans.mulP(e, &end);
start.convolveInverse(mScale);
end.convolveInverse(mScale);
// Ganked from TSMesh::castRay()
// Keep track of startTime and endTime. They start out at just under 0 and just over 1, respectively.
// As we check against each plane, prune start and end times back to represent current intersection of
// line with all the planes (or rather with all the half-spaces defined by the planes).
// But, instead of explicitly keeping track of startTime and endTime, keep track as numerator and denominator
// so that we can avoid as many divisions as possible.
// F32 startTime = -0.01f;
F32 startNum = -0.01f;
F32 startDen = 1.00f;
// F32 endTime = 1.01f;
F32 endNum = 1.01f;
F32 endDen = 1.00f;
S32 curPlane = 0;
U32 curMaterial = 0;
bool found = false;
// the following block of code is an optimization...
// it isn't necessary if the longer version of the main loop is used
bool tmpFound;
S32 tmpPlane;
F32 sgn = -1.0f;
F32 * pnum = &startNum;
F32 * pden = &startDen;
S32 * pplane = &curPlane;
bool * pfound = &found;
for (U32 i = 0; i < mFaces.mPlaneList.size(); i++)
{
Point3F planeNormal = mFaces.mPlaneList[i];
F32 planeConstant = mFaces.mPlaneList[i].d;
// if start & end outside, no collision
// if start & end inside, continue
// if start outside, end inside, or visa versa, find intersection of line with plane
// then update intersection of line with hull (using startTime and endTime)
F32 dot1 = mDot(planeNormal, start) + planeConstant;
F32 dot2 = mDot(planeNormal, end) + planeConstant;
if (dot1*dot2>0.0f)
{
// same side of the plane...which side -- dot==0 considered inside
if (dot1>0.0f)
// start and end outside of this plane, no collision
return false;
// start and end inside plane, continue
continue;
}
AssertFatal(dot1/(dot1-dot2)>=0.0f && dot1/(dot1-dot2)<=1.0f,"ConvexBrush::castRay (1)");
// find intersection (time) with this plane...
// F32 time = dot1 / (dot1-dot2);
F32 num = mFabs(dot1);
F32 den = mFabs(dot1-dot2);
// the following block of code is an optimized version...
// this can be commented out and the following block of code used instead
// if debugging a problem in this code, that should probably be done
// if you want to see how this works, look at the following block of code,
// not this one...
// Note that this does not get optimized appropriately...it is included this way
// as an idea for future optimization.
if (sgn*dot1>=0)
{
sgn *= -1.0f;
pnum = (F32*) ((dsize_t)pnum ^ (dsize_t)&endNum ^ (dsize_t)&startNum);
pden = (F32*) ((dsize_t)pden ^ (dsize_t)&endDen ^ (dsize_t)&startDen);
pplane = (S32*) ((dsize_t)pplane ^ (dsize_t)&tmpPlane ^ (dsize_t)&curPlane);
pfound = (bool*) ((dsize_t)pfound ^ (dsize_t)&tmpFound ^ (dsize_t)&found);
}
bool noCollision = num*endDen*sgn<endNum*den*sgn && num*startDen*sgn<startNum*den*sgn;
if (num * *pden * sgn < *pnum * den * sgn && !noCollision)
{
*pnum = num;
*pden = den;
*pplane = i;
*pfound = true;
}
else if (noCollision)
return false;
}
// setup rayInfo
if (found && info)
{
info->t = (F32)startNum/(F32)startDen; // finally divide...
info->normal = mFaces.mPlaneList[curPlane];
// Find the face that uses this plane and its material
for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
{
if (mFaces.mPolyList[i].plane == curPlane)
{
info->material = mFaces.mPolyList[i].material;
break;
}
}
return true;
}
else if (found)
return true;
// only way to get here is if start is inside hull...
// we could return null and just plug in garbage for the material and normal...
return false;
}
void ConvexBrush::resetBrush()
{
mBounds.min = Point3F(0, 0, 0);
mBounds.max = Point3F(0, 0, 0);
mCentroid = Point3F(0.0f, 0.0f, 0.0f);
mStatus = Unprocessed;
// Set our transform to no translation or rotation
mTransform.identity();
mScale.set(1.0f, 1.0f, 1.0f);
mRotation.set(EulerF(0.0f, 0.0f, 0.0f));
// Data
mFaces.clear();
mTexInfos.clear();
}
OptimizedPolyList ConvexBrush::getIntersectingPolys(OptimizedPolyList* list)
{
OptimizedPolyList ret;
for (U32 i = 0; i < list->mPolyList.size(); i++)
{
if (isPolyInside(list, i))
list->copyPolyToList(&ret, i);
}
return ret;
}
OptimizedPolyList ConvexBrush::getNonIntersectingPolys(OptimizedPolyList* list)
{
OptimizedPolyList ret;
for (U32 i = 0; i < list->mPolyList.size(); i++)
{
if (!isPolyInside(list, i))
list->copyPolyToList(&ret, i);
}
return ret;
}
bool ConvexBrush::isPolyInside(OptimizedPolyList* list, U32 pdx)
{
// This relies on the brush to be convex
bool inside = true;
OptimizedPolyList::Poly& poly = list->mPolyList[pdx];
for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
{
OptimizedPolyList::Poly& face = mFaces.mPolyList[i];
PlaneF& pln = mFaces.mPlaneList[face.plane];
bool behind = true;
for (U32 j = 0; j < poly.vertexCount; j++)
{
U32 idx = list->mIndexList[j + poly.vertexStart];
Point3F& pt = list->mVertexList[idx];
U32 tside = pln.whichSide(pt);
if (tside == PlaneF::Front)
{
behind = false;
break;
}
}
if (!behind)
{
inside = false;
break;
}
}
return inside;
}