904 lines
30 KiB
C++
Executable File
904 lines
30 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|