123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972 |
- /* Copyright (c) 2002-2012 Croteam Ltd.
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as published by
- the Free Software Foundation
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
- #include "stdh.h"
- #include <Engine/Brushes/Brush.h>
- #include <Engine/Templates/DynamicArray.cpp>
- #include <Engine/Templates/StaticArray.cpp>
- #include <Engine/Templates/StaticStackArray.cpp>
- #include <Engine/Math/Float.h>
- #include <Engine/Math/Geometry.inl>
- #include <Engine/Base/Stream.h>
- #include <Engine/Base/Console.h>
- // define this to dump all triangularization steps to debugging output
- //#define DUMP_ALLSTEPS 1
- //#define OPERATEIN2D 1
- // this must not be caught by the dependency catcher!
- static CTFileName _fnmDebugOutput(CTString("Temp\\Triangularization.out"));
- // set if debug output file is open
- static BOOL _bDebugOutputOpen = FALSE;
- // file where debugging output is written to
- static CTFileStream _strmDebugOutput;
- // edge splitting epsilon
- //#define EPSILON 0.0
- //#define EPSILON (1.0/8388608.0) // 1/2^23
- #define EPSILON (1.0f/65536.0f) // 1/2^16
- //#define EPSILON DOUBLE(0.00390625) // 1/2^8
- //#define EPSILON 0.0009765625f // 1/2^10
- //#define EPSILON 0.03125f // 1/2^5
- //#define EPSILON 0.00390625f // 1/2^8
- // minimum triangle quality that is trivially accepted
- #define QUALITY_ACCEPTTRIVIALLY DOUBLE(0.01)
- // this function is here to satisfy compiler's weird need when compiling
- // the template for CDynamicArray<CBrushVertex *>
- inline void Clear(CBrushVertex *pbvx) {
- (void)pbvx;
- };
- /*
- Algorithm used:
- We take each edge and each vertex that is left of that edge, and try to find out
- if the edge and the vertex can span a triangle on the polygon without itersecting
- any other edges.
- A simple convex BSP is used for testing intersection. The triangle edges are
- represented by planes oriented outwards (to the right side of the edge).
- Triangle quality is defined as division of the area and square of the length of
- the longest edge. We always try to extract the triangle with the best quality. Note
- that clockwise triangles will have negative area.
- Also we remember if there is left and/or right edge that connects
- the edge and the vertex. That makes it posible to geometricaly remove the triangle
- from the array of edges if it satisfies.
- Although it is teoretically true that every polygon can be triangularized this way,
- it is not always possible to find triangle with positive area that doesn't intersect
- any edges in the polygon. This is due to the numerical inprecisions created during CSG
- or similar operations.
- Therefore, we define comparation between two triangles this way:
- (1) Triangle that doesn't intersect any edges is better than one that does.
- (2) When (1) is indeterminate, the one with greater quality is considered better.
- Note that a triangle with negative area can be found best. In such case, it is not really
- created, but only removed from the polygon. This is mostly the case with some small
- degenerate parts of polygons. This way, the problematic case is just eliminated without
- loosing too much precision in the triangularized representation of the polygon.
- This method can diverge by adding and removing same triangle repeatedly. Therefore,
- we don't always start at testing at the first edge in polygon, but move the starting
- point when we find some triangle, 'shuffling' the choices of new triangles that way.
- */
- // a class that handles triangularization of a polygon
- class CTriangularizer {
- public:
- DOUBLE3D tr_vPolygonNormal;
- // reference to original polygon (just for debugging)
- CBrushPolygon &tr_bpoOriginalPolygon;
- // reference to original array of polygon edges
- CStaticArray<CBrushPolygonEdge> &tr_abpeOriginalEdges;
- // temporary array of edges
- CDynamicArray<CBrushEdge> tr_abedEdges;
- // major axes of the polygon plane
- INDEX tr_iAxis0;
- INDEX tr_iAxis1;
-
- // these describe the triangle curently considered
- CBrushEdge *tr_pbedLeft; // left edge of the triangle if found
- CBrushEdge *tr_pbedRight; // right edge of the triangle if found
- CBrushEdge *tr_pbedBottom; // bottom edge of the triangle
- CBrushVertex *tr_pbvxTopVertex; // top vertex of the triangle
- DOUBLEplane3D tr_plLeft; // splitter plane of left edge
- DOUBLEplane3D tr_plRight; // splitter plane of right edge
- DOUBLEplane3D tr_plBottom; // splitter plane of bottom edge
- DOUBLEplane3D tr_plEdgeConsidered; // splitter plane of the edge considered bottom
- // these describe the best triangle found yet
- DOUBLE tr_fQualityBest; // quality of the best triangle
- BOOL tr_bIntersectedBest; // set if best triangle intersects some edges
- CBrushEdge *tr_pbedLeftBest; // left edge of the triangle if found
- CBrushEdge *tr_pbedRightBest; // right edge of the triangle if found
- CBrushEdge *tr_pbedBottomBest; // bottom edge of the triangle
- CBrushVertex *tr_pbvxTopVertexBest; // top vertex of the triangle
- // get a vertex coordinates
- inline DOUBLE3D GetVertex(CBrushVertex *pbvx) const;
- /* Create a splitter plane for an edge. */
- inline void EdgeToPlane(const DOUBLE3D &vVertex0, const DOUBLE3D &vVertex1,
- DOUBLEplane3D &plPlane) const;
- /* Clip edge to a plane and check if something remains behind. */
- BOOL ClipEdge(DOUBLE3D &vVertex0, DOUBLE3D &vVertex1, const DOUBLEplane3D &plPlane) const;
- /* Check if an edge is entirely or partially inside considered triangle. */
- BOOL EdgeInsideTriangle(const CBrushEdge *pbed) const;
- /* Calculate the quality of currently considered triangle. */
- DOUBLE TriangleQuality(void) const;
- /* Check that duplicate or reverse edges do not exist. */
- void CheckForInvalidEdges(void);
- /* Add given edge to temporary array. */
- void AddEdge(CBrushVertex *pbvxVertex0, CBrushVertex *pbvxVertex1);
- /* Create an array of forward oriented edges from polygon edges of a polygon. */
- void MakeEdgesForTriangularization(void);
- /* Test all edges for intersection with considered triangle. */
- BOOL CheckTriangleAgainstEdges(void);
- /* Find best triangle in array of edges. */
- void FindBestTriangle(void);
- /* Find if left/right triangle edges already exist. */
- void FindExistingTriangleEdges(void);
- /* Remove best triangle from edges. */
- void RemoveBestTriangleFromPolygon(void);
- /* Add best triangle to triangles. */
- void AddBestTriangleToTriangles(void);
- /* Print a statement to debugging output file. */
- void DPrintF(char *strFormat, ...);
- /* Dump triangle edges to debug output. */
- void DumpEdges(void);
- // find vertices used without duplication
- void FindVerticesUsed(void);
- // build array of element indices
- void MakeElements(void);
- public:
- // target array for resulting triangles
- CDynamicArray<CBrushVertex *> tr_apbvxTriangleVertices;
- // array for vertices without duplicates
- CStaticStackArray<CBrushVertex *> tr_apbvxVertices;
- // array for triangle elements
- CStaticArray<INDEX> tr_aiElements;
- /* Constructor - do triangularization for a polygon. */
- CTriangularizer(CBrushPolygon &bpoOriginalPolygon);
- INDEX tr_iError;
- };
- // get a vertex coordinates
- inline DOUBLE3D CTriangularizer::GetVertex(CBrushVertex *pbvx) const
- {
- #ifdef OPERATEIN2D
- return DOUBLE3D(
- pbvx->bvx_vdPreciseRelative(tr_iAxis0),
- pbvx->bvx_vdPreciseRelative(tr_iAxis1),
- 0);
- #else
- return pbvx->bvx_vdPreciseRelative;
- #endif
- }
-
- /*
- * Create a splitter plane for an edge.
- */
- inline void CTriangularizer::EdgeToPlane(const DOUBLE3D &vVertex0, const DOUBLE3D &vVertex1,
- DOUBLEplane3D &plPlane) const
- {
- // make a plane containing the edge, perpendicular to the polygon plane, looking
- // to the right of the edge
- plPlane = DOUBLEplane3D((vVertex1-vVertex0)*tr_vPolygonNormal, vVertex0);
- }
- /*
- * Clip edge to a plane and check if something remains behind.
- */
- BOOL CTriangularizer::ClipEdge(DOUBLE3D &vVertex0, DOUBLE3D &vVertex1,
- const DOUBLEplane3D &plPlane) const
- {
- // calculate point distances from clip plane
- DOUBLE fDistance0 = plPlane.PointDistance(vVertex0);
- DOUBLE fDistance1 = plPlane.PointDistance(vVertex1);
-
- /* ---- first point behind plane ---- */
- if (fDistance0 < -EPSILON) {
- // if both are back
- if (fDistance1 < -EPSILON) {
- // whole edge is behind
- return TRUE;
- // if first is back, second front
- } else if (fDistance1 > +EPSILON) {
- // calculate intersection coordinates
- vVertex1 = vVertex0-(vVertex0-vVertex1)*fDistance0/(fDistance0-fDistance1);
- // the remaining part is behind
- return TRUE;
- // if first is back, second on the plane
- } else {
- // the whole edge is back
- return TRUE;
- }
- /* ---- first point in front of plane ---- */
- } else if (fDistance0 > +EPSILON) {
- // if first is front, second back
- if (fDistance1 < -EPSILON) {
- // calculate intersection coordinates
- vVertex0 = vVertex1-(vVertex1-vVertex0)*fDistance1/(fDistance1-fDistance0);
- // the remaining part is behind
- return TRUE;
- // if both are front
- } else if (fDistance1 > +EPSILON) {
- // whole edge is front
- return FALSE;
- // if first is front, second on the plane
- } else {
- // whole edge is front
- return FALSE;
- }
- /* ---- first point on the plane ---- */
- } else {
- // if first is on the plane, second back
- if (fDistance1 < -EPSILON) {
- // the whole edge is back
- return TRUE;
- // if first is on the plane, second front
- } else if (fDistance1 > +EPSILON) {
- // whole edge is front
- return FALSE;
- // if both are on the plane
- } else {
- // assume the whole edge is back (!!!! is this correct?)
- return TRUE;
- }
- }
- }
- /*
- * Check if an edge is entirely or partially inside considered triangle.
- */
- BOOL CTriangularizer::EdgeInsideTriangle(const CBrushEdge *pbed) const
- {
- // copy edge vertices
- DOUBLE3D vVertex0, vVertex1;
- vVertex0 = GetVertex(pbed->bed_pbvxVertex0);
- vVertex1 = GetVertex(pbed->bed_pbvxVertex1);
- // try to clip it to each triangle edge in turn,
- // if anything remains behind - it is inside
- return (ClipEdge(vVertex0, vVertex1, tr_plLeft)
- &&ClipEdge(vVertex0, vVertex1, tr_plRight)
- &&ClipEdge(vVertex0, vVertex1, tr_plBottom));
- }
- /*
- * Calculate the quality of currently considered triangle.
- */
- DOUBLE CTriangularizer::TriangleQuality(void) const
- {
- DOUBLE3D vEdgeBottom = GetVertex(tr_pbedBottom->bed_pbvxVertex1)
- - GetVertex(tr_pbedBottom->bed_pbvxVertex0);
- DOUBLE3D vEdgeLeft = GetVertex(tr_pbedBottom->bed_pbvxVertex0)
- - GetVertex(tr_pbvxTopVertex);
- DOUBLE3D vEdgeRight = GetVertex(tr_pbvxTopVertex)
- - GetVertex(tr_pbedBottom->bed_pbvxVertex1);
- // calculate triangle normal as cross product of two edges
- DOUBLE3D vNormal = vEdgeLeft*(-vEdgeRight);
- // calculate area as half the length of the normal
- DOUBLE fArea = vNormal.Length()/ DOUBLE(2.0);
- // area must be initially positive
- ASSERT(fArea>=0);
- // if triangle normal is opposite to the polygon normal
- if (vNormal%tr_vPolygonNormal<0) {
- // make area negative
- fArea = -fArea;
- }
- // find length of all edges
- DOUBLE fLengthBottom = vEdgeBottom.Length();
- DOUBLE fLengthLeft = vEdgeLeft .Length();
- DOUBLE fLengthRight = vEdgeRight .Length();
- // find maximum length
- DOUBLE fLengthMax = Max(fLengthBottom, Max(fLengthLeft, fLengthRight));
- // maximum length must be positive
- ASSERT(fLengthMax>0.0f);
- // quality is division of the area and square of the length of the longest edge
- return fArea/(fLengthMax*fLengthMax);
- }
- /*
- * Create an array of forward oriented edges from polygon edges of a polygon.
- */
- void CTriangularizer::MakeEdgesForTriangularization(void)
- {
- // get number of edges in polygon
- INDEX ctEdges = tr_abpeOriginalEdges.Count();
- // create that much edges in the array
- CBrushEdge *pbedEdges = tr_abedEdges.New(ctEdges);
- tr_abedEdges.Lock();
- // for each edge
- for(INDEX iEdge=0; iEdge<ctEdges; iEdge++) {
- CBrushPolygonEdge &bpe = tr_abpeOriginalEdges[iEdge];
- CBrushEdge &bed = tr_abedEdges[iEdge];
- // if polygon edge is reversed
- if (bpe.bpe_bReverse) {
- // make edge in array reversed
- bed.bed_pbvxVertex0 = bpe.bpe_pbedEdge->bed_pbvxVertex1;
- bed.bed_pbvxVertex1 = bpe.bpe_pbedEdge->bed_pbvxVertex0;
- // if polygon edge is not reversed
- } else {
- // make edge in array normal
- bed.bed_pbvxVertex0 = bpe.bpe_pbedEdge->bed_pbvxVertex0;
- bed.bed_pbvxVertex1 = bpe.bpe_pbedEdge->bed_pbvxVertex1;
- }
- }
- tr_abedEdges.Unlock();
- }
- /*
- * Add given edge to temporary array.
- */
- void CTriangularizer::AddEdge(CBrushVertex *pbvxVertex0, CBrushVertex *pbvxVertex1)
- {
- #ifndef NDEBUG
- // for each edge
- {FOREACHINDYNAMICARRAY(tr_abedEdges, CBrushEdge, itbed) {
- // must not exist
- if (itbed->bed_pbvxVertex0 == pbvxVertex0
- &&itbed->bed_pbvxVertex1 == pbvxVertex1) {
- return;
- }
- ASSERT(itbed->bed_pbvxVertex0 != pbvxVertex0
- ||itbed->bed_pbvxVertex1 != pbvxVertex1);
- // must not have reverse
- ASSERT(itbed->bed_pbvxVertex0 != pbvxVertex1
- ||itbed->bed_pbvxVertex1 != pbvxVertex0);
- }}
- #endif
- // add new edge
- CBrushEdge *pbedNew = tr_abedEdges.New(1);
- pbedNew->bed_pbvxVertex0 = pbvxVertex0;
- pbedNew->bed_pbvxVertex1 = pbvxVertex1;
- }
- /*
- * Check that duplicate or reverse edges do not exist.
- */
- void CTriangularizer::CheckForInvalidEdges(void)
- {
- // for each edge
- {FOREACHINDYNAMICARRAY(tr_abedEdges, CBrushEdge, itbed1) {
- // for each edge
- {FOREACHINDYNAMICARRAY(tr_abedEdges, CBrushEdge, itbed2) {
- // if same edge
- if (&*itbed1 == &*itbed2) {
- // skip
- continue;
- }
- CBrushEdge *pbed1 = itbed1;
- CBrushEdge *pbed2 = itbed2;
- // must not exist
- ASSERT(pbed1->bed_pbvxVertex0 != pbed2->bed_pbvxVertex0
- ||pbed1->bed_pbvxVertex1 != pbed2->bed_pbvxVertex1);
- // must not have reverse
- ASSERT(pbed1->bed_pbvxVertex0 != pbed2->bed_pbvxVertex1
- ||pbed1->bed_pbvxVertex1 != pbed2->bed_pbvxVertex0);
- }}
- }}
- }
- /*
- * Add best triangle to triangles.
- */
- void CTriangularizer::AddBestTriangleToTriangles(void)
- {
- // add the triangle to triangles
- CBrushVertex **ppbvxTriangle = tr_apbvxTriangleVertices.New(3);
- ppbvxTriangle[0] = tr_pbedBottomBest->bed_pbvxVertex0;
- ppbvxTriangle[1] = tr_pbedBottomBest->bed_pbvxVertex1;
- ppbvxTriangle[2] = tr_pbvxTopVertexBest;
- }
- /*
- * Remove best triangle from edges.
- */
- void CTriangularizer::RemoveBestTriangleFromPolygon(void)
- {
- tr_pbedBottom = tr_pbedBottomBest;
- tr_pbvxTopVertex = tr_pbvxTopVertexBest;
- FindExistingTriangleEdges();
- tr_pbedLeftBest = tr_pbedLeft;
- tr_pbedRightBest = tr_pbedRight;
- tr_pbedBottomBest = tr_pbedBottom;
- tr_pbvxTopVertexBest = tr_pbvxTopVertex;
- // if left edge was found
- if (tr_pbedLeftBest!=NULL) {
- // remove the left edge from the edges
- tr_abedEdges.Delete(tr_pbedLeftBest);
- // if left edge was not found
- } else {
- // add reverse of the left edge to the edges
- AddEdge(tr_pbedBottomBest->bed_pbvxVertex0, tr_pbvxTopVertexBest);
- }
- // if right edge was found
- if (tr_pbedRightBest!=NULL) {
- // remove the right edge from the edges
- tr_abedEdges.Delete(tr_pbedRightBest);
- // if right edge was not found
- } else {
- // add reverse of the right edge to the edges
- AddEdge(tr_pbvxTopVertexBest, tr_pbedBottomBest->bed_pbvxVertex1);
- }
- // remove the base edge from the edges
- tr_abedEdges.Delete(tr_pbedBottomBest);
- }
- /*
- * Find if left/right triangle edges already exist.
- */
- void CTriangularizer::FindExistingTriangleEdges(void)
- {
- // initialize left edge as not existing
- tr_pbedLeft = NULL;
- // initialize right edge as not existing
- tr_pbedRight = NULL;
- // for each edge
- FOREACHINDYNAMICARRAY(tr_abedEdges, CBrushEdge, itbed) {
- CBrushEdge *pbed = itbed;
- // if it is the bottom edge of the triangle
- if (tr_pbedBottom == itbed) {
- // do not test it for intersection
- NOTHING;
- // if it is not bottom edge of triangle
- } else {
- ASSERT(itbed->bed_pbvxVertex0 != tr_pbedBottom->bed_pbvxVertex0
- ||itbed->bed_pbvxVertex1 != tr_pbedBottom->bed_pbvxVertex1);
- // if it is the left edge of the triangle
- if (itbed->bed_pbvxVertex1 == tr_pbedBottom->bed_pbvxVertex0
- &&itbed->bed_pbvxVertex0 == tr_pbvxTopVertex) {
- // remember it as the left edge
- ASSERT(tr_pbedLeft==NULL);
- tr_pbedLeft = itbed;
- // if it is the right edge of the triangle
- } else if (itbed->bed_pbvxVertex0 == tr_pbedBottom->bed_pbvxVertex1
- &&itbed->bed_pbvxVertex1 == tr_pbvxTopVertex) {
- // remember it as the right edge
- ASSERT(tr_pbedRight==NULL);
- tr_pbedRight = itbed;
- }
- }
- }
- }
- /*
- * Test all edges for intersection with considered triangle.
- */
- BOOL CTriangularizer::CheckTriangleAgainstEdges(void)
- {
- // for each edge
- FOREACHINDYNAMICARRAY(tr_abedEdges, CBrushEdge, itbed) {
- CBrushEdge *pbed = itbed;
- // if it is the bottom edge of the triangle
- if (tr_pbedBottom == itbed) {
- // do not test it for intersection
- NOTHING;
- // if it is not bottom edge of triangle
- } else {
- ASSERT(itbed->bed_pbvxVertex0 != tr_pbedBottom->bed_pbvxVertex0
- ||itbed->bed_pbvxVertex1 != tr_pbedBottom->bed_pbvxVertex1);
- // if it is the left edge of the triangle
- if (itbed->bed_pbvxVertex1 == tr_pbedBottom->bed_pbvxVertex0
- &&itbed->bed_pbvxVertex0 == tr_pbvxTopVertex) {
- // do not test it for intersection
- NOTHING;
- // if it is the right edge of the triangle
- } else if (itbed->bed_pbvxVertex0 == tr_pbedBottom->bed_pbvxVertex1
- &&itbed->bed_pbvxVertex1 == tr_pbvxTopVertex) {
- // do not test it for intersection
- NOTHING;
- // if it is neither of triangle edges
- } else {
- // if this edge intersects with any of the triangle edges
- if (EdgeInsideTriangle(itbed)) {
- return TRUE;
- }
- }
- }
- }
- // if no intersections are found
- return FALSE;
- }
- // calculate edge direction (COPIED FROM CTMATH!)
- void EdgeDir(const DOUBLE3D &vPoint0, const DOUBLE3D &vPoint1,
- DOUBLE3D &vDirection, DOUBLE3D &vReferencePoint)
- {
- // if the vertices are reversed
- /* if (CompareVertices(vPoint0, vPoint1)<0) {
- // swap them
- Swap(vPoint0, vPoint1);
- // mark edge as reversed
- edx_bReverse = TRUE;
- // if the vertices are not reversed
- } else {
- // mark edge as not reversed
- edx_bReverse = FALSE;
- }
- */
- // normalize the direction
- vDirection = (vPoint1-vPoint0).Normalize();
- /* calculate the reference point on the line from any point on line (p) and direction (d):
- r = p - ((p.d)/(d.d))*d
- */
- vReferencePoint = vPoint0 -
- vDirection * ((vPoint0 % vDirection))/(vDirection % vDirection);
- }
- /*
- * Print a statement to debugging output file.
- */
- void CTriangularizer::DPrintF(char *strFormat, ...)
- {
- char strBuffer[256];
- // format the message in buffer
- va_list arg;
- va_start(arg, strFormat);
- vsprintf(strBuffer, strFormat, arg);
- // if the debug output file is not open
- if (!_bDebugOutputOpen) {
- // open it
- try {
- _strmDebugOutput.Create_t(_fnmDebugOutput, CTStream::CM_TEXT);
- _bDebugOutputOpen = TRUE;
- // if not successful
- } catch (char *strError) {
- (void) strError;
- // print nothing
- return;
- }
- }
- // write the message to the file
- _strmDebugOutput.Write_t(strBuffer, strlen(strBuffer));
- }
- /*
- * Dump triangle edges to debug output.
- */
- void CTriangularizer::DumpEdges(void)
- {
- DPrintF("%d\n", tr_abedEdges.Count());
- FOREACHINDYNAMICARRAY(tr_abedEdges, CBrushEdge, itbed) {
- DPrintF("(%f, %f) ->",
- GetVertex(itbed->bed_pbvxVertex0)(1),
- GetVertex(itbed->bed_pbvxVertex0)(2));
- DPrintF(" (%f, %f) ",
- GetVertex(itbed->bed_pbvxVertex1)(1),
- GetVertex(itbed->bed_pbvxVertex1)(2));
- DOUBLE3D vDirection, vReferencePoint;
- EdgeDir(
- GetVertex(itbed->bed_pbvxVertex0), GetVertex(itbed->bed_pbvxVertex1),
- vDirection, vReferencePoint);
- DPrintF("[(%f, %f), ",
- vDirection(1),
- vDirection(2));
- DPrintF("(%f, %f)]\n",
- vReferencePoint(1),
- vReferencePoint(2));
- }
- }
- // edge offsets used to 'shuffle' triangularization
- static INDEX iBottomEdgeOffset;
- static INDEX iTopEdgeOffset;
- /*
- * Find best triangle in array of edges.
- */
- /* NOTE: Currently only searching for first acceptable triangle.
- */
- void CTriangularizer::FindBestTriangle(void)
- {
- // clear best triangle description
- tr_fQualityBest = DOUBLE(-1E30);
- tr_bIntersectedBest = TRUE;
- tr_pbedLeftBest = NULL;
- tr_pbedRightBest = NULL;
- tr_pbedBottomBest = NULL;
- tr_pbvxTopVertexBest = NULL;
- iBottomEdgeOffset++;
- // for each edge
- tr_abedEdges.Lock();
- INDEX ctEdges = tr_abedEdges.Count();
- for(INDEX ibedBottom=0; ibedBottom<tr_abedEdges.Count(); ibedBottom++) {
- // consider this edge as bottom edge of triangle
- tr_pbedBottom = &tr_abedEdges[(ibedBottom+iBottomEdgeOffset)%ctEdges];
- ASSERT(tr_pbedBottom->bed_pbvxVertex0 != tr_pbedBottom->bed_pbvxVertex1);
- // create bottom splitter
- EdgeToPlane(GetVertex(tr_pbedBottom->bed_pbvxVertex0),
- GetVertex(tr_pbedBottom->bed_pbvxVertex1), tr_plEdgeConsidered);
- iTopEdgeOffset++;
- // for each edge
- for(INDEX ibedTop=0; ibedTop<ctEdges; ibedTop++) {
- // consider first vertex of this edge as top vertex of triangle
- tr_pbvxTopVertex = tr_abedEdges[(ibedTop+ibedBottom+iTopEdgeOffset)%ctEdges].bed_pbvxVertex0;
- // if the top vertex is one of the vertices of bottom edge
- if ( tr_pbvxTopVertex == tr_pbedBottom->bed_pbvxVertex0
- || tr_pbvxTopVertex == tr_pbedBottom->bed_pbvxVertex1) {
- // skip this triangle
- continue;
- }
-
- // create bottom splitter from the bottom edge splitter
- tr_plBottom = tr_plEdgeConsidered;
- // create left splitter
- EdgeToPlane(GetVertex(tr_pbvxTopVertex),
- GetVertex(tr_pbedBottom->bed_pbvxVertex0), tr_plLeft);
- // create right splitter
- EdgeToPlane(GetVertex(tr_pbedBottom->bed_pbvxVertex1),
- GetVertex(tr_pbvxTopVertex), tr_plRight);
- // calculate its quality
- DOUBLE fCurrentQuality = TriangleQuality();
- // if triangle is clockwise
- if (fCurrentQuality<0) {
- continue;
- // reverse its splitters
- tr_plLeft.Flip();
- tr_plRight.Flip();
- tr_plBottom.Flip();
- }
- // check if no edges intersect with considered triangle
- BOOL bCurrentIntersected = CheckTriangleAgainstEdges();
- // if the current triangle is better than the best triangle yet
- if ((tr_bIntersectedBest && !bCurrentIntersected)
- ||(tr_bIntersectedBest==bCurrentIntersected && fCurrentQuality>tr_fQualityBest)) {
- // find if left/right triangle edges already exist.
- FindExistingTriangleEdges();
- // set current triangle as best triangle
- tr_bIntersectedBest = bCurrentIntersected;
- tr_fQualityBest = fCurrentQuality;
- tr_pbedLeftBest = tr_pbedLeft;
- tr_pbedRightBest = tr_pbedRight;
- tr_pbedBottomBest = tr_pbedBottom;
- tr_pbvxTopVertexBest = tr_pbvxTopVertex;
- // if the triangle is trivially acceptable
- if (!bCurrentIntersected && fCurrentQuality>=QUALITY_ACCEPTTRIVIALLY) {
- // finish searching
- tr_abedEdges.Unlock();
- return;
- }
- }
- }
- }
- tr_abedEdges.Unlock();
- #if 0
- // if no acceptable triangles have been found
- if (tr_fQualityBest<???) {
- /* dump all sector's vertices */
- /*
- FOREACHINSTATICARRAY(tr_bpoOriginalPolygon.bpo_pbscSector->bsc_abvxVertices,
- CBrushVertex, itbvx) {
- DPrintF("(0x%p, %f, %f, %f)\n", &(*itbvx),
- (*itbvx)(1), (*itbvx)(2), (*itbvx)(3));
- }
- */
- // dump remaining edges
- DPrintF("Triangularization failed!\n");
- DPrintF("best quality found: %f\n", tr_fQualityBest);
- DPrintF("Remaining edges:\n");
- DumpEdges();
- /* // dump all edges
- tr_abedEdges.Clear();
- MakeEdgesForTriangularization();
- DumpEdges();
- */
- // error!
- ASSERTALWAYS("No acceptable triangles found for triangularization!");
- FatalError("No acceptable triangles found for triangularization!\n"
- "Debugging information written to file '%s'.", _fnmDebugOutput);
- }
- #endif
- }
- // find vertices used without duplication
- void CTriangularizer::FindVerticesUsed(void)
- {
- // make an empty array that will contain the vertices
- tr_apbvxVertices.PopAll();
- // for each triangle vertex
- tr_apbvxTriangleVertices.Lock();
- for(INDEX ipbvx=0; ipbvx<tr_apbvxTriangleVertices.Count(); ipbvx++) {
- CBrushVertex *pbvx = tr_apbvxTriangleVertices[ipbvx];
- // for each vertex already added
- BOOL bFound=FALSE;
- for(INDEX ipbvxAdded=0; ipbvxAdded<tr_apbvxVertices.Count(); ipbvxAdded++) {
- // if it is the same one, stop searching
- if (tr_apbvxVertices[ipbvxAdded]==pbvx) {
- bFound = TRUE;
- break;
- }
- }
- // if not found
- if (!bFound) {
- // add it to the array
- tr_apbvxVertices.Push() = pbvx;
- }
- }
- tr_apbvxTriangleVertices.Unlock();
- }
- // build array of element indices
- void CTriangularizer::MakeElements(void)
- {
- // make elements array
- INDEX ctElements = tr_apbvxTriangleVertices.Count();
- tr_aiElements.Clear();
- tr_aiElements.New(ctElements);
- // for each triangle vertex
- tr_apbvxTriangleVertices.Lock();
- for(INDEX i=0; i<ctElements; i++) {
- CBrushVertex *pbvx = tr_apbvxTriangleVertices[i];
- // find its element index
- BOOL bFound=FALSE;
- for(INDEX ipbvx=0; ipbvx<tr_apbvxVertices.Count(); ipbvx++) {
- // if it is the same one, stop searching
- if (tr_apbvxVertices[ipbvx]==pbvx) {
- tr_aiElements[i] = ipbvx;
- bFound = TRUE;
- break;
- }
- }
- ASSERT(bFound);
- }
- tr_apbvxTriangleVertices.Unlock();
- }
- /*
- * Constructor - do triangularization for a polygon.
- */
- CTriangularizer::CTriangularizer(CBrushPolygon &bpoOriginalPolygon)
- : tr_bpoOriginalPolygon(bpoOriginalPolygon)
- , tr_abpeOriginalEdges(bpoOriginalPolygon.bpo_abpePolygonEdges) // remember original edges
- {
- // find polygon normal and major axes
- #ifdef OPERATEIN2D
- INDEX iMaxNormal = bpoOriginalPolygon.bpo_pbplPlane->bpl_pldPreciseRelative.GetMaxNormal();
- INDEX iMaxSign = Sgn(bpoOriginalPolygon.bpo_pbplPlane->bpl_pldPreciseRelative(iMaxNormal));
- ASSERT(iMaxSign!=0);
- switch(iMaxNormal) {
- case 1:
- if (iMaxSign==-1) {
- tr_iAxis0 = 3; tr_iAxis1 = 2; tr_vPolygonNormal = DOUBLE3D(-1,0,0);
- } else {
- tr_iAxis0 = 2; tr_iAxis1 = 3; tr_vPolygonNormal = DOUBLE3D(1,0,0);
- }
- break;
- case 2:
- if (iMaxSign==-1) {
- tr_iAxis0 = 1; tr_iAxis1 = 3; tr_vPolygonNormal = DOUBLE3D(0,-1,0);
- } else {
- tr_iAxis0 = 3; tr_iAxis1 = 1; tr_vPolygonNormal = DOUBLE3D(0,1,0);
- }
- break;
- case 3:
- if (iMaxSign==-1) {
- tr_iAxis0 = 2; tr_iAxis1 = 1; tr_vPolygonNormal = DOUBLE3D(0,0,-1);
- } else {
- tr_iAxis0 = 1; tr_iAxis1 = 2; tr_vPolygonNormal = DOUBLE3D(0,0,1);
- }
- break;
- default:
- ASSERT(FALSE);
- tr_iAxis0 = 2; tr_iAxis1 = 3; tr_vPolygonNormal = DOUBLE3D(1,0,0);
- }
- tr_vPolygonNormal = DOUBLE3D(0,0,1);
- #else
- tr_iAxis0 = -1; tr_iAxis1 = -1; tr_vPolygonNormal = bpoOriginalPolygon.bpo_pbplPlane->bpl_pldPreciseRelative;
- #endif
-
- // create a dynamic array of edges
- MakeEdgesForTriangularization();
- tr_iError = -1;
- // estimate max number of triangles
- INDEX ctOrgEdges = tr_abpeOriginalEdges.Count();
- INDEX ctMaxHoles = ctOrgEdges;
- INDEX ctMaxTriangles = ctOrgEdges-2+2*ctMaxHoles;
- iBottomEdgeOffset = 0;
- iTopEdgeOffset = 0;
- #ifdef DUMP_ALLSTEPS
- DPrintF("PolygonBegin\n");
- #endif
- // ASSERT(tr_abedEdges.Count()!=8);
- // while the array of edges is not empty
- //*
- INDEX iPasses = 0;
- while (tr_abedEdges.Count()>0) {
- // if total number of triangles is too large, or searching too long
- INDEX ctTriangles = tr_apbvxTriangleVertices.Count()/3;
- if (ctTriangles>ctMaxTriangles*2 || iPasses>ctMaxTriangles*2) {
- // error, quit triangulation
- tr_iError = 2;
- return;
- }
- #ifdef DUMP_ALLSTEPS
- // dump remaining edges
- DumpEdges();
- #endif
- // find best triangle
- FindBestTriangle();
- #ifdef DUMP_ALLSTEPS
- DPrintF("BestQuality=%f\n", tr_fQualityBest);
- #endif
- // if no triangle is found
- if (tr_fQualityBest<0.0) {
- // quit searching
- tr_iError = 1;
- return;
- }
- if (tr_bIntersectedBest) {
- // quit searching
- tr_iError = 3;
- return;
- }
- // if best triangle has positive quality
- if (tr_fQualityBest>0.0) {
- // add it to triangles
- AddBestTriangleToTriangles();
- }
- // remove best triangle from edges
- RemoveBestTriangleFromPolygon();
- #ifndef NDEBUG
- // check that there are no invalid edges after creating
- CheckForInvalidEdges();
- #endif
- iPasses++;
- }
- tr_iError = 0;
- //*/
- }
- /*
- * Make triangular representation of the polygon.
- */
- void CBrushPolygon::Triangulate(void)
- {
- // if already triangulated
- if (bpo_apbvxTriangleVertices.Count()>0) {
- // do nothing
- return;
- }
- // triangularize the polygon
- CTriangularizer tr(*this);
- // find vertices used without duplication
- tr.FindVerticesUsed();
- // build array of element indices
- tr.MakeElements();
- // if there was an error
- if (tr.tr_iError!=0) {
- // report it
- // CPrintF( TRANS("Cannot properly triangulate a polygon: error %d!\n"), tr.tr_iError);
- // mark the polygon
- bpo_ulFlags|=BPOF_INVALIDTRIANGLES;
- // if there no errors
- } else {
- // mark the polygon as triangulated well
- bpo_ulFlags&=~BPOF_INVALIDTRIANGLES;
- }
- // copy dynamic array of triangles to the triangular representation of the polygon
- INDEX ctVertices = tr.tr_apbvxVertices.Count();
- bpo_apbvxTriangleVertices.Clear();
- bpo_apbvxTriangleVertices.New(ctVertices);
- for (INDEX iVertex=0; iVertex<ctVertices; iVertex++) {
- bpo_apbvxTriangleVertices[iVertex] = tr.tr_apbvxVertices[iVertex];
- }
- bpo_aiTriangleElements = tr.tr_aiElements;
- if (_bDebugOutputOpen) {
- _strmDebugOutput.Close();
- _bDebugOutputOpen = FALSE;
- }
- }
|