BrushTriangularize.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. /* Copyright (c) 2002-2012 Croteam Ltd.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of version 2 of the GNU General Public License as published by
  4. the Free Software Foundation
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along
  10. with this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  12. #include "stdh.h"
  13. #include <Engine/Brushes/Brush.h>
  14. #include <Engine/Templates/DynamicArray.cpp>
  15. #include <Engine/Templates/StaticArray.cpp>
  16. #include <Engine/Templates/StaticStackArray.cpp>
  17. #include <Engine/Math/Float.h>
  18. #include <Engine/Math/Geometry.inl>
  19. #include <Engine/Base/Stream.h>
  20. #include <Engine/Base/Console.h>
  21. // define this to dump all triangularization steps to debugging output
  22. //#define DUMP_ALLSTEPS 1
  23. //#define OPERATEIN2D 1
  24. // this must not be caught by the dependency catcher!
  25. static CTFileName _fnmDebugOutput(CTString("Temp\\Triangularization.out"));
  26. // set if debug output file is open
  27. static BOOL _bDebugOutputOpen = FALSE;
  28. // file where debugging output is written to
  29. static CTFileStream _strmDebugOutput;
  30. // edge splitting epsilon
  31. //#define EPSILON 0.0
  32. //#define EPSILON (1.0/8388608.0) // 1/2^23
  33. #define EPSILON (1.0f/65536.0f) // 1/2^16
  34. //#define EPSILON DOUBLE(0.00390625) // 1/2^8
  35. //#define EPSILON 0.0009765625f // 1/2^10
  36. //#define EPSILON 0.03125f // 1/2^5
  37. //#define EPSILON 0.00390625f // 1/2^8
  38. // minimum triangle quality that is trivially accepted
  39. #define QUALITY_ACCEPTTRIVIALLY DOUBLE(0.01)
  40. // this function is here to satisfy compiler's weird need when compiling
  41. // the template for CDynamicArray<CBrushVertex *>
  42. inline void Clear(CBrushVertex *pbvx) {
  43. (void)pbvx;
  44. };
  45. /*
  46. Algorithm used:
  47. We take each edge and each vertex that is left of that edge, and try to find out
  48. if the edge and the vertex can span a triangle on the polygon without itersecting
  49. any other edges.
  50. A simple convex BSP is used for testing intersection. The triangle edges are
  51. represented by planes oriented outwards (to the right side of the edge).
  52. Triangle quality is defined as division of the area and square of the length of
  53. the longest edge. We always try to extract the triangle with the best quality. Note
  54. that clockwise triangles will have negative area.
  55. Also we remember if there is left and/or right edge that connects
  56. the edge and the vertex. That makes it posible to geometricaly remove the triangle
  57. from the array of edges if it satisfies.
  58. Although it is teoretically true that every polygon can be triangularized this way,
  59. it is not always possible to find triangle with positive area that doesn't intersect
  60. any edges in the polygon. This is due to the numerical inprecisions created during CSG
  61. or similar operations.
  62. Therefore, we define comparation between two triangles this way:
  63. (1) Triangle that doesn't intersect any edges is better than one that does.
  64. (2) When (1) is indeterminate, the one with greater quality is considered better.
  65. Note that a triangle with negative area can be found best. In such case, it is not really
  66. created, but only removed from the polygon. This is mostly the case with some small
  67. degenerate parts of polygons. This way, the problematic case is just eliminated without
  68. loosing too much precision in the triangularized representation of the polygon.
  69. This method can diverge by adding and removing same triangle repeatedly. Therefore,
  70. we don't always start at testing at the first edge in polygon, but move the starting
  71. point when we find some triangle, 'shuffling' the choices of new triangles that way.
  72. */
  73. // a class that handles triangularization of a polygon
  74. class CTriangularizer {
  75. public:
  76. DOUBLE3D tr_vPolygonNormal;
  77. // reference to original polygon (just for debugging)
  78. CBrushPolygon &tr_bpoOriginalPolygon;
  79. // reference to original array of polygon edges
  80. CStaticArray<CBrushPolygonEdge> &tr_abpeOriginalEdges;
  81. // temporary array of edges
  82. CDynamicArray<CBrushEdge> tr_abedEdges;
  83. // major axes of the polygon plane
  84. INDEX tr_iAxis0;
  85. INDEX tr_iAxis1;
  86. // these describe the triangle curently considered
  87. CBrushEdge *tr_pbedLeft; // left edge of the triangle if found
  88. CBrushEdge *tr_pbedRight; // right edge of the triangle if found
  89. CBrushEdge *tr_pbedBottom; // bottom edge of the triangle
  90. CBrushVertex *tr_pbvxTopVertex; // top vertex of the triangle
  91. DOUBLEplane3D tr_plLeft; // splitter plane of left edge
  92. DOUBLEplane3D tr_plRight; // splitter plane of right edge
  93. DOUBLEplane3D tr_plBottom; // splitter plane of bottom edge
  94. DOUBLEplane3D tr_plEdgeConsidered; // splitter plane of the edge considered bottom
  95. // these describe the best triangle found yet
  96. DOUBLE tr_fQualityBest; // quality of the best triangle
  97. BOOL tr_bIntersectedBest; // set if best triangle intersects some edges
  98. CBrushEdge *tr_pbedLeftBest; // left edge of the triangle if found
  99. CBrushEdge *tr_pbedRightBest; // right edge of the triangle if found
  100. CBrushEdge *tr_pbedBottomBest; // bottom edge of the triangle
  101. CBrushVertex *tr_pbvxTopVertexBest; // top vertex of the triangle
  102. // get a vertex coordinates
  103. inline DOUBLE3D GetVertex(CBrushVertex *pbvx) const;
  104. /* Create a splitter plane for an edge. */
  105. inline void EdgeToPlane(const DOUBLE3D &vVertex0, const DOUBLE3D &vVertex1,
  106. DOUBLEplane3D &plPlane) const;
  107. /* Clip edge to a plane and check if something remains behind. */
  108. BOOL ClipEdge(DOUBLE3D &vVertex0, DOUBLE3D &vVertex1, const DOUBLEplane3D &plPlane) const;
  109. /* Check if an edge is entirely or partially inside considered triangle. */
  110. BOOL EdgeInsideTriangle(const CBrushEdge *pbed) const;
  111. /* Calculate the quality of currently considered triangle. */
  112. DOUBLE TriangleQuality(void) const;
  113. /* Check that duplicate or reverse edges do not exist. */
  114. void CheckForInvalidEdges(void);
  115. /* Add given edge to temporary array. */
  116. void AddEdge(CBrushVertex *pbvxVertex0, CBrushVertex *pbvxVertex1);
  117. /* Create an array of forward oriented edges from polygon edges of a polygon. */
  118. void MakeEdgesForTriangularization(void);
  119. /* Test all edges for intersection with considered triangle. */
  120. BOOL CheckTriangleAgainstEdges(void);
  121. /* Find best triangle in array of edges. */
  122. void FindBestTriangle(void);
  123. /* Find if left/right triangle edges already exist. */
  124. void FindExistingTriangleEdges(void);
  125. /* Remove best triangle from edges. */
  126. void RemoveBestTriangleFromPolygon(void);
  127. /* Add best triangle to triangles. */
  128. void AddBestTriangleToTriangles(void);
  129. /* Print a statement to debugging output file. */
  130. void DPrintF(char *strFormat, ...);
  131. /* Dump triangle edges to debug output. */
  132. void DumpEdges(void);
  133. // find vertices used without duplication
  134. void FindVerticesUsed(void);
  135. // build array of element indices
  136. void MakeElements(void);
  137. public:
  138. // target array for resulting triangles
  139. CDynamicArray<CBrushVertex *> tr_apbvxTriangleVertices;
  140. // array for vertices without duplicates
  141. CStaticStackArray<CBrushVertex *> tr_apbvxVertices;
  142. // array for triangle elements
  143. CStaticArray<INDEX> tr_aiElements;
  144. /* Constructor - do triangularization for a polygon. */
  145. CTriangularizer(CBrushPolygon &bpoOriginalPolygon);
  146. INDEX tr_iError;
  147. };
  148. // get a vertex coordinates
  149. inline DOUBLE3D CTriangularizer::GetVertex(CBrushVertex *pbvx) const
  150. {
  151. #ifdef OPERATEIN2D
  152. return DOUBLE3D(
  153. pbvx->bvx_vdPreciseRelative(tr_iAxis0),
  154. pbvx->bvx_vdPreciseRelative(tr_iAxis1),
  155. 0);
  156. #else
  157. return pbvx->bvx_vdPreciseRelative;
  158. #endif
  159. }
  160. /*
  161. * Create a splitter plane for an edge.
  162. */
  163. inline void CTriangularizer::EdgeToPlane(const DOUBLE3D &vVertex0, const DOUBLE3D &vVertex1,
  164. DOUBLEplane3D &plPlane) const
  165. {
  166. // make a plane containing the edge, perpendicular to the polygon plane, looking
  167. // to the right of the edge
  168. plPlane = DOUBLEplane3D((vVertex1-vVertex0)*tr_vPolygonNormal, vVertex0);
  169. }
  170. /*
  171. * Clip edge to a plane and check if something remains behind.
  172. */
  173. BOOL CTriangularizer::ClipEdge(DOUBLE3D &vVertex0, DOUBLE3D &vVertex1,
  174. const DOUBLEplane3D &plPlane) const
  175. {
  176. // calculate point distances from clip plane
  177. DOUBLE fDistance0 = plPlane.PointDistance(vVertex0);
  178. DOUBLE fDistance1 = plPlane.PointDistance(vVertex1);
  179. /* ---- first point behind plane ---- */
  180. if (fDistance0 < -EPSILON) {
  181. // if both are back
  182. if (fDistance1 < -EPSILON) {
  183. // whole edge is behind
  184. return TRUE;
  185. // if first is back, second front
  186. } else if (fDistance1 > +EPSILON) {
  187. // calculate intersection coordinates
  188. vVertex1 = vVertex0-(vVertex0-vVertex1)*fDistance0/(fDistance0-fDistance1);
  189. // the remaining part is behind
  190. return TRUE;
  191. // if first is back, second on the plane
  192. } else {
  193. // the whole edge is back
  194. return TRUE;
  195. }
  196. /* ---- first point in front of plane ---- */
  197. } else if (fDistance0 > +EPSILON) {
  198. // if first is front, second back
  199. if (fDistance1 < -EPSILON) {
  200. // calculate intersection coordinates
  201. vVertex0 = vVertex1-(vVertex1-vVertex0)*fDistance1/(fDistance1-fDistance0);
  202. // the remaining part is behind
  203. return TRUE;
  204. // if both are front
  205. } else if (fDistance1 > +EPSILON) {
  206. // whole edge is front
  207. return FALSE;
  208. // if first is front, second on the plane
  209. } else {
  210. // whole edge is front
  211. return FALSE;
  212. }
  213. /* ---- first point on the plane ---- */
  214. } else {
  215. // if first is on the plane, second back
  216. if (fDistance1 < -EPSILON) {
  217. // the whole edge is back
  218. return TRUE;
  219. // if first is on the plane, second front
  220. } else if (fDistance1 > +EPSILON) {
  221. // whole edge is front
  222. return FALSE;
  223. // if both are on the plane
  224. } else {
  225. // assume the whole edge is back (!!!! is this correct?)
  226. return TRUE;
  227. }
  228. }
  229. }
  230. /*
  231. * Check if an edge is entirely or partially inside considered triangle.
  232. */
  233. BOOL CTriangularizer::EdgeInsideTriangle(const CBrushEdge *pbed) const
  234. {
  235. // copy edge vertices
  236. DOUBLE3D vVertex0, vVertex1;
  237. vVertex0 = GetVertex(pbed->bed_pbvxVertex0);
  238. vVertex1 = GetVertex(pbed->bed_pbvxVertex1);
  239. // try to clip it to each triangle edge in turn,
  240. // if anything remains behind - it is inside
  241. return (ClipEdge(vVertex0, vVertex1, tr_plLeft)
  242. &&ClipEdge(vVertex0, vVertex1, tr_plRight)
  243. &&ClipEdge(vVertex0, vVertex1, tr_plBottom));
  244. }
  245. /*
  246. * Calculate the quality of currently considered triangle.
  247. */
  248. DOUBLE CTriangularizer::TriangleQuality(void) const
  249. {
  250. DOUBLE3D vEdgeBottom = GetVertex(tr_pbedBottom->bed_pbvxVertex1)
  251. - GetVertex(tr_pbedBottom->bed_pbvxVertex0);
  252. DOUBLE3D vEdgeLeft = GetVertex(tr_pbedBottom->bed_pbvxVertex0)
  253. - GetVertex(tr_pbvxTopVertex);
  254. DOUBLE3D vEdgeRight = GetVertex(tr_pbvxTopVertex)
  255. - GetVertex(tr_pbedBottom->bed_pbvxVertex1);
  256. // calculate triangle normal as cross product of two edges
  257. DOUBLE3D vNormal = vEdgeLeft*(-vEdgeRight);
  258. // calculate area as half the length of the normal
  259. DOUBLE fArea = vNormal.Length()/ DOUBLE(2.0);
  260. // area must be initially positive
  261. ASSERT(fArea>=0);
  262. // if triangle normal is opposite to the polygon normal
  263. if (vNormal%tr_vPolygonNormal<0) {
  264. // make area negative
  265. fArea = -fArea;
  266. }
  267. // find length of all edges
  268. DOUBLE fLengthBottom = vEdgeBottom.Length();
  269. DOUBLE fLengthLeft = vEdgeLeft .Length();
  270. DOUBLE fLengthRight = vEdgeRight .Length();
  271. // find maximum length
  272. DOUBLE fLengthMax = Max(fLengthBottom, Max(fLengthLeft, fLengthRight));
  273. // maximum length must be positive
  274. ASSERT(fLengthMax>0.0f);
  275. // quality is division of the area and square of the length of the longest edge
  276. return fArea/(fLengthMax*fLengthMax);
  277. }
  278. /*
  279. * Create an array of forward oriented edges from polygon edges of a polygon.
  280. */
  281. void CTriangularizer::MakeEdgesForTriangularization(void)
  282. {
  283. // get number of edges in polygon
  284. INDEX ctEdges = tr_abpeOriginalEdges.Count();
  285. // create that much edges in the array
  286. CBrushEdge *pbedEdges = tr_abedEdges.New(ctEdges);
  287. tr_abedEdges.Lock();
  288. // for each edge
  289. for(INDEX iEdge=0; iEdge<ctEdges; iEdge++) {
  290. CBrushPolygonEdge &bpe = tr_abpeOriginalEdges[iEdge];
  291. CBrushEdge &bed = tr_abedEdges[iEdge];
  292. // if polygon edge is reversed
  293. if (bpe.bpe_bReverse) {
  294. // make edge in array reversed
  295. bed.bed_pbvxVertex0 = bpe.bpe_pbedEdge->bed_pbvxVertex1;
  296. bed.bed_pbvxVertex1 = bpe.bpe_pbedEdge->bed_pbvxVertex0;
  297. // if polygon edge is not reversed
  298. } else {
  299. // make edge in array normal
  300. bed.bed_pbvxVertex0 = bpe.bpe_pbedEdge->bed_pbvxVertex0;
  301. bed.bed_pbvxVertex1 = bpe.bpe_pbedEdge->bed_pbvxVertex1;
  302. }
  303. }
  304. tr_abedEdges.Unlock();
  305. }
  306. /*
  307. * Add given edge to temporary array.
  308. */
  309. void CTriangularizer::AddEdge(CBrushVertex *pbvxVertex0, CBrushVertex *pbvxVertex1)
  310. {
  311. #ifndef NDEBUG
  312. // for each edge
  313. {FOREACHINDYNAMICARRAY(tr_abedEdges, CBrushEdge, itbed) {
  314. // must not exist
  315. if (itbed->bed_pbvxVertex0 == pbvxVertex0
  316. &&itbed->bed_pbvxVertex1 == pbvxVertex1) {
  317. return;
  318. }
  319. ASSERT(itbed->bed_pbvxVertex0 != pbvxVertex0
  320. ||itbed->bed_pbvxVertex1 != pbvxVertex1);
  321. // must not have reverse
  322. ASSERT(itbed->bed_pbvxVertex0 != pbvxVertex1
  323. ||itbed->bed_pbvxVertex1 != pbvxVertex0);
  324. }}
  325. #endif
  326. // add new edge
  327. CBrushEdge *pbedNew = tr_abedEdges.New(1);
  328. pbedNew->bed_pbvxVertex0 = pbvxVertex0;
  329. pbedNew->bed_pbvxVertex1 = pbvxVertex1;
  330. }
  331. /*
  332. * Check that duplicate or reverse edges do not exist.
  333. */
  334. void CTriangularizer::CheckForInvalidEdges(void)
  335. {
  336. // for each edge
  337. {FOREACHINDYNAMICARRAY(tr_abedEdges, CBrushEdge, itbed1) {
  338. // for each edge
  339. {FOREACHINDYNAMICARRAY(tr_abedEdges, CBrushEdge, itbed2) {
  340. // if same edge
  341. if (&*itbed1 == &*itbed2) {
  342. // skip
  343. continue;
  344. }
  345. CBrushEdge *pbed1 = itbed1;
  346. CBrushEdge *pbed2 = itbed2;
  347. // must not exist
  348. ASSERT(pbed1->bed_pbvxVertex0 != pbed2->bed_pbvxVertex0
  349. ||pbed1->bed_pbvxVertex1 != pbed2->bed_pbvxVertex1);
  350. // must not have reverse
  351. ASSERT(pbed1->bed_pbvxVertex0 != pbed2->bed_pbvxVertex1
  352. ||pbed1->bed_pbvxVertex1 != pbed2->bed_pbvxVertex0);
  353. }}
  354. }}
  355. }
  356. /*
  357. * Add best triangle to triangles.
  358. */
  359. void CTriangularizer::AddBestTriangleToTriangles(void)
  360. {
  361. // add the triangle to triangles
  362. CBrushVertex **ppbvxTriangle = tr_apbvxTriangleVertices.New(3);
  363. ppbvxTriangle[0] = tr_pbedBottomBest->bed_pbvxVertex0;
  364. ppbvxTriangle[1] = tr_pbedBottomBest->bed_pbvxVertex1;
  365. ppbvxTriangle[2] = tr_pbvxTopVertexBest;
  366. }
  367. /*
  368. * Remove best triangle from edges.
  369. */
  370. void CTriangularizer::RemoveBestTriangleFromPolygon(void)
  371. {
  372. tr_pbedBottom = tr_pbedBottomBest;
  373. tr_pbvxTopVertex = tr_pbvxTopVertexBest;
  374. FindExistingTriangleEdges();
  375. tr_pbedLeftBest = tr_pbedLeft;
  376. tr_pbedRightBest = tr_pbedRight;
  377. tr_pbedBottomBest = tr_pbedBottom;
  378. tr_pbvxTopVertexBest = tr_pbvxTopVertex;
  379. // if left edge was found
  380. if (tr_pbedLeftBest!=NULL) {
  381. // remove the left edge from the edges
  382. tr_abedEdges.Delete(tr_pbedLeftBest);
  383. // if left edge was not found
  384. } else {
  385. // add reverse of the left edge to the edges
  386. AddEdge(tr_pbedBottomBest->bed_pbvxVertex0, tr_pbvxTopVertexBest);
  387. }
  388. // if right edge was found
  389. if (tr_pbedRightBest!=NULL) {
  390. // remove the right edge from the edges
  391. tr_abedEdges.Delete(tr_pbedRightBest);
  392. // if right edge was not found
  393. } else {
  394. // add reverse of the right edge to the edges
  395. AddEdge(tr_pbvxTopVertexBest, tr_pbedBottomBest->bed_pbvxVertex1);
  396. }
  397. // remove the base edge from the edges
  398. tr_abedEdges.Delete(tr_pbedBottomBest);
  399. }
  400. /*
  401. * Find if left/right triangle edges already exist.
  402. */
  403. void CTriangularizer::FindExistingTriangleEdges(void)
  404. {
  405. // initialize left edge as not existing
  406. tr_pbedLeft = NULL;
  407. // initialize right edge as not existing
  408. tr_pbedRight = NULL;
  409. // for each edge
  410. FOREACHINDYNAMICARRAY(tr_abedEdges, CBrushEdge, itbed) {
  411. CBrushEdge *pbed = itbed;
  412. // if it is the bottom edge of the triangle
  413. if (tr_pbedBottom == itbed) {
  414. // do not test it for intersection
  415. NOTHING;
  416. // if it is not bottom edge of triangle
  417. } else {
  418. ASSERT(itbed->bed_pbvxVertex0 != tr_pbedBottom->bed_pbvxVertex0
  419. ||itbed->bed_pbvxVertex1 != tr_pbedBottom->bed_pbvxVertex1);
  420. // if it is the left edge of the triangle
  421. if (itbed->bed_pbvxVertex1 == tr_pbedBottom->bed_pbvxVertex0
  422. &&itbed->bed_pbvxVertex0 == tr_pbvxTopVertex) {
  423. // remember it as the left edge
  424. ASSERT(tr_pbedLeft==NULL);
  425. tr_pbedLeft = itbed;
  426. // if it is the right edge of the triangle
  427. } else if (itbed->bed_pbvxVertex0 == tr_pbedBottom->bed_pbvxVertex1
  428. &&itbed->bed_pbvxVertex1 == tr_pbvxTopVertex) {
  429. // remember it as the right edge
  430. ASSERT(tr_pbedRight==NULL);
  431. tr_pbedRight = itbed;
  432. }
  433. }
  434. }
  435. }
  436. /*
  437. * Test all edges for intersection with considered triangle.
  438. */
  439. BOOL CTriangularizer::CheckTriangleAgainstEdges(void)
  440. {
  441. // for each edge
  442. FOREACHINDYNAMICARRAY(tr_abedEdges, CBrushEdge, itbed) {
  443. CBrushEdge *pbed = itbed;
  444. // if it is the bottom edge of the triangle
  445. if (tr_pbedBottom == itbed) {
  446. // do not test it for intersection
  447. NOTHING;
  448. // if it is not bottom edge of triangle
  449. } else {
  450. ASSERT(itbed->bed_pbvxVertex0 != tr_pbedBottom->bed_pbvxVertex0
  451. ||itbed->bed_pbvxVertex1 != tr_pbedBottom->bed_pbvxVertex1);
  452. // if it is the left edge of the triangle
  453. if (itbed->bed_pbvxVertex1 == tr_pbedBottom->bed_pbvxVertex0
  454. &&itbed->bed_pbvxVertex0 == tr_pbvxTopVertex) {
  455. // do not test it for intersection
  456. NOTHING;
  457. // if it is the right edge of the triangle
  458. } else if (itbed->bed_pbvxVertex0 == tr_pbedBottom->bed_pbvxVertex1
  459. &&itbed->bed_pbvxVertex1 == tr_pbvxTopVertex) {
  460. // do not test it for intersection
  461. NOTHING;
  462. // if it is neither of triangle edges
  463. } else {
  464. // if this edge intersects with any of the triangle edges
  465. if (EdgeInsideTriangle(itbed)) {
  466. return TRUE;
  467. }
  468. }
  469. }
  470. }
  471. // if no intersections are found
  472. return FALSE;
  473. }
  474. // calculate edge direction (COPIED FROM CTMATH!)
  475. void EdgeDir(const DOUBLE3D &vPoint0, const DOUBLE3D &vPoint1,
  476. DOUBLE3D &vDirection, DOUBLE3D &vReferencePoint)
  477. {
  478. // if the vertices are reversed
  479. /* if (CompareVertices(vPoint0, vPoint1)<0) {
  480. // swap them
  481. Swap(vPoint0, vPoint1);
  482. // mark edge as reversed
  483. edx_bReverse = TRUE;
  484. // if the vertices are not reversed
  485. } else {
  486. // mark edge as not reversed
  487. edx_bReverse = FALSE;
  488. }
  489. */
  490. // normalize the direction
  491. vDirection = (vPoint1-vPoint0).Normalize();
  492. /* calculate the reference point on the line from any point on line (p) and direction (d):
  493. r = p - ((p.d)/(d.d))*d
  494. */
  495. vReferencePoint = vPoint0 -
  496. vDirection * ((vPoint0 % vDirection))/(vDirection % vDirection);
  497. }
  498. /*
  499. * Print a statement to debugging output file.
  500. */
  501. void CTriangularizer::DPrintF(char *strFormat, ...)
  502. {
  503. char strBuffer[256];
  504. // format the message in buffer
  505. va_list arg;
  506. va_start(arg, strFormat);
  507. vsprintf(strBuffer, strFormat, arg);
  508. // if the debug output file is not open
  509. if (!_bDebugOutputOpen) {
  510. // open it
  511. try {
  512. _strmDebugOutput.Create_t(_fnmDebugOutput, CTStream::CM_TEXT);
  513. _bDebugOutputOpen = TRUE;
  514. // if not successful
  515. } catch (char *strError) {
  516. (void) strError;
  517. // print nothing
  518. return;
  519. }
  520. }
  521. // write the message to the file
  522. _strmDebugOutput.Write_t(strBuffer, strlen(strBuffer));
  523. }
  524. /*
  525. * Dump triangle edges to debug output.
  526. */
  527. void CTriangularizer::DumpEdges(void)
  528. {
  529. DPrintF("%d\n", tr_abedEdges.Count());
  530. FOREACHINDYNAMICARRAY(tr_abedEdges, CBrushEdge, itbed) {
  531. DPrintF("(%f, %f) ->",
  532. GetVertex(itbed->bed_pbvxVertex0)(1),
  533. GetVertex(itbed->bed_pbvxVertex0)(2));
  534. DPrintF(" (%f, %f) ",
  535. GetVertex(itbed->bed_pbvxVertex1)(1),
  536. GetVertex(itbed->bed_pbvxVertex1)(2));
  537. DOUBLE3D vDirection, vReferencePoint;
  538. EdgeDir(
  539. GetVertex(itbed->bed_pbvxVertex0), GetVertex(itbed->bed_pbvxVertex1),
  540. vDirection, vReferencePoint);
  541. DPrintF("[(%f, %f), ",
  542. vDirection(1),
  543. vDirection(2));
  544. DPrintF("(%f, %f)]\n",
  545. vReferencePoint(1),
  546. vReferencePoint(2));
  547. }
  548. }
  549. // edge offsets used to 'shuffle' triangularization
  550. static INDEX iBottomEdgeOffset;
  551. static INDEX iTopEdgeOffset;
  552. /*
  553. * Find best triangle in array of edges.
  554. */
  555. /* NOTE: Currently only searching for first acceptable triangle.
  556. */
  557. void CTriangularizer::FindBestTriangle(void)
  558. {
  559. // clear best triangle description
  560. tr_fQualityBest = DOUBLE(-1E30);
  561. tr_bIntersectedBest = TRUE;
  562. tr_pbedLeftBest = NULL;
  563. tr_pbedRightBest = NULL;
  564. tr_pbedBottomBest = NULL;
  565. tr_pbvxTopVertexBest = NULL;
  566. iBottomEdgeOffset++;
  567. // for each edge
  568. tr_abedEdges.Lock();
  569. INDEX ctEdges = tr_abedEdges.Count();
  570. for(INDEX ibedBottom=0; ibedBottom<tr_abedEdges.Count(); ibedBottom++) {
  571. // consider this edge as bottom edge of triangle
  572. tr_pbedBottom = &tr_abedEdges[(ibedBottom+iBottomEdgeOffset)%ctEdges];
  573. ASSERT(tr_pbedBottom->bed_pbvxVertex0 != tr_pbedBottom->bed_pbvxVertex1);
  574. // create bottom splitter
  575. EdgeToPlane(GetVertex(tr_pbedBottom->bed_pbvxVertex0),
  576. GetVertex(tr_pbedBottom->bed_pbvxVertex1), tr_plEdgeConsidered);
  577. iTopEdgeOffset++;
  578. // for each edge
  579. for(INDEX ibedTop=0; ibedTop<ctEdges; ibedTop++) {
  580. // consider first vertex of this edge as top vertex of triangle
  581. tr_pbvxTopVertex = tr_abedEdges[(ibedTop+ibedBottom+iTopEdgeOffset)%ctEdges].bed_pbvxVertex0;
  582. // if the top vertex is one of the vertices of bottom edge
  583. if ( tr_pbvxTopVertex == tr_pbedBottom->bed_pbvxVertex0
  584. || tr_pbvxTopVertex == tr_pbedBottom->bed_pbvxVertex1) {
  585. // skip this triangle
  586. continue;
  587. }
  588. // create bottom splitter from the bottom edge splitter
  589. tr_plBottom = tr_plEdgeConsidered;
  590. // create left splitter
  591. EdgeToPlane(GetVertex(tr_pbvxTopVertex),
  592. GetVertex(tr_pbedBottom->bed_pbvxVertex0), tr_plLeft);
  593. // create right splitter
  594. EdgeToPlane(GetVertex(tr_pbedBottom->bed_pbvxVertex1),
  595. GetVertex(tr_pbvxTopVertex), tr_plRight);
  596. // calculate its quality
  597. DOUBLE fCurrentQuality = TriangleQuality();
  598. // if triangle is clockwise
  599. if (fCurrentQuality<0) {
  600. continue;
  601. // reverse its splitters
  602. tr_plLeft.Flip();
  603. tr_plRight.Flip();
  604. tr_plBottom.Flip();
  605. }
  606. // check if no edges intersect with considered triangle
  607. BOOL bCurrentIntersected = CheckTriangleAgainstEdges();
  608. // if the current triangle is better than the best triangle yet
  609. if ((tr_bIntersectedBest && !bCurrentIntersected)
  610. ||(tr_bIntersectedBest==bCurrentIntersected && fCurrentQuality>tr_fQualityBest)) {
  611. // find if left/right triangle edges already exist.
  612. FindExistingTriangleEdges();
  613. // set current triangle as best triangle
  614. tr_bIntersectedBest = bCurrentIntersected;
  615. tr_fQualityBest = fCurrentQuality;
  616. tr_pbedLeftBest = tr_pbedLeft;
  617. tr_pbedRightBest = tr_pbedRight;
  618. tr_pbedBottomBest = tr_pbedBottom;
  619. tr_pbvxTopVertexBest = tr_pbvxTopVertex;
  620. // if the triangle is trivially acceptable
  621. if (!bCurrentIntersected && fCurrentQuality>=QUALITY_ACCEPTTRIVIALLY) {
  622. // finish searching
  623. tr_abedEdges.Unlock();
  624. return;
  625. }
  626. }
  627. }
  628. }
  629. tr_abedEdges.Unlock();
  630. #if 0
  631. // if no acceptable triangles have been found
  632. if (tr_fQualityBest<???) {
  633. /* dump all sector's vertices */
  634. /*
  635. FOREACHINSTATICARRAY(tr_bpoOriginalPolygon.bpo_pbscSector->bsc_abvxVertices,
  636. CBrushVertex, itbvx) {
  637. DPrintF("(0x%p, %f, %f, %f)\n", &(*itbvx),
  638. (*itbvx)(1), (*itbvx)(2), (*itbvx)(3));
  639. }
  640. */
  641. // dump remaining edges
  642. DPrintF("Triangularization failed!\n");
  643. DPrintF("best quality found: %f\n", tr_fQualityBest);
  644. DPrintF("Remaining edges:\n");
  645. DumpEdges();
  646. /* // dump all edges
  647. tr_abedEdges.Clear();
  648. MakeEdgesForTriangularization();
  649. DumpEdges();
  650. */
  651. // error!
  652. ASSERTALWAYS("No acceptable triangles found for triangularization!");
  653. FatalError("No acceptable triangles found for triangularization!\n"
  654. "Debugging information written to file '%s'.", _fnmDebugOutput);
  655. }
  656. #endif
  657. }
  658. // find vertices used without duplication
  659. void CTriangularizer::FindVerticesUsed(void)
  660. {
  661. // make an empty array that will contain the vertices
  662. tr_apbvxVertices.PopAll();
  663. // for each triangle vertex
  664. tr_apbvxTriangleVertices.Lock();
  665. for(INDEX ipbvx=0; ipbvx<tr_apbvxTriangleVertices.Count(); ipbvx++) {
  666. CBrushVertex *pbvx = tr_apbvxTriangleVertices[ipbvx];
  667. // for each vertex already added
  668. BOOL bFound=FALSE;
  669. for(INDEX ipbvxAdded=0; ipbvxAdded<tr_apbvxVertices.Count(); ipbvxAdded++) {
  670. // if it is the same one, stop searching
  671. if (tr_apbvxVertices[ipbvxAdded]==pbvx) {
  672. bFound = TRUE;
  673. break;
  674. }
  675. }
  676. // if not found
  677. if (!bFound) {
  678. // add it to the array
  679. tr_apbvxVertices.Push() = pbvx;
  680. }
  681. }
  682. tr_apbvxTriangleVertices.Unlock();
  683. }
  684. // build array of element indices
  685. void CTriangularizer::MakeElements(void)
  686. {
  687. // make elements array
  688. INDEX ctElements = tr_apbvxTriangleVertices.Count();
  689. tr_aiElements.Clear();
  690. tr_aiElements.New(ctElements);
  691. // for each triangle vertex
  692. tr_apbvxTriangleVertices.Lock();
  693. for(INDEX i=0; i<ctElements; i++) {
  694. CBrushVertex *pbvx = tr_apbvxTriangleVertices[i];
  695. // find its element index
  696. BOOL bFound=FALSE;
  697. for(INDEX ipbvx=0; ipbvx<tr_apbvxVertices.Count(); ipbvx++) {
  698. // if it is the same one, stop searching
  699. if (tr_apbvxVertices[ipbvx]==pbvx) {
  700. tr_aiElements[i] = ipbvx;
  701. bFound = TRUE;
  702. break;
  703. }
  704. }
  705. ASSERT(bFound);
  706. }
  707. tr_apbvxTriangleVertices.Unlock();
  708. }
  709. /*
  710. * Constructor - do triangularization for a polygon.
  711. */
  712. CTriangularizer::CTriangularizer(CBrushPolygon &bpoOriginalPolygon)
  713. : tr_bpoOriginalPolygon(bpoOriginalPolygon)
  714. , tr_abpeOriginalEdges(bpoOriginalPolygon.bpo_abpePolygonEdges) // remember original edges
  715. {
  716. // find polygon normal and major axes
  717. #ifdef OPERATEIN2D
  718. INDEX iMaxNormal = bpoOriginalPolygon.bpo_pbplPlane->bpl_pldPreciseRelative.GetMaxNormal();
  719. INDEX iMaxSign = Sgn(bpoOriginalPolygon.bpo_pbplPlane->bpl_pldPreciseRelative(iMaxNormal));
  720. ASSERT(iMaxSign!=0);
  721. switch(iMaxNormal) {
  722. case 1:
  723. if (iMaxSign==-1) {
  724. tr_iAxis0 = 3; tr_iAxis1 = 2; tr_vPolygonNormal = DOUBLE3D(-1,0,0);
  725. } else {
  726. tr_iAxis0 = 2; tr_iAxis1 = 3; tr_vPolygonNormal = DOUBLE3D(1,0,0);
  727. }
  728. break;
  729. case 2:
  730. if (iMaxSign==-1) {
  731. tr_iAxis0 = 1; tr_iAxis1 = 3; tr_vPolygonNormal = DOUBLE3D(0,-1,0);
  732. } else {
  733. tr_iAxis0 = 3; tr_iAxis1 = 1; tr_vPolygonNormal = DOUBLE3D(0,1,0);
  734. }
  735. break;
  736. case 3:
  737. if (iMaxSign==-1) {
  738. tr_iAxis0 = 2; tr_iAxis1 = 1; tr_vPolygonNormal = DOUBLE3D(0,0,-1);
  739. } else {
  740. tr_iAxis0 = 1; tr_iAxis1 = 2; tr_vPolygonNormal = DOUBLE3D(0,0,1);
  741. }
  742. break;
  743. default:
  744. ASSERT(FALSE);
  745. tr_iAxis0 = 2; tr_iAxis1 = 3; tr_vPolygonNormal = DOUBLE3D(1,0,0);
  746. }
  747. tr_vPolygonNormal = DOUBLE3D(0,0,1);
  748. #else
  749. tr_iAxis0 = -1; tr_iAxis1 = -1; tr_vPolygonNormal = bpoOriginalPolygon.bpo_pbplPlane->bpl_pldPreciseRelative;
  750. #endif
  751. // create a dynamic array of edges
  752. MakeEdgesForTriangularization();
  753. tr_iError = -1;
  754. // estimate max number of triangles
  755. INDEX ctOrgEdges = tr_abpeOriginalEdges.Count();
  756. INDEX ctMaxHoles = ctOrgEdges;
  757. INDEX ctMaxTriangles = ctOrgEdges-2+2*ctMaxHoles;
  758. iBottomEdgeOffset = 0;
  759. iTopEdgeOffset = 0;
  760. #ifdef DUMP_ALLSTEPS
  761. DPrintF("PolygonBegin\n");
  762. #endif
  763. // ASSERT(tr_abedEdges.Count()!=8);
  764. // while the array of edges is not empty
  765. //*
  766. INDEX iPasses = 0;
  767. while (tr_abedEdges.Count()>0) {
  768. // if total number of triangles is too large, or searching too long
  769. INDEX ctTriangles = tr_apbvxTriangleVertices.Count()/3;
  770. if (ctTriangles>ctMaxTriangles*2 || iPasses>ctMaxTriangles*2) {
  771. // error, quit triangulation
  772. tr_iError = 2;
  773. return;
  774. }
  775. #ifdef DUMP_ALLSTEPS
  776. // dump remaining edges
  777. DumpEdges();
  778. #endif
  779. // find best triangle
  780. FindBestTriangle();
  781. #ifdef DUMP_ALLSTEPS
  782. DPrintF("BestQuality=%f\n", tr_fQualityBest);
  783. #endif
  784. // if no triangle is found
  785. if (tr_fQualityBest<0.0) {
  786. // quit searching
  787. tr_iError = 1;
  788. return;
  789. }
  790. if (tr_bIntersectedBest) {
  791. // quit searching
  792. tr_iError = 3;
  793. return;
  794. }
  795. // if best triangle has positive quality
  796. if (tr_fQualityBest>0.0) {
  797. // add it to triangles
  798. AddBestTriangleToTriangles();
  799. }
  800. // remove best triangle from edges
  801. RemoveBestTriangleFromPolygon();
  802. #ifndef NDEBUG
  803. // check that there are no invalid edges after creating
  804. CheckForInvalidEdges();
  805. #endif
  806. iPasses++;
  807. }
  808. tr_iError = 0;
  809. //*/
  810. }
  811. /*
  812. * Make triangular representation of the polygon.
  813. */
  814. void CBrushPolygon::Triangulate(void)
  815. {
  816. // if already triangulated
  817. if (bpo_apbvxTriangleVertices.Count()>0) {
  818. // do nothing
  819. return;
  820. }
  821. // triangularize the polygon
  822. CTriangularizer tr(*this);
  823. // find vertices used without duplication
  824. tr.FindVerticesUsed();
  825. // build array of element indices
  826. tr.MakeElements();
  827. // if there was an error
  828. if (tr.tr_iError!=0) {
  829. // report it
  830. // CPrintF( TRANS("Cannot properly triangulate a polygon: error %d!\n"), tr.tr_iError);
  831. // mark the polygon
  832. bpo_ulFlags|=BPOF_INVALIDTRIANGLES;
  833. // if there no errors
  834. } else {
  835. // mark the polygon as triangulated well
  836. bpo_ulFlags&=~BPOF_INVALIDTRIANGLES;
  837. }
  838. // copy dynamic array of triangles to the triangular representation of the polygon
  839. INDEX ctVertices = tr.tr_apbvxVertices.Count();
  840. bpo_apbvxTriangleVertices.Clear();
  841. bpo_apbvxTriangleVertices.New(ctVertices);
  842. for (INDEX iVertex=0; iVertex<ctVertices; iVertex++) {
  843. bpo_apbvxTriangleVertices[iVertex] = tr.tr_apbvxVertices[iVertex];
  844. }
  845. bpo_aiTriangleElements = tr.tr_aiElements;
  846. if (_bDebugOutputOpen) {
  847. _strmDebugOutput.Close();
  848. _bDebugOutputOpen = FALSE;
  849. }
  850. }