Torque3D/Engine/source/math/util/decomposePoly.cpp

581 lines
13 KiB
C++
Raw Normal View History

#include "math/util/decomposePoly.h"
// twoIndices Methods
twoIndices::twoIndices()
{
i1 = i2 = 0;
}
twoIndices::twoIndices(U8 a, U8 b)
{
i1 = a;
i2 = b;
}
// decompTri Methods
decompTri::decompTri()
{
mVertIdx[0] = 0;
mVertIdx[1] = 0;
mVertIdx[2] = 0;
}
void decompTri::orderVerts()
{
// Bubble sort, smallest to largest
U8 temp;
for (U8 i = 2; i > 0; i--)
{
for (U8 j = 0; j < i; j++)
{
if (mVertIdx[j] > mVertIdx[j + 1])
{
temp = mVertIdx[j];
mVertIdx[j] = mVertIdx[j + 1];
mVertIdx[j + 1] = temp;
}
}
}
}
void decompTri::setVert(U8 val, U8 idx)
{
if(idx < 3)
mVertIdx[idx] = val;
}
U8 decompTri::getVert(U8 idx)
{
if(idx < 3)
return mVertIdx[idx];
return mVertIdx[2];
}
twoIndices decompTri::getOtherVerts(U8 idx)
{
if(idx == mVertIdx[0])
return twoIndices(mVertIdx[1], mVertIdx[2]);
if(idx == mVertIdx[1])
return twoIndices(mVertIdx[0], mVertIdx[2]);
if(idx == mVertIdx[2])
return twoIndices(mVertIdx[0], mVertIdx[1]);
return twoIndices(0,0);
}
// decompPoly Methods
void decompPoly::addVert(Point3F &newVert)
{
bool found = false;
for(U8 i = 0; i < mVertList.size(); i++)
{
if(newVert == mVertList[i])
found = true;
}
if(!found)
mVertList.push_back(newVert);
}
void decompPoly::initEdgeList()
{
mEdgeList.clear();
S32 next = 0;
for(S32 i = 0; i < mVertList.size(); i++)
{
if(i == mVertList.size()-1)
next = 0;
else
next = i+1;
mEdgeList.push_back(twoIndices(i, next));
}
}
bool decompPoly::sameSide(Point3F &p1, Point3F &p2, Point3F &l1, Point3F &l2)
{
return ((p1.x-l1.x)*(l2.y-l1.y)-(l2.x-l1.x)*(p1.y-l1.y))*((p2.x-l1.x)*(l2.y-l1.y)-(l2.x-l1.x)*(p2.y-l1.y)) > 0;
}
bool decompPoly::isInside(decompTri &tri, U8 vertIdx)
{
Point3F a(mVertList[tri.getVert(0)]);
Point3F b(mVertList[tri.getVert(1)]);
Point3F c(mVertList[tri.getVert(2)]);
Point3F p(mVertList[vertIdx]);
return (sameSide(p,a,b,c) && sameSide(p,b,a,c) && sameSide(p,c,a,b));
}
U8 decompPoly::leftmost()
{
F32 xMin = 9999999.f;
U8 idx = 0;
for(U8 i = 0; i < mVertList.size(); i++)
{
if(mVertList[i].x < xMin && isVertInEdgeList(i))
{
xMin = mVertList[i].x;
idx = i;
}
}
return idx;
}
U8 decompPoly::rightmost()
{
F32 xMax = -9999999.f;
U8 idx = 0;
for(U8 i = 0; i < mVertList.size(); i++)
{
if(mVertList[i].x > xMax && isVertInEdgeList(i))
{
xMax = mVertList[i].x;
idx = i;
}
}
return idx;
}
U8 decompPoly::uppermost()
{
F32 yMax = -9999999.f;
U8 idx = 0;
for(U8 i = 0; i < mVertList.size(); i++)
{
if(mVertList[i].y > yMax && isVertInEdgeList(i))
{
yMax = mVertList[i].y;
idx = i;
}
}
return idx;
}
U8 decompPoly::lowermost()
{
F32 yMin = 9999999.f;
U8 idx = 0;
for(U8 i = 0; i < mVertList.size(); i++)
{
if(mVertList[i].y < yMin && isVertInEdgeList(i))
{
yMin = mVertList[i].y;
idx = i;
}
}
return idx;
}
twoIndices decompPoly::findEdges(U8 vertIdx, bool &notUnique)
{
U8 found = 0;
U8 edgeIdx[2];
for(U8 i = 0; i < mEdgeList.size(); i++)
{
if(mEdgeList[i].i1 == vertIdx || mEdgeList[i].i2 == vertIdx)
{
edgeIdx[found++] = i;
}
}
notUnique = found > 2;
return twoIndices(edgeIdx[0], edgeIdx[1]);
}
decompTri decompPoly::formTriFromEdges(U8 idx1, U8 idx2)
{
decompTri tri;
tri.setVert(mEdgeList[idx1].i1, 0);
tri.setVert(mEdgeList[idx1].i2, 1);
if(mEdgeList[idx2].i1 == tri.getVert(0) || mEdgeList[idx2].i1 == tri.getVert(1))
{
tri.setVert(mEdgeList[idx2].i2, 2);
}
else
{
tri.setVert(mEdgeList[idx2].i1, 2);
}
return tri;
}
twoIndices decompPoly::leftmostInsideVerts(U8 idx1, U8 idx2)
{
F32 xMin = 9999999.f;
S32 left[] = {-1, -1};
for(U8 i = 0; i < 2; i++)
{
for(U8 j = 0; j < mInsideVerts.size(); j++)
{
if(mVertList[mInsideVerts[j]].x < xMin && mInsideVerts[j] != left[0])
{
xMin = mVertList[mInsideVerts[j]].x;
left[i] = mInsideVerts[j];
}
}
if(mVertList[idx1].x < xMin && idx1 != left[0])
{
xMin = mVertList[idx1].x;
left[i] = idx1;
}
if(mVertList[idx2].x < xMin && idx2 != left[0])
{
xMin = mVertList[idx2].x;
left[i] = idx2;
}
xMin = 9999999.f;
}
return twoIndices(left[0], left[1]);
}
twoIndices decompPoly::rightmostInsideVerts(U8 idx1, U8 idx2)
{
F32 xMax = -9999999.f;
S32 right[] = {-1, -1};
for(U8 i = 0; i < 2; i++)
{
for(U8 j = 0; j < mInsideVerts.size(); j++)
{
if(mVertList[mInsideVerts[j]].x > xMax && mInsideVerts[j] != right[0])
{
xMax = mVertList[mInsideVerts[j]].x;
right[i] = mInsideVerts[j];
}
}
if(mVertList[idx1].x > xMax && idx1 != right[0])
{
xMax = mVertList[idx1].x;
right[i] = idx1;
}
if(mVertList[idx2].x > xMax && idx2 != right[0])
{
xMax = mVertList[idx2].x;
right[i] = idx2;
}
xMax = -9999999.f;
}
return twoIndices(right[0], right[1]);
}
twoIndices decompPoly::uppermostInsideVerts(U8 idx1, U8 idx2)
{
F32 yMax = -9999999.f;
S32 up[] = {-1, -1};
for(U8 i = 0; i < 2; i++)
{
for(U8 j = 0; j < mInsideVerts.size(); j++)
{
if(mVertList[mInsideVerts[j]].y > yMax && mInsideVerts[j] != up[0])
{
yMax = mVertList[mInsideVerts[j]].y;
up[i] = mInsideVerts[j];
}
}
if(mVertList[idx1].y > yMax && idx1 != up[0])
{
yMax = mVertList[idx1].y;
up[i] = idx1;
}
if(mVertList[idx2].y > yMax && idx2 != up[0])
{
yMax = mVertList[idx2].y;
up[i] = idx2;
}
yMax = -9999999.f;
}
return twoIndices(up[0], up[1]);
}
twoIndices decompPoly::lowermostInsideVerts(U8 idx1, U8 idx2)
{
F32 yMin = 9999999.f;
S32 down[] = {-1, -1};
for(U8 i = 0; i < 2; i++)
{
for(U8 j = 0; j < mInsideVerts.size(); j++)
{
if(mVertList[mInsideVerts[j]].y < yMin && mInsideVerts[j] != down[0])
{
yMin = mVertList[mInsideVerts[j]].y;
down[i] = mInsideVerts[j];
}
}
if(mVertList[idx1].y < yMin && idx1 != down[0])
{
yMin = mVertList[idx1].y;
down[i] = idx1;
}
if(mVertList[idx2].y < yMin && idx2 != down[0])
{
yMin = mVertList[idx2].y;
down[i] = idx2;
}
yMin = 9999999.f;
}
return twoIndices(down[0], down[1]);
}
void decompPoly::findPointsInside()
{
mInsideVerts.clear();
for(U8 i = 0; i < mVertList.size(); i++)
{
if(i != mTestTri.getVert(0) && i != mTestTri.getVert(1) && i != mTestTri.getVert(2))
{
if(isInside(mTestTri, i))
{
mInsideVerts.push_back(i);
}
}
}
}
void decompPoly::addRemoveEdge(U8 idx1, U8 idx2)
{
twoIndices edge1(idx1, idx2);
if(!mEdgeList.remove(edge1))
mEdgeList.push_back(edge1);
}
void decompPoly::newPoly()
{
mVertList.clear();
mEdgeList.clear();
mInsideVerts.clear();
mTris.clear();
}
bool decompPoly::isVertInEdgeList(U8 idx)
{
for(U8 i = 0; i < mEdgeList.size(); i++)
{
if(mEdgeList[i].i1 == idx || mEdgeList[i].i2 == idx)
return true;
}
return false;
}
Point3F decompPoly::getVert(U8 idx)
{
if(idx < mVertList.size())
return mVertList[idx];
return Point3F(0.0f, 0.0f, 0.0f);
}
U8 decompPoly::getTriIdx(U32 tri, U8 idx)
{
if(tri < mTris.size() && idx < 3)
return mTris[tri].getVert(idx);
return 0;
}
bool decompPoly::checkEdgeLength(F32 len)
{
Point3F p1, p2;
for(U8 i = 0; i < mEdgeList.size(); i++)
{
p1 = mVertList[mEdgeList[i].i1];
p2 = mVertList[mEdgeList[i].i2];
p1 = p2 - p1;
if(p1.len() < len)
return false;
}
return true;
}
//---------------------------------------------------
// Concave polygon decomposition into triangles
//
// Based upon this resource:
// http://www.siggraph.org/education/materials/HyperGraph/scanline/outprims/polygon1.htm
//
// 1. Find leftmost vertex in polygon (smallest value along x-axis).
// 2. Find the two edges adjacent to this vertex.
// 3. Form a test tri by connecting the open side of these two edges.
// 4. See if any of the vertices in the poly, besides the ones that form the test tri, are inside the
// test tri.
// 5. If there are verts inside, find the 3 leftmost of the inside verts and the test tri verts, form
// a new test tri from these verts, and repeat from step 4. When no verts are inside, continue.
// 6. Save test tri as a valid triangle.
// 7. Remove the edges that the test tri and poly share, and add the new edges from the test tri to
// the poly to form a new closed poly with the test tri subtracted out.
//
// Note: our polygon can contain no more than 255 verts. Complex polygons can create situations where
// a vertex has more than 2 adjacent edges, causing step 2 to fail. This method improves on the resource
// listed above by not limiting the decomposition to the leftmost side only. If step 2 fails, the
// algorithm also tries to decompose the polygon from the top, right, and bottom until one succeeds or
// they all fail. If they all fail, the polygon is too complex to be decomposed with this method.
bool decompPoly::decompose()
{
// Must have at least 3 verts to form a poly
if(mVertList.size() < 3)
return false;
// Clear out any previously stored tris
mTris.clear();
// Initialize the edge list with the default edges
initEdgeList();
twoIndices otherVerts, outerVerts2;
U32 counter = 0;
U8 uniqueVertAttempt = 0;
U8 formTriAttempt = 0;
bool notUnique = false;
// The main decomposition loop
while(mEdgeList.size() > 3)
{
// Find outermost vert in poly, LMV
U8 outerVertIdx;
switch(uniqueVertAttempt)
{
case 0: outerVertIdx = leftmost(); break;
case 1: outerVertIdx = rightmost(); break;
case 2: outerVertIdx = uppermost(); break;
case 3: outerVertIdx = uppermost(); break;
default: outerVertIdx = leftmost();
}
// Find edges that share LMV
twoIndices edgesIdx = findEdges(outerVertIdx, notUnique);
// If vert shares more than two edges, try decomposing from different direction
if(notUnique)
{
if(uniqueVertAttempt < 4)
{
uniqueVertAttempt++;
continue;
}
else
{
newPoly();
return false;
}
}
// Sanity check
if(edgesIdx.i1 >= mEdgeList.size() || edgesIdx.i2 >= mEdgeList.size())
{
newPoly();
return false;
}
// Form the test tri from these 2 edges
mTestTri = formTriFromEdges(edgesIdx.i1, edgesIdx.i2);
// See if there are any other poly verts inside the test tri
findPointsInside();
// If there are verts inside, repeat procedure until there are none
while(mInsideVerts.size() > 0 && formTriAttempt++ < mVertList.size())
{
// Get the two verts of the test tri that are not the LMV
otherVerts = mTestTri.getOtherVerts(outerVertIdx);
// Get the 2 outermost verts of the inside and test tri verts (excluding LMV)
switch(uniqueVertAttempt)
{
case 0: outerVerts2 = leftmostInsideVerts(otherVerts.i1, otherVerts.i2); break;
case 1: outerVerts2 = rightmostInsideVerts(otherVerts.i1, otherVerts.i2); break;
case 2: outerVerts2 = uppermostInsideVerts(otherVerts.i1, otherVerts.i2); break;
case 3: outerVerts2 = uppermostInsideVerts(otherVerts.i1, otherVerts.i2); break;
default: outerVerts2 = leftmostInsideVerts(otherVerts.i1, otherVerts.i2);
}
// Form a new test tri from LMV, and the 2 new leftmost verts above
mTestTri.setVert(outerVerts2.i1, 0);
mTestTri.setVert(outerVertIdx, 1);
mTestTri.setVert(outerVerts2.i2, 2);
// See if there are verts inside this new test tri
findPointsInside();
}
// We have found a valid tri
mTris.push_back(mTestTri);
// Remove edges common to test tri and poly... add unique edges from test tri to poly
addRemoveEdge(mTestTri.getVert(0), mTestTri.getVert(1));
addRemoveEdge(mTestTri.getVert(1), mTestTri.getVert(2));
addRemoveEdge(mTestTri.getVert(0), mTestTri.getVert(2));
// This should never take 255 iterations... we must be stuck in an infinite loop
if(counter++ > 255)
{
newPoly();
return false;
}
// Reset attempts
formTriAttempt = 0;
uniqueVertAttempt = 0;
}
// The last tri
mTris.push_back(formTriFromEdges(0,1));
// The verts need to be ordered to draw correctly
for(U8 i = 0; i < mTris.size(); i++)
{
mTris[i].orderVerts();
}
return true;
}