1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "sys/platform.h"
30
31 //#pragma optimize( "", off )
32
33
34 // #ifdef WIN32 // DG: this caused trouble, especially with SDL2
35 #if 0
36 #include <windows.h>
37 #include <GL/gl.h>
38 #endif
39
40 #include "tools/compilers/dmap/dmap.h"
41
42 /*
43
44 New vertexes will be created where edges cross.
45
46 optimization requires an accurate t junction fixer.
47
48
49
50 */
51
52 idBounds optBounds;
53
54 #define MAX_OPT_VERTEXES 0x10000
55 int numOptVerts;
56 optVertex_t optVerts[MAX_OPT_VERTEXES];
57
58 #define MAX_OPT_EDGES 0x40000
59 static int numOptEdges;
60 static optEdge_t optEdges[MAX_OPT_EDGES];
61
62 static bool IsTriangleValid( const optVertex_t *v1, const optVertex_t *v2, const optVertex_t *v3 );
63 static bool IsTriangleDegenerate( const optVertex_t *v1, const optVertex_t *v2, const optVertex_t *v3 );
64
65 static idRandom orandom;
66
67 /*
68 ==============
69 ValidateEdgeCounts
70 ==============
71 */
ValidateEdgeCounts(optIsland_t * island)72 static void ValidateEdgeCounts( optIsland_t *island ) {
73 optVertex_t *vert;
74 optEdge_t *e;
75 int c;
76
77 for ( vert = island->verts ; vert ; vert = vert->islandLink ) {
78 c = 0;
79 for ( e = vert->edges ; e ; ) {
80 c++;
81 if ( e->v1 == vert ) {
82 e = e->v1link;
83 } else if ( e->v2 == vert ) {
84 e = e->v2link;
85 } else {
86 common->Error( "ValidateEdgeCounts: mislinked" );
87 }
88 }
89 if ( c != 2 && c != 0 ) {
90 // this can still happen at diamond intersections
91 // common->Printf( "ValidateEdgeCounts: %i edges\n", c );
92 }
93 }
94 }
95
96
97 /*
98 ====================
99 AllocEdge
100 ====================
101 */
AllocEdge(void)102 static optEdge_t *AllocEdge( void ) {
103 optEdge_t *e;
104
105 if ( numOptEdges == MAX_OPT_EDGES ) {
106 common->Error( "MAX_OPT_EDGES" );
107 }
108 e = &optEdges[ numOptEdges ];
109 numOptEdges++;
110 memset( e, 0, sizeof( *e ) );
111
112 return e;
113 }
114
115 /*
116 ====================
117 RemoveEdgeFromVert
118 ====================
119 */
RemoveEdgeFromVert(optEdge_t * e1,optVertex_t * vert)120 static void RemoveEdgeFromVert( optEdge_t *e1, optVertex_t *vert ) {
121 optEdge_t **prev;
122 optEdge_t *e;
123
124 if ( !vert ) {
125 return;
126 }
127 prev = &vert->edges;
128 while ( *prev ) {
129 e = *prev;
130 if ( e == e1 ) {
131 if ( e1->v1 == vert ) {
132 *prev = e1->v1link;
133 } else if ( e1->v2 == vert ) {
134 *prev = e1->v2link;
135 } else {
136 common->Error( "RemoveEdgeFromVert: vert not found" );
137 }
138 return;
139 }
140
141 if ( e->v1 == vert ) {
142 prev = &e->v1link;
143 } else if ( e->v2 == vert ) {
144 prev = &e->v2link;
145 } else {
146 common->Error( "RemoveEdgeFromVert: vert not found" );
147 }
148 }
149 }
150
151 /*
152 ====================
153 UnlinkEdge
154 ====================
155 */
UnlinkEdge(optEdge_t * e,optIsland_t * island)156 static void UnlinkEdge( optEdge_t *e, optIsland_t *island ) {
157 optEdge_t **prev;
158
159 RemoveEdgeFromVert( e, e->v1 );
160 RemoveEdgeFromVert( e, e->v2 );
161
162 for ( prev = &island->edges ; *prev ; prev = &(*prev)->islandLink ) {
163 if ( *prev == e ) {
164 *prev = e->islandLink;
165 return;
166 }
167 }
168
169 common->Error( "RemoveEdgeFromIsland: couldn't free edge" );
170 }
171
172
173 /*
174 ====================
175 LinkEdge
176 ====================
177 */
LinkEdge(optEdge_t * e)178 static void LinkEdge( optEdge_t *e ) {
179 e->v1link = e->v1->edges;
180 e->v1->edges = e;
181
182 e->v2link = e->v2->edges;
183 e->v2->edges = e;
184 }
185
186 /*
187 ================
188 FindOptVertex
189 ================
190 */
FindOptVertex(idDrawVert * v,optimizeGroup_t * opt)191 static optVertex_t *FindOptVertex( idDrawVert *v, optimizeGroup_t *opt ) {
192 int i;
193 float x, y;
194 optVertex_t *vert;
195
196 // deal with everything strictly as 2D
197 x = v->xyz * opt->axis[0];
198 y = v->xyz * opt->axis[1];
199
200 // should we match based on the t-junction fixing hash verts?
201 for ( i = 0 ; i < numOptVerts ; i++ ) {
202 if ( optVerts[i].pv[0] == x && optVerts[i].pv[1] == y ) {
203 return &optVerts[i];
204 }
205 }
206
207 if ( numOptVerts >= MAX_OPT_VERTEXES ) {
208 common->Error( "MAX_OPT_VERTEXES" );
209 return NULL;
210 }
211
212 numOptVerts++;
213
214 vert = &optVerts[i];
215 memset( vert, 0, sizeof( *vert ) );
216 vert->v = *v;
217 vert->pv[0] = x;
218 vert->pv[1] = y;
219 vert->pv[2] = 0;
220
221 optBounds.AddPoint( vert->pv );
222
223 return vert;
224 }
225
226 /*
227 ================
228 DrawAllEdges
229 ================
230 */
DrawAllEdges(void)231 static void DrawAllEdges( void ) {
232 int i;
233
234 if ( !dmapGlobals.drawflag ) {
235 return;
236 }
237
238 Draw_ClearWindow();
239
240 qglBegin( GL_LINES );
241 for ( i = 0 ; i < numOptEdges ; i++ ) {
242 if ( optEdges[i].v1 == NULL ) {
243 continue;
244 }
245 qglColor3f( 1, 0, 0 );
246 qglVertex3fv( optEdges[i].v1->pv.ToFloatPtr() );
247 qglColor3f( 0, 0, 0 );
248 qglVertex3fv( optEdges[i].v2->pv.ToFloatPtr() );
249 }
250 qglEnd();
251 qglFlush();
252
253 // GLimp_SwapBuffers();
254 }
255
256 /*
257 ================
258 DrawVerts
259 ================
260 */
DrawVerts(optIsland_t * island)261 static void DrawVerts( optIsland_t *island ) {
262 optVertex_t *vert;
263
264 if ( !dmapGlobals.drawflag ) {
265 return;
266 }
267
268 qglEnable( GL_BLEND );
269 qglBlendFunc( GL_ONE, GL_ONE );
270 qglColor3f( 0.3f, 0.3f, 0.3f );
271 qglPointSize( 3 );
272 qglBegin( GL_POINTS );
273 for ( vert = island->verts ; vert ; vert = vert->islandLink ) {
274 qglVertex3fv( vert->pv.ToFloatPtr() );
275 }
276 qglEnd();
277 qglDisable( GL_BLEND );
278 qglFlush();
279 }
280
281 /*
282 ================
283 DrawEdges
284 ================
285 */
DrawEdges(optIsland_t * island)286 static void DrawEdges( optIsland_t *island ) {
287 optEdge_t *edge;
288
289 if ( !dmapGlobals.drawflag ) {
290 return;
291 }
292
293 Draw_ClearWindow();
294
295 qglBegin( GL_LINES );
296 for ( edge = island->edges ; edge ; edge = edge->islandLink ) {
297 if ( edge->v1 == NULL ) {
298 continue;
299 }
300 qglColor3f( 1, 0, 0 );
301 qglVertex3fv( edge->v1->pv.ToFloatPtr() );
302 qglColor3f( 0, 0, 0 );
303 qglVertex3fv( edge->v2->pv.ToFloatPtr() );
304 }
305 qglEnd();
306 qglFlush();
307
308 // GLimp_SwapBuffers();
309 }
310
311 //=================================================================
312
313 /*
314 =================
315 VertexBetween
316 =================
317 */
VertexBetween(const optVertex_t * p1,const optVertex_t * v1,const optVertex_t * v2)318 static bool VertexBetween( const optVertex_t *p1, const optVertex_t *v1, const optVertex_t *v2 ) {
319 idVec3 d1, d2;
320 float d;
321
322 d1 = p1->pv - v1->pv;
323 d2 = p1->pv - v2->pv;
324 d = d1 * d2;
325 if ( d < 0 ) {
326 return true;
327 }
328 return false;
329 }
330
331
332 /*
333 ====================
334 EdgeIntersection
335
336 Creates a new optVertex_t where the line segments cross.
337 This should only be called if PointsStraddleLine returned true
338
339 Will return NULL if the lines are colinear
340 ====================
341 */
EdgeIntersection(const optVertex_t * p1,const optVertex_t * p2,const optVertex_t * l1,const optVertex_t * l2,optimizeGroup_t * opt)342 static optVertex_t *EdgeIntersection( const optVertex_t *p1, const optVertex_t *p2,
343 const optVertex_t *l1, const optVertex_t *l2, optimizeGroup_t *opt ) {
344 float f;
345 idDrawVert *v;
346 idVec3 dir1, dir2, cross1, cross2;
347
348 dir1 = p1->pv - l1->pv;
349 dir2 = p1->pv - l2->pv;
350 cross1 = dir1.Cross( dir2 );
351
352 dir1 = p2->pv - l1->pv;
353 dir2 = p2->pv - l2->pv;
354 cross2 = dir1.Cross( dir2 );
355
356 if ( cross1[2] - cross2[2] == 0 ) {
357 return NULL;
358 }
359
360 f = cross1[2] / ( cross1[2] - cross2[2] );
361
362 // FIXME: how are we freeing this, since it doesn't belong to a tri?
363 v = (idDrawVert *)Mem_Alloc( sizeof( *v ) );
364 memset( v, 0, sizeof( *v ) );
365
366 v->xyz = p1->v.xyz * ( 1.0 - f ) + p2->v.xyz * f;
367 v->normal = p1->v.normal * ( 1.0 - f ) + p2->v.normal * f;
368 v->normal.Normalize();
369 v->st[0] = p1->v.st[0] * ( 1.0 - f ) + p2->v.st[0] * f;
370 v->st[1] = p1->v.st[1] * ( 1.0 - f ) + p2->v.st[1] * f;
371
372 return FindOptVertex( v, opt );
373 }
374
375
376 /*
377 ====================
378 PointsStraddleLine
379
380 Colinear is considdered crossing.
381 ====================
382 */
PointsStraddleLine(optVertex_t * p1,optVertex_t * p2,optVertex_t * l1,optVertex_t * l2)383 static bool PointsStraddleLine( optVertex_t *p1, optVertex_t *p2, optVertex_t *l1, optVertex_t *l2 ) {
384 bool t1, t2;
385
386 t1 = IsTriangleDegenerate( l1, l2, p1 );
387 t2 = IsTriangleDegenerate( l1, l2, p2 );
388 if ( t1 && t2 ) {
389 // colinear case
390 float s1, s2, s3, s4;
391 bool positive, negative;
392
393 s1 = ( p1->pv - l1->pv ) * ( l2->pv - l1->pv );
394 s2 = ( p2->pv - l1->pv ) * ( l2->pv - l1->pv );
395 s3 = ( p1->pv - l2->pv ) * ( l2->pv - l1->pv );
396 s4 = ( p2->pv - l2->pv ) * ( l2->pv - l1->pv );
397
398 if ( s1 > 0 || s2 > 0 || s3 > 0 || s4 > 0 ) {
399 positive = true;
400 } else {
401 positive = false;
402 }
403 if ( s1 < 0 || s2 < 0 || s3 < 0 || s4 < 0 ) {
404 negative = true;
405 } else {
406 negative = false;
407 }
408
409 if ( positive && negative ) {
410 return true;
411 }
412 return false;
413 } else if ( p1 != l1 && p1 != l2 && p2 != l1 && p2 != l2 ) {
414 // no shared verts
415 t1 = IsTriangleValid( l1, l2, p1 );
416 t2 = IsTriangleValid( l1, l2, p2 );
417 if ( t1 && t2 ) {
418 return false;
419 }
420
421 t1 = IsTriangleValid( l1, p1, l2 );
422 t2 = IsTriangleValid( l1, p2, l2 );
423 if ( t1 && t2 ) {
424 return false;
425 }
426
427 return true;
428 } else {
429 // a shared vert, not colinear, so not crossing
430 return false;
431 }
432 }
433
434
435 /*
436 ====================
437 EdgesCross
438 ====================
439 */
EdgesCross(optVertex_t * a1,optVertex_t * a2,optVertex_t * b1,optVertex_t * b2)440 static bool EdgesCross( optVertex_t *a1, optVertex_t *a2, optVertex_t *b1, optVertex_t *b2 ) {
441 // if both verts match, consider it to be crossed
442 if ( a1 == b1 && a2 == b2 ) {
443 return true;
444 }
445 if ( a1 == b2 && a2 == b1 ) {
446 return true;
447 }
448 // if only one vert matches, it might still be colinear, which
449 // would be considered crossing
450
451 // if both lines' verts are on opposite sides of the other
452 // line, it is crossed
453 if ( !PointsStraddleLine( a1, a2, b1, b2 ) ) {
454 return false;
455 }
456 if ( !PointsStraddleLine( b1, b2, a1, a2 ) ) {
457 return false;
458 }
459
460 return true;
461 }
462
463 /*
464 ====================
465 TryAddNewEdge
466
467 ====================
468 */
TryAddNewEdge(optVertex_t * v1,optVertex_t * v2,optIsland_t * island)469 static bool TryAddNewEdge( optVertex_t *v1, optVertex_t *v2, optIsland_t *island ) {
470 optEdge_t *e;
471
472 // if the new edge crosses any other edges, don't add it
473 for ( e = island->edges ; e ; e = e->islandLink ) {
474 if ( EdgesCross( e->v1, e->v2, v1, v2 ) ) {
475 return false;
476 }
477 }
478
479 if ( dmapGlobals.drawflag ) {
480 qglBegin( GL_LINES );
481 qglColor3f( 0, ( 128 + orandom.RandomInt( 127 ) )/ 255.0, 0 );
482 qglVertex3fv( v1->pv.ToFloatPtr() );
483 qglVertex3fv( v2->pv.ToFloatPtr() );
484 qglEnd();
485 qglFlush();
486 }
487 // add it
488 e = AllocEdge();
489
490 e->islandLink = island->edges;
491 island->edges = e;
492 e->v1 = v1;
493 e->v2 = v2;
494
495 e->created = true;
496
497 // link the edge to its verts
498 LinkEdge( e );
499
500 return true;
501 }
502
503 typedef struct {
504 optVertex_t *v1, *v2;
505 float length;
506 } edgeLength_t;
507
508
LengthSort(const void * a,const void * b)509 static int LengthSort( const void *a, const void *b ) {
510 const edgeLength_t *ea, *eb;
511
512 ea = (const edgeLength_t *)a;
513 eb = (const edgeLength_t *)b;
514 if ( ea->length < eb->length ) {
515 return -1;
516 }
517 if ( ea->length > eb->length ) {
518 return 1;
519 }
520 return 0;
521 }
522
523 /*
524 ==================
525 AddInteriorEdges
526
527 Add all possible edges between the verts
528 ==================
529 */
AddInteriorEdges(optIsland_t * island)530 static void AddInteriorEdges( optIsland_t *island ) {
531 int c_addedEdges;
532 optVertex_t *vert, *vert2;
533 int c_verts;
534 edgeLength_t *lengths;
535 int numLengths;
536 int i;
537
538 DrawVerts( island );
539
540 // count the verts
541 c_verts = 0;
542 for ( vert = island->verts ; vert ; vert = vert->islandLink ) {
543 if ( !vert->edges ) {
544 continue;
545 }
546 c_verts++;
547 }
548
549 // allocate space for all the lengths
550 lengths = (edgeLength_t *)Mem_Alloc( sizeof( *lengths ) * c_verts * c_verts / 2 );
551 numLengths = 0;
552 for ( vert = island->verts ; vert ; vert = vert->islandLink ) {
553 if ( !vert->edges ) {
554 continue;
555 }
556 for ( vert2 = vert->islandLink ; vert2 ; vert2 = vert2->islandLink ) {
557 idVec3 dir;
558
559 if ( !vert2->edges ) {
560 continue;
561 }
562 lengths[numLengths].v1 = vert;
563 lengths[numLengths].v2 = vert2;
564 dir = ( vert->pv - vert2->pv ) ;
565 lengths[numLengths].length = dir.Length();
566 numLengths++;
567 }
568 }
569
570
571 // sort by length, shortest first
572 qsort( lengths, numLengths, sizeof( lengths[0] ), LengthSort );
573
574 // try to create them in that order
575 c_addedEdges = 0;
576 for ( i = 0 ; i < numLengths ; i++ ) {
577 if ( TryAddNewEdge( lengths[i].v1, lengths[i].v2, island ) ) {
578 c_addedEdges++;
579 }
580 }
581
582 if ( dmapGlobals.verbose ) {
583 common->Printf( "%6i tested segments\n", numLengths );
584 common->Printf( "%6i added interior edges\n", c_addedEdges );
585 }
586
587 Mem_Free( lengths );
588 }
589
590
591
592 //==================================================================
593
594 /*
595 ====================
596 RemoveIfColinear
597
598 ====================
599 */
600 #define COLINEAR_EPSILON 0.1
RemoveIfColinear(optVertex_t * ov,optIsland_t * island)601 static void RemoveIfColinear( optVertex_t *ov, optIsland_t *island ) {
602 optEdge_t *e, *e1, *e2;
603 optVertex_t *v1, *v2, *v3;
604 idVec3 dir1, dir2;
605 float dist;
606 idVec3 point;
607 idVec3 offset;
608 float off;
609
610 v2 = ov;
611
612 // we must find exactly two edges before testing for colinear
613 e1 = NULL;
614 e2 = NULL;
615 for ( e = ov->edges ; e ; ) {
616 if ( !e1 ) {
617 e1 = e;
618 } else if ( !e2 ) {
619 e2 = e;
620 } else {
621 return; // can't remove a vertex with three edges
622 }
623 if ( e->v1 == v2 ) {
624 e = e->v1link;
625 } else if ( e->v2 == v2 ) {
626 e = e->v2link;
627 } else {
628 common->Error( "RemoveIfColinear: mislinked edge" );
629 return;
630 }
631 }
632
633 // can't remove if no edges
634 if ( !e1 ) {
635 return;
636 }
637
638 if ( !e2 ) {
639 // this may still happen legally when a tiny triangle is
640 // the only thing in a group
641 common->Printf( "WARNING: vertex with only one edge\n" );
642 return;
643 }
644
645 if ( e1->v1 == v2 ) {
646 v1 = e1->v2;
647 } else if ( e1->v2 == v2 ) {
648 v1 = e1->v1;
649 } else {
650 common->Error( "RemoveIfColinear: mislinked edge" );
651 return;
652 }
653 if ( e2->v1 == v2 ) {
654 v3 = e2->v2;
655 } else if ( e2->v2 == v2 ) {
656 v3 = e2->v1;
657 } else {
658 common->Error( "RemoveIfColinear: mislinked edge" );
659 return;
660 }
661
662 if ( v1 == v3 ) {
663 common->Error( "RemoveIfColinear: mislinked edge" );
664 return;
665 }
666
667 // they must point in opposite directions
668 dist = ( v3->pv - v2->pv ) * ( v1->pv - v2->pv );
669 if ( dist >= 0 ) {
670 return;
671 }
672
673 // see if they are colinear
674 VectorSubtract( v3->v.xyz, v1->v.xyz, dir1 );
675 dir1.Normalize();
676 VectorSubtract( v2->v.xyz, v1->v.xyz, dir2 );
677 dist = DotProduct( dir2, dir1 );
678 VectorMA( v1->v.xyz, dist, dir1, point );
679 VectorSubtract( point, v2->v.xyz, offset );
680 off = offset.Length();
681
682 if ( off > COLINEAR_EPSILON ) {
683 return;
684 }
685
686 if ( dmapGlobals.drawflag ) {
687 qglBegin( GL_LINES );
688 qglColor3f( 1, 1, 0 );
689 qglVertex3fv( v1->pv.ToFloatPtr() );
690 qglVertex3fv( v2->pv.ToFloatPtr() );
691 qglEnd();
692 qglFlush();
693 qglBegin( GL_LINES );
694 qglColor3f( 0, 1, 1 );
695 qglVertex3fv( v2->pv.ToFloatPtr() );
696 qglVertex3fv( v3->pv.ToFloatPtr() );
697 qglEnd();
698 qglFlush();
699 }
700
701 // replace the two edges with a single edge
702 UnlinkEdge( e1, island );
703 UnlinkEdge( e2, island );
704
705 // v2 should have no edges now
706 if ( v2->edges ) {
707 common->Error( "RemoveIfColinear: didn't remove properly" );
708 return;
709 }
710
711
712 // if there is an existing edge that already
713 // has these exact verts, we have just collapsed a
714 // sliver triangle out of existance, and all the edges
715 // can be removed
716 for ( e = island->edges ; e ; e = e->islandLink ) {
717 if ( ( e->v1 == v1 && e->v2 == v3 )
718 || ( e->v1 == v3 && e->v2 == v1 ) ) {
719 UnlinkEdge( e, island );
720 RemoveIfColinear( v1, island );
721 RemoveIfColinear( v3, island );
722 return;
723 }
724 }
725
726 // if we can't add the combined edge, link
727 // the originals back in
728 if ( !TryAddNewEdge( v1, v3, island ) ) {
729 e1->islandLink = island->edges;
730 island->edges = e1;
731 LinkEdge( e1 );
732
733 e2->islandLink = island->edges;
734 island->edges = e2;
735 LinkEdge( e2 );
736 return;
737 }
738
739 // recursively try to combine both verts now,
740 // because things may have changed since the last combine test
741 RemoveIfColinear( v1, island );
742 RemoveIfColinear( v3, island );
743 }
744
745 /*
746 ====================
747 CombineColinearEdges
748 ====================
749 */
CombineColinearEdges(optIsland_t * island)750 static void CombineColinearEdges( optIsland_t *island ) {
751 int c_edges;
752 optVertex_t *ov;
753 optEdge_t *e;
754
755 c_edges = 0;
756 for ( e = island->edges ; e ; e = e->islandLink ) {
757 c_edges++;
758 }
759 if ( dmapGlobals.verbose ) {
760 common->Printf( "%6i original exterior edges\n", c_edges );
761 }
762
763 for ( ov = island->verts ; ov ; ov = ov->islandLink ) {
764 RemoveIfColinear( ov, island );
765 }
766
767 c_edges = 0;
768 for ( e = island->edges ; e ; e = e->islandLink ) {
769 c_edges++;
770 }
771 if ( dmapGlobals.verbose ) {
772 common->Printf( "%6i optimized exterior edges\n", c_edges );
773 }
774 }
775
776
777 //==================================================================
778
779 /*
780 ===================
781 FreeOptTriangles
782
783 ===================
784 */
FreeOptTriangles(optIsland_t * island)785 static void FreeOptTriangles( optIsland_t *island ) {
786 optTri_t *opt, *next;
787
788 for ( opt = island->tris ; opt ; opt = next ) {
789 next = opt->next;
790 Mem_Free( opt );
791 }
792
793 island->tris = NULL;
794 }
795
796
797 /*
798 =================
799 IsTriangleValid
800
801 empty area will be considered invalid.
802 Due to some truly aweful epsilon issues, a triangle can switch between
803 valid and invalid depending on which order you look at the verts, so
804 consider it invalid if any one of the possibilities is invalid.
805 =================
806 */
IsTriangleValid(const optVertex_t * v1,const optVertex_t * v2,const optVertex_t * v3)807 static bool IsTriangleValid( const optVertex_t *v1, const optVertex_t *v2, const optVertex_t *v3 ) {
808 idVec3 d1, d2, normal;
809
810 d1 = v2->pv - v1->pv;
811 d2 = v3->pv - v1->pv;
812 normal = d1.Cross( d2 );
813 if ( normal[2] <= 0 ) {
814 return false;
815 }
816
817 d1 = v3->pv - v2->pv;
818 d2 = v1->pv - v2->pv;
819 normal = d1.Cross( d2 );
820 if ( normal[2] <= 0 ) {
821 return false;
822 }
823
824 d1 = v1->pv - v3->pv;
825 d2 = v2->pv - v3->pv;
826 normal = d1.Cross( d2 );
827 if ( normal[2] <= 0 ) {
828 return false;
829 }
830
831 return true;
832 }
833
834
835 /*
836 =================
837 IsTriangleDegenerate
838
839 Returns false if it is either front or back facing
840 =================
841 */
IsTriangleDegenerate(const optVertex_t * v1,const optVertex_t * v2,const optVertex_t * v3)842 static bool IsTriangleDegenerate( const optVertex_t *v1, const optVertex_t *v2, const optVertex_t *v3 ) {
843 #if 1
844 idVec3 d1, d2, normal;
845
846 d1 = v2->pv - v1->pv;
847 d2 = v3->pv - v1->pv;
848 normal = d1.Cross( d2 );
849 if ( normal[2] == 0 ) {
850 return true;
851 }
852 return false;
853 #else
854 return (bool)!IsTriangleValid( v1, v2, v3 );
855 #endif
856 }
857
858
859 /*
860 ==================
861 PointInTri
862
863 Tests if a 2D point is inside an original triangle
864 ==================
865 */
PointInTri(const idVec3 & p,const mapTri_t * tri,optIsland_t * island)866 static bool PointInTri( const idVec3 &p, const mapTri_t *tri, optIsland_t *island ) {
867 idVec3 d1, d2, normal;
868
869 // the normal[2] == 0 case is not uncommon when a square is triangulated in
870 // the opposite manner to the original
871
872 d1 = tri->optVert[0]->pv - p;
873 d2 = tri->optVert[1]->pv - p;
874 normal = d1.Cross( d2 );
875 if ( normal[2] < 0 ) {
876 return false;
877 }
878
879 d1 = tri->optVert[1]->pv - p;
880 d2 = tri->optVert[2]->pv - p;
881 normal = d1.Cross( d2 );
882 if ( normal[2] < 0 ) {
883 return false;
884 }
885
886 d1 = tri->optVert[2]->pv - p;
887 d2 = tri->optVert[0]->pv - p;
888 normal = d1.Cross( d2 );
889 if ( normal[2] < 0 ) {
890 return false;
891 }
892
893 return true;
894 }
895
896
897 /*
898 ====================
899 LinkTriToEdge
900
901 ====================
902 */
LinkTriToEdge(optTri_t * optTri,optEdge_t * edge)903 static void LinkTriToEdge( optTri_t *optTri, optEdge_t *edge ) {
904 if ( ( edge->v1 == optTri->v[0] && edge->v2 == optTri->v[1] )
905 || ( edge->v1 == optTri->v[1] && edge->v2 == optTri->v[2] )
906 || ( edge->v1 == optTri->v[2] && edge->v2 == optTri->v[0] ) ) {
907 if ( edge->backTri ) {
908 common->Printf( "Warning: LinkTriToEdge: already in use\n" );
909 return;
910 }
911 edge->backTri = optTri;
912 return;
913 }
914 if ( ( edge->v1 == optTri->v[1] && edge->v2 == optTri->v[0] )
915 || ( edge->v1 == optTri->v[2] && edge->v2 == optTri->v[1] )
916 || ( edge->v1 == optTri->v[0] && edge->v2 == optTri->v[2] ) ) {
917 if ( edge->frontTri ) {
918 common->Printf( "Warning: LinkTriToEdge: already in use\n" );
919 return;
920 }
921 edge->frontTri = optTri;
922 return;
923 }
924 common->Error( "LinkTriToEdge: edge not found on tri" );
925 }
926
927 /*
928 ===============
929 CreateOptTri
930 ===============
931 */
CreateOptTri(optVertex_t * first,optEdge_t * e1,optEdge_t * e2,optIsland_t * island)932 static void CreateOptTri( optVertex_t *first, optEdge_t *e1, optEdge_t *e2, optIsland_t *island ) {
933 optEdge_t *opposite;
934 optVertex_t *second, *third;
935 optTri_t *optTri;
936 mapTri_t *tri;
937
938 if ( e1->v1 == first ) {
939 second = e1->v2;
940 } else if ( e1->v2 == first ) {
941 second = e1->v1;
942 } else {
943 common->Error( "CreateOptTri: mislinked edge" );
944 return;
945 }
946
947 if ( e2->v1 == first ) {
948 third = e2->v2;
949 } else if ( e2->v2 == first ) {
950 third = e2->v1;
951 } else {
952 common->Error( "CreateOptTri: mislinked edge" );
953 return;
954 }
955
956 if ( !IsTriangleValid( first, second, third ) ) {
957 common->Error( "CreateOptTri: invalid" );
958 return;
959 }
960
961 //DrawEdges( island );
962
963 // identify the third edge
964 if ( dmapGlobals.drawflag ) {
965 qglColor3f(1,1,0);
966 qglBegin( GL_LINES );
967 qglVertex3fv( e1->v1->pv.ToFloatPtr() );
968 qglVertex3fv( e1->v2->pv.ToFloatPtr() );
969 qglEnd();
970 qglFlush();
971 qglColor3f(0,1,1);
972 qglBegin( GL_LINES );
973 qglVertex3fv( e2->v1->pv.ToFloatPtr() );
974 qglVertex3fv( e2->v2->pv.ToFloatPtr() );
975 qglEnd();
976 qglFlush();
977 }
978
979 for ( opposite = second->edges ; opposite ; ) {
980 if ( opposite != e1 && ( opposite->v1 == third || opposite->v2 == third ) ) {
981 break;
982 }
983 if ( opposite->v1 == second ) {
984 opposite = opposite->v1link;
985 } else if ( opposite->v2 == second ) {
986 opposite = opposite->v2link;
987 } else {
988 common->Error( "BuildOptTriangles: mislinked edge" );
989 return;
990 }
991 }
992
993 if ( !opposite ) {
994 common->Printf( "Warning: BuildOptTriangles: couldn't locate opposite\n" );
995 return;
996 }
997
998 if ( dmapGlobals.drawflag ) {
999 qglColor3f(1,0,1);
1000 qglBegin( GL_LINES );
1001 qglVertex3fv( opposite->v1->pv.ToFloatPtr() );
1002 qglVertex3fv( opposite->v2->pv.ToFloatPtr() );
1003 qglEnd();
1004 qglFlush();
1005 }
1006
1007 // create new triangle
1008 optTri = (optTri_t *)Mem_Alloc( sizeof( *optTri ) );
1009 optTri->v[0] = first;
1010 optTri->v[1] = second;
1011 optTri->v[2] = third;
1012 optTri->midpoint = ( optTri->v[0]->pv + optTri->v[1]->pv + optTri->v[2]->pv ) * ( 1.0f / 3.0f );
1013 optTri->next = island->tris;
1014 island->tris = optTri;
1015
1016 if ( dmapGlobals.drawflag ) {
1017 qglColor3f( 1, 1, 1 );
1018 qglPointSize( 4 );
1019 qglBegin( GL_POINTS );
1020 qglVertex3fv( optTri->midpoint.ToFloatPtr() );
1021 qglEnd();
1022 qglFlush();
1023 }
1024
1025 // find the midpoint, and scan through all the original triangles to
1026 // see if it is inside any of them
1027 for ( tri = island->group->triList ; tri ; tri = tri->next ) {
1028 if ( PointInTri( optTri->midpoint, tri, island ) ) {
1029 break;
1030 }
1031 }
1032 if ( tri ) {
1033 optTri->filled = true;
1034 } else {
1035 optTri->filled = false;
1036 }
1037 if ( dmapGlobals.drawflag ) {
1038 if ( optTri->filled ) {
1039 qglColor3f( ( 128 + orandom.RandomInt( 127 ) )/ 255.0, 0, 0 );
1040 } else {
1041 qglColor3f( 0, ( 128 + orandom.RandomInt( 127 ) ) / 255.0, 0 );
1042 }
1043 qglBegin( GL_TRIANGLES );
1044 qglVertex3fv( optTri->v[0]->pv.ToFloatPtr() );
1045 qglVertex3fv( optTri->v[1]->pv.ToFloatPtr() );
1046 qglVertex3fv( optTri->v[2]->pv.ToFloatPtr() );
1047 qglEnd();
1048 qglColor3f( 1, 1, 1 );
1049 qglBegin( GL_LINE_LOOP );
1050 qglVertex3fv( optTri->v[0]->pv.ToFloatPtr() );
1051 qglVertex3fv( optTri->v[1]->pv.ToFloatPtr() );
1052 qglVertex3fv( optTri->v[2]->pv.ToFloatPtr() );
1053 qglEnd();
1054 qglFlush();
1055 }
1056
1057 // link the triangle to it's edges
1058 LinkTriToEdge( optTri, e1 );
1059 LinkTriToEdge( optTri, e2 );
1060 LinkTriToEdge( optTri, opposite );
1061 }
1062
1063 // debugging tool
1064 #if 0
1065 static void ReportNearbyVertexes( const optVertex_t *v, const optIsland_t *island ) {
1066 const optVertex_t *ov;
1067 float d;
1068 idVec3 vec;
1069
1070 common->Printf( "verts near 0x%p (%f, %f)\n", v, v->pv[0], v->pv[1] );
1071 for ( ov = island->verts ; ov ; ov = ov->islandLink ) {
1072 if ( ov == v ) {
1073 continue;
1074 }
1075
1076 vec = ov->pv - v->pv;
1077
1078 d = vec.Length();
1079 if ( d < 1 ) {
1080 common->Printf( "0x%p = (%f, %f)\n", ov, ov->pv[0], ov->pv[1] );
1081 }
1082 }
1083 }
1084 #endif
1085
1086 /*
1087 ====================
1088 BuildOptTriangles
1089
1090 Generate a new list of triangles from the optEdeges
1091 ====================
1092 */
BuildOptTriangles(optIsland_t * island)1093 static void BuildOptTriangles( optIsland_t *island ) {
1094 optVertex_t *ov, *second = NULL, *third = NULL, *middle = NULL;
1095 optEdge_t *e1, *e1Next = NULL, *e2, *e2Next = NULL, *check, *checkNext = NULL;
1096
1097 // free them
1098 FreeOptTriangles( island );
1099
1100 // clear the vertex emitted flags
1101 for ( ov = island->verts ; ov ; ov = ov->islandLink ) {
1102 ov->emited = false;
1103 }
1104
1105 // clear the edge triangle links
1106 for ( check = island->edges ; check ; check = check->islandLink ) {
1107 check->frontTri = check->backTri = NULL;
1108 }
1109
1110 // check all possible triangle made up out of the
1111 // edges coming off the vertex
1112 for ( ov = island->verts ; ov ; ov = ov->islandLink ) {
1113 if ( !ov->edges ) {
1114 continue;
1115 }
1116
1117 #if 0
1118 if ( dmapGlobals.drawflag && ov == (optVertex_t *)0x1845a60 ) {
1119 for ( e1 = ov->edges ; e1 ; e1 = e1Next ) {
1120 qglBegin( GL_LINES );
1121 qglColor3f( 0,1,0 );
1122 qglVertex3fv( e1->v1->pv.ToFloatPtr() );
1123 qglVertex3fv( e1->v2->pv.ToFloatPtr() );
1124 qglEnd();
1125 qglFlush();
1126 if ( e1->v1 == ov ) {
1127 e1Next = e1->v1link;
1128 } else if ( e1->v2 == ov ) {
1129 e1Next = e1->v2link;
1130 }
1131 }
1132 }
1133 #endif
1134 for ( e1 = ov->edges ; e1 ; e1 = e1Next ) {
1135 if ( e1->v1 == ov ) {
1136 second = e1->v2;
1137 e1Next = e1->v1link;
1138 } else if ( e1->v2 == ov ) {
1139 second = e1->v1;
1140 e1Next = e1->v2link;
1141 } else {
1142 common->Error( "BuildOptTriangles: mislinked edge" );
1143 }
1144
1145 // if the vertex has already been used, it can't be used again
1146 if ( second->emited ) {
1147 continue;
1148 }
1149
1150 for ( e2 = ov->edges ; e2 ; e2 = e2Next ) {
1151 if ( e2->v1 == ov ) {
1152 third = e2->v2;
1153 e2Next = e2->v1link;
1154 } else if ( e2->v2 == ov ) {
1155 third = e2->v1;
1156 e2Next = e2->v2link;
1157 } else {
1158 common->Error( "BuildOptTriangles: mislinked edge" );
1159 }
1160 if ( e2 == e1 ) {
1161 continue;
1162 }
1163
1164 // if the vertex has already been used, it can't be used again
1165 if ( third->emited ) {
1166 continue;
1167 }
1168
1169 // if the triangle is backwards or degenerate, don't use it
1170 if ( !IsTriangleValid( ov, second, third ) ) {
1171 continue;
1172 }
1173
1174 // see if any other edge bisects these two, which means
1175 // this triangle shouldn't be used
1176 for ( check = ov->edges ; check ; check = checkNext ) {
1177 if ( check->v1 == ov ) {
1178 middle = check->v2;
1179 checkNext = check->v1link;
1180 } else if ( check->v2 == ov ) {
1181 middle = check->v1;
1182 checkNext = check->v2link;
1183 } else {
1184 common->Error( "BuildOptTriangles: mislinked edge" );
1185 }
1186
1187 if ( check == e1 || check == e2 ) {
1188 continue;
1189 }
1190
1191 if ( IsTriangleValid( ov, second, middle )
1192 && IsTriangleValid( ov, middle, third ) ) {
1193 break; // should use the subdivided ones
1194 }
1195 }
1196
1197 if ( check ) {
1198 continue; // don't use it
1199 }
1200
1201 // the triangle is valid
1202 CreateOptTri( ov, e1, e2, island );
1203 }
1204 }
1205
1206 // later vertexes will not emit triangles that use an
1207 // edge that this vert has already used
1208 ov->emited = true;
1209 }
1210 }
1211
1212
1213
1214 /*
1215 ====================
1216 RegenerateTriangles
1217
1218 Add new triangles to the group's regeneratedTris
1219 ====================
1220 */
RegenerateTriangles(optIsland_t * island)1221 static void RegenerateTriangles( optIsland_t *island ) {
1222 optTri_t *optTri;
1223 mapTri_t *tri;
1224 int c_out;
1225
1226 c_out = 0;
1227
1228 for ( optTri = island->tris ; optTri ; optTri = optTri->next ) {
1229 if ( !optTri->filled ) {
1230 continue;
1231 }
1232
1233 // create a new mapTri_t
1234 tri = AllocTri();
1235
1236 tri->material = island->group->material;
1237 tri->mergeGroup = island->group->mergeGroup;
1238
1239 tri->v[0] = optTri->v[0]->v;
1240 tri->v[1] = optTri->v[1]->v;
1241 tri->v[2] = optTri->v[2]->v;
1242
1243 idPlane plane;
1244 PlaneForTri( tri, plane );
1245 if ( plane.Normal() * dmapGlobals.mapPlanes[ island->group->planeNum ].Normal() <= 0 ) {
1246 // this can happen reasonably when a triangle is nearly degenerate in
1247 // optimization planar space, and winds up being degenerate in 3D space
1248 common->Printf( "WARNING: backwards triangle generated!\n" );
1249 // discard it
1250 FreeTri( tri );
1251 continue;
1252 }
1253
1254 c_out++;
1255 tri->next = island->group->regeneratedTris;
1256 island->group->regeneratedTris = tri;
1257 }
1258
1259 FreeOptTriangles( island );
1260
1261 if ( dmapGlobals.verbose ) {
1262 common->Printf( "%6i tris out\n", c_out );
1263 }
1264 }
1265
1266 //===========================================================================
1267
1268 /*
1269 ====================
1270 RemoveInteriorEdges
1271
1272 Edges that have triangles of the same type (filled / empty)
1273 on both sides will be removed
1274 ====================
1275 */
RemoveInteriorEdges(optIsland_t * island)1276 static void RemoveInteriorEdges( optIsland_t *island ) {
1277 int c_interiorEdges;
1278 int c_exteriorEdges;
1279 optEdge_t *e, *next;
1280 bool front, back;
1281
1282 c_exteriorEdges = 0;
1283 c_interiorEdges = 0;
1284 for ( e = island->edges ; e ; e = next ) {
1285 // we might remove the edge, so get the next link now
1286 next = e->islandLink;
1287
1288 if ( !e->frontTri ) {
1289 front = false;
1290 } else {
1291 front = e->frontTri->filled;
1292 }
1293 if ( !e->backTri ) {
1294 back = false;
1295 } else {
1296 back = e->backTri->filled;
1297 }
1298
1299 if ( front == back ) {
1300 // free the edge
1301 UnlinkEdge( e, island );
1302 c_interiorEdges++;
1303 continue;
1304 }
1305
1306 c_exteriorEdges++;
1307 }
1308
1309 if ( dmapGlobals.verbose ) {
1310 common->Printf( "%6i original interior edges\n", c_interiorEdges );
1311 common->Printf( "%6i original exterior edges\n", c_exteriorEdges );
1312 }
1313 }
1314
1315 //==================================================================================
1316
1317 typedef struct {
1318 optVertex_t *v1, *v2;
1319 } originalEdges_t;
1320
1321 /*
1322 =================
1323 AddEdgeIfNotAlready
1324 =================
1325 */
AddEdgeIfNotAlready(optVertex_t * v1,optVertex_t * v2)1326 void AddEdgeIfNotAlready( optVertex_t *v1, optVertex_t *v2 ) {
1327 optEdge_t *e;
1328
1329 // make sure that there isn't an identical edge already added
1330 for ( e = v1->edges ; e ; ) {
1331 if ( ( e->v1 == v1 && e->v2 == v2 ) || ( e->v1 == v2 && e->v2 == v1 ) ) {
1332 return; // already added
1333 }
1334 if ( e->v1 == v1 ) {
1335 e = e->v1link;
1336 } else if ( e->v2 == v1 ) {
1337 e = e->v2link;
1338 } else {
1339 common->Error( "SplitEdgeByList: bad edge link" );
1340 }
1341 }
1342
1343 // this edge is a keeper
1344 e = AllocEdge();
1345 e->v1 = v1;
1346 e->v2 = v2;
1347
1348 e->islandLink = NULL;
1349
1350 // link the edge to its verts
1351 LinkEdge( e );
1352 }
1353
1354
1355
1356 /*
1357 =================
1358 DrawOriginalEdges
1359 =================
1360 */
DrawOriginalEdges(int numOriginalEdges,originalEdges_t * originalEdges)1361 static void DrawOriginalEdges( int numOriginalEdges, originalEdges_t *originalEdges ) {
1362 int i;
1363
1364 if ( !dmapGlobals.drawflag ) {
1365 return;
1366 }
1367 Draw_ClearWindow();
1368
1369 qglBegin( GL_LINES );
1370 for ( i = 0 ; i < numOriginalEdges ; i++ ) {
1371 qglColor3f( 1, 0, 0 );
1372 qglVertex3fv( originalEdges[i].v1->pv.ToFloatPtr() );
1373 qglColor3f( 0, 0, 0 );
1374 qglVertex3fv( originalEdges[i].v2->pv.ToFloatPtr() );
1375 }
1376 qglEnd();
1377 qglFlush();
1378 }
1379
1380
1381 typedef struct edgeCrossing_s {
1382 struct edgeCrossing_s *next;
1383 optVertex_t *ov;
1384 } edgeCrossing_t;
1385
1386 static originalEdges_t *originalEdges;
1387 static int numOriginalEdges;
1388
1389 /*
1390 =================
1391 AddOriginalTriangle
1392 =================
1393 */
AddOriginalTriangle(optVertex_t * v[3])1394 static void AddOriginalTriangle( optVertex_t *v[3] ) {
1395 optVertex_t *v1, *v2;
1396
1397 // if this triangle is backwards (possible with epsilon issues)
1398 // ignore it completely
1399 if ( !IsTriangleValid( v[0], v[1], v[2] ) ) {
1400 common->Printf( "WARNING: backwards triangle in input!\n" );
1401 return;
1402 }
1403
1404 for ( int i = 0 ; i < 3 ; i++ ) {
1405 v1 = v[i];
1406 v2 = v[(i+1)%3];
1407
1408 if ( v1 == v2 ) {
1409 // this probably shouldn't happen, because the
1410 // tri would be degenerate
1411 continue;
1412 }
1413 int j;
1414 // see if there is an existing one
1415 for ( j = 0 ; j < numOriginalEdges ; j++ ) {
1416 if ( originalEdges[j].v1 == v1 && originalEdges[j].v2 == v2 ) {
1417 break;
1418 }
1419 if ( originalEdges[j].v2 == v1 && originalEdges[j].v1 == v2 ) {
1420 break;
1421 }
1422 }
1423
1424 if ( j == numOriginalEdges ) {
1425 // add it
1426 originalEdges[j].v1 = v1;
1427 originalEdges[j].v2 = v2;
1428 numOriginalEdges++;
1429 }
1430 }
1431 }
1432
1433 /*
1434 =================
1435 AddOriginalEdges
1436 =================
1437 */
AddOriginalEdges(optimizeGroup_t * opt)1438 static void AddOriginalEdges( optimizeGroup_t *opt ) {
1439 mapTri_t *tri;
1440 optVertex_t *v[3];
1441 int numTris;
1442
1443 if ( dmapGlobals.verbose ) {
1444 common->Printf( "----\n" );
1445 common->Printf( "%6i original tris\n", CountTriList( opt->triList ) );
1446 }
1447
1448 optBounds.Clear();
1449
1450 // allocate space for max possible edges
1451 numTris = CountTriList( opt->triList );
1452 originalEdges = (originalEdges_t *)Mem_Alloc( numTris * 3 * sizeof( *originalEdges ) );
1453 numOriginalEdges = 0;
1454
1455 // add all unique triangle edges
1456 numOptVerts = 0;
1457 numOptEdges = 0;
1458 for ( tri = opt->triList ; tri ; tri = tri->next ) {
1459 v[0] = tri->optVert[0] = FindOptVertex( &tri->v[0], opt );
1460 v[1] = tri->optVert[1] = FindOptVertex( &tri->v[1], opt );
1461 v[2] = tri->optVert[2] = FindOptVertex( &tri->v[2], opt );
1462
1463 AddOriginalTriangle( v );
1464 }
1465 }
1466
1467 /*
1468 =====================
1469 SplitOriginalEdgesAtCrossings
1470 =====================
1471 */
SplitOriginalEdgesAtCrossings(optimizeGroup_t * opt)1472 void SplitOriginalEdgesAtCrossings( optimizeGroup_t *opt ) {
1473 int i, j, k, l;
1474 int numOriginalVerts;
1475 edgeCrossing_t **crossings;
1476
1477 numOriginalVerts = numOptVerts;
1478 // now split any crossing edges and create optEdges
1479 // linked to the vertexes
1480
1481 // debug drawing bounds
1482 dmapGlobals.drawBounds = optBounds;
1483
1484 dmapGlobals.drawBounds[0][0] -= 2;
1485 dmapGlobals.drawBounds[0][1] -= 2;
1486 dmapGlobals.drawBounds[1][0] += 2;
1487 dmapGlobals.drawBounds[1][1] += 2;
1488
1489 // generate crossing points between all the original edges
1490 crossings = (edgeCrossing_t **)Mem_ClearedAlloc( numOriginalEdges * sizeof( *crossings ) );
1491
1492 for ( i = 0 ; i < numOriginalEdges ; i++ ) {
1493 if ( dmapGlobals.drawflag ) {
1494 DrawOriginalEdges( numOriginalEdges, originalEdges );
1495 qglBegin( GL_LINES );
1496 qglColor3f( 0, 1, 0 );
1497 qglVertex3fv( originalEdges[i].v1->pv.ToFloatPtr() );
1498 qglColor3f( 0, 0, 1 );
1499 qglVertex3fv( originalEdges[i].v2->pv.ToFloatPtr() );
1500 qglEnd();
1501 qglFlush();
1502 }
1503 for ( j = i+1 ; j < numOriginalEdges ; j++ ) {
1504 optVertex_t *v1, *v2, *v3, *v4;
1505 optVertex_t *newVert;
1506 edgeCrossing_t *cross;
1507
1508 v1 = originalEdges[i].v1;
1509 v2 = originalEdges[i].v2;
1510 v3 = originalEdges[j].v1;
1511 v4 = originalEdges[j].v2;
1512
1513 if ( !EdgesCross( v1, v2, v3, v4 ) ) {
1514 continue;
1515 }
1516
1517 // this is the only point in optimization where
1518 // completely new points are created, and it only
1519 // happens if there is overlapping coplanar
1520 // geometry in the source triangles
1521 newVert = EdgeIntersection( v1, v2, v3, v4, opt );
1522
1523 if ( !newVert ) {
1524 //common->Printf( "lines %i (%i to %i) and %i (%i to %i) are colinear\n", i, v1 - optVerts, v2 - optVerts,
1525 // j, v3 - optVerts, v4 - optVerts ); // !@#
1526 // colinear, so add both verts of each edge to opposite
1527 if ( VertexBetween( v3, v1, v2 ) ) {
1528 cross = (edgeCrossing_t *)Mem_ClearedAlloc( sizeof( *cross ) );
1529 cross->ov = v3;
1530 cross->next = crossings[i];
1531 crossings[i] = cross;
1532 }
1533
1534 if ( VertexBetween( v4, v1, v2 ) ) {
1535 cross = (edgeCrossing_t *)Mem_ClearedAlloc( sizeof( *cross ) );
1536 cross->ov = v4;
1537 cross->next = crossings[i];
1538 crossings[i] = cross;
1539 }
1540
1541 if ( VertexBetween( v1, v3, v4 ) ) {
1542 cross = (edgeCrossing_t *)Mem_ClearedAlloc( sizeof( *cross ) );
1543 cross->ov = v1;
1544 cross->next = crossings[j];
1545 crossings[j] = cross;
1546 }
1547
1548 if ( VertexBetween( v2, v3, v4 ) ) {
1549 cross = (edgeCrossing_t *)Mem_ClearedAlloc( sizeof( *cross ) );
1550 cross->ov = v2;
1551 cross->next = crossings[j];
1552 crossings[j] = cross;
1553 }
1554
1555 continue;
1556 }
1557 #if 0
1558 if ( newVert && newVert != v1 && newVert != v2 && newVert != v3 && newVert != v4 ) {
1559 common->Printf( "lines %i (%i to %i) and %i (%i to %i) cross at new point %i\n", i, v1 - optVerts, v2 - optVerts,
1560 j, v3 - optVerts, v4 - optVerts, newVert - optVerts );
1561 } else if ( newVert ) {
1562 common->Printf( "lines %i (%i to %i) and %i (%i to %i) intersect at old point %i\n", i, v1 - optVerts, v2 - optVerts,
1563 j, v3 - optVerts, v4 - optVerts, newVert - optVerts );
1564 }
1565 #endif
1566 if ( newVert != v1 && newVert != v2 ) {
1567 cross = (edgeCrossing_t *)Mem_ClearedAlloc( sizeof( *cross ) );
1568 cross->ov = newVert;
1569 cross->next = crossings[i];
1570 crossings[i] = cross;
1571 }
1572
1573 if ( newVert != v3 && newVert != v4 ) {
1574 cross = (edgeCrossing_t *)Mem_ClearedAlloc( sizeof( *cross ) );
1575 cross->ov = newVert;
1576 cross->next = crossings[j];
1577 crossings[j] = cross;
1578 }
1579
1580 }
1581 }
1582
1583
1584 // now split each edge by its crossing points
1585 // colinear edges will have duplicated edges added, but it won't hurt anything
1586 for ( i = 0 ; i < numOriginalEdges ; i++ ) {
1587 edgeCrossing_t *cross, *nextCross;
1588 int numCross;
1589 optVertex_t **sorted;
1590
1591 numCross = 0;
1592 for ( cross = crossings[i] ; cross ; cross = cross->next ) {
1593 numCross++;
1594 }
1595 numCross += 2; // account for originals
1596 sorted = (optVertex_t **)Mem_Alloc( numCross * sizeof( *sorted ) );
1597 sorted[0] = originalEdges[i].v1;
1598 sorted[1] = originalEdges[i].v2;
1599 j = 2;
1600 for ( cross = crossings[i] ; cross ; cross = nextCross ) {
1601 nextCross = cross->next;
1602 sorted[j] = cross->ov;
1603 Mem_Free( cross );
1604 j++;
1605 }
1606
1607 // add all possible fragment combinations that aren't divided
1608 // by another point
1609 for ( j = 0 ; j < numCross ; j++ ) {
1610 for ( k = j+1 ; k < numCross ; k++ ) {
1611 for ( l = 0 ; l < numCross ; l++ ) {
1612 if ( sorted[l] == sorted[j] || sorted[l] == sorted[k] ) {
1613 continue;
1614 }
1615 if ( sorted[j] == sorted[k] ) {
1616 continue;
1617 }
1618 if ( VertexBetween( sorted[l], sorted[j], sorted[k] ) ) {
1619 break;
1620 }
1621 }
1622 if ( l == numCross ) {
1623 //common->Printf( "line %i fragment from point %i to %i\n", i, sorted[j] - optVerts, sorted[k] - optVerts );
1624 AddEdgeIfNotAlready( sorted[j], sorted[k] );
1625 }
1626 }
1627 }
1628
1629 Mem_Free( sorted );
1630 }
1631
1632
1633 Mem_Free( crossings );
1634 Mem_Free( originalEdges );
1635
1636 // check for duplicated edges
1637 for ( i = 0 ; i < numOptEdges ; i++ ) {
1638 for ( j = i+1 ; j < numOptEdges ; j++ ) {
1639 if ( ( optEdges[i].v1 == optEdges[j].v1 && optEdges[i].v2 == optEdges[j].v2 )
1640 || ( optEdges[i].v1 == optEdges[j].v2 && optEdges[i].v2 == optEdges[j].v1 ) ) {
1641 common->Printf( "duplicated optEdge\n" );
1642 }
1643 }
1644 }
1645
1646 if ( dmapGlobals.verbose ) {
1647 common->Printf( "%6i original edges\n", numOriginalEdges );
1648 common->Printf( "%6i edges after splits\n", numOptEdges );
1649 common->Printf( "%6i original vertexes\n", numOriginalVerts );
1650 common->Printf( "%6i vertexes after splits\n", numOptVerts );
1651 }
1652 }
1653
1654 //=================================================================
1655
1656
1657 /*
1658 ===================
1659 CullUnusedVerts
1660
1661 Unlink any verts with no edges, so they
1662 won't be used in the retriangulation
1663 ===================
1664 */
CullUnusedVerts(optIsland_t * island)1665 static void CullUnusedVerts( optIsland_t *island ) {
1666 optVertex_t **prev, *vert;
1667 int c_keep, c_free;
1668 optEdge_t *edge;
1669
1670 c_keep = 0;
1671 c_free = 0;
1672
1673 for ( prev = &island->verts ; *prev ; ) {
1674 vert = *prev;
1675
1676 if ( !vert->edges ) {
1677 // free it
1678 *prev = vert->islandLink;
1679 c_free++;
1680 } else {
1681 edge = vert->edges;
1682 if ( ( edge->v1 == vert && !edge->v1link )
1683 || ( edge->v2 == vert && !edge->v2link ) ) {
1684 // is is occasionally possible to get a vert
1685 // with only a single edge when colinear optimizations
1686 // crunch down a complex sliver
1687 UnlinkEdge( edge, island );
1688 // free it
1689 *prev = vert->islandLink;
1690 c_free++;
1691 } else {
1692 prev = &vert->islandLink;
1693 c_keep++;
1694 }
1695 }
1696 }
1697
1698 if ( dmapGlobals.verbose ) {
1699 common->Printf( "%6i verts kept\n", c_keep );
1700 common->Printf( "%6i verts freed\n", c_free );
1701 }
1702 }
1703
1704
1705
1706 /*
1707 ====================
1708 OptimizeIsland
1709
1710 At this point, all needed vertexes are already in the
1711 list, including any that were added at crossing points.
1712
1713 Interior and colinear vertexes will be removed, and
1714 a new triangulation will be created.
1715 ====================
1716 */
OptimizeIsland(optIsland_t * island)1717 static void OptimizeIsland( optIsland_t *island ) {
1718 // add space-filling fake edges so we have a complete
1719 // triangulation of a convex hull before optimization
1720 AddInteriorEdges( island );
1721 DrawEdges( island );
1722
1723 // determine all the possible triangles, and decide if
1724 // the are filled or empty
1725 BuildOptTriangles( island );
1726
1727 // remove interior vertexes that have filled triangles
1728 // between all their edges
1729 RemoveInteriorEdges( island );
1730 DrawEdges( island );
1731
1732 ValidateEdgeCounts( island );
1733
1734 // remove vertexes that only have two colinear edges
1735 CombineColinearEdges( island );
1736 CullUnusedVerts( island );
1737 DrawEdges( island );
1738
1739 // add new internal edges between the remaining exterior edges
1740 // to give us a full triangulation again
1741 AddInteriorEdges( island );
1742 DrawEdges( island );
1743
1744 // determine all the possible triangles, and decide if
1745 // the are filled or empty
1746 BuildOptTriangles( island );
1747
1748 // make mapTri_t out of the filled optTri_t
1749 RegenerateTriangles( island );
1750 }
1751
1752 /*
1753 ================
1754 AddVertexToIsland_r
1755 ================
1756 */
1757 #if 0
1758 static void AddVertexToIsland_r( optVertex_t *vert, optIsland_t *island ) {
1759 optEdge_t *e;
1760
1761 // we can't just check islandLink, because the
1762 // last vert will have a NULL
1763 if ( vert->addedToIsland ) {
1764 return;
1765 }
1766 vert->addedToIsland = true;
1767 vert->islandLink = island->verts;
1768 island->verts = vert;
1769
1770 for ( e = vert->edges ; e ; ) {
1771 if ( !e->addedToIsland ) {
1772 e->addedToIsland = true;
1773
1774 e->islandLink = island->edges;
1775 island->edges = e;
1776 }
1777
1778 if ( e->v1 == vert ) {
1779 AddVertexToIsland_r( e->v2, island );
1780 e = e->v1link;
1781 continue;
1782 }
1783 if ( e->v2 == vert ) {
1784 AddVertexToIsland_r( e->v1, island );
1785 e = e->v2link;
1786 continue;
1787 }
1788 common->Error( "AddVertexToIsland_r: mislinked vert" );
1789 }
1790
1791 }
1792 #endif
1793
1794 /*
1795 ====================
1796 SeparateIslands
1797
1798 While the algorithm should theoretically handle any collection
1799 of triangles, there are speed and stability benefits to making
1800 it work on as small a list as possible, so separate disconnected
1801 collections of edges and process separately.
1802
1803 FIXME: we need to separate the source triangles before
1804 doing this, because PointInSourceTris() can give a bad answer if
1805 the source list has triangles not used in the optimization
1806 ====================
1807 */
1808 #if 0
1809 static void SeparateIslands( optimizeGroup_t *opt ) {
1810 int i;
1811 optIsland_t island;
1812 int numIslands;
1813
1814 DrawAllEdges();
1815
1816 numIslands = 0;
1817 for ( i = 0 ; i < numOptVerts ; i++ ) {
1818 if ( optVerts[i].addedToIsland ) {
1819 continue;
1820 }
1821 numIslands++;
1822 memset( &island, 0, sizeof( island ) );
1823 island.group = opt;
1824 AddVertexToIsland_r( &optVerts[i], &island );
1825 OptimizeIsland( &island );
1826 }
1827 if ( dmapGlobals.verbose ) {
1828 common->Printf( "%6i islands\n", numIslands );
1829 }
1830 }
1831 #endif
1832
DontSeparateIslands(optimizeGroup_t * opt)1833 static void DontSeparateIslands( optimizeGroup_t *opt ) {
1834 int i;
1835 optIsland_t island;
1836
1837 DrawAllEdges();
1838
1839 memset( &island, 0, sizeof( island ) );
1840 island.group = opt;
1841
1842 // link everything together
1843 for ( i = 0 ; i < numOptVerts ; i++ ) {
1844 optVerts[i].islandLink = island.verts;
1845 island.verts = &optVerts[i];
1846 }
1847
1848 for ( i = 0 ; i < numOptEdges ; i++ ) {
1849 optEdges[i].islandLink = island.edges;
1850 island.edges = &optEdges[i];
1851 }
1852
1853 OptimizeIsland( &island );
1854 }
1855
1856
1857 /*
1858 ====================
1859 PointInSourceTris
1860
1861 This is a sloppy bounding box check
1862 ====================
1863 */
1864 #if 0
1865 static bool PointInSourceTris( float x, float y, float z, optimizeGroup_t *opt ) {
1866 mapTri_t *tri;
1867 idBounds b;
1868 idVec3 p;
1869
1870 if ( !opt->material->IsDrawn() ) {
1871 return false;
1872 }
1873
1874 p[0] = x;
1875 p[1] = y;
1876 p[2] = z;
1877 for ( tri = opt->triList ; tri ; tri = tri->next ) {
1878 b.Clear();
1879 b.AddPoint( tri->v[0].xyz );
1880 b.AddPoint( tri->v[1].xyz );
1881 b.AddPoint( tri->v[2].xyz );
1882
1883 if ( b.ContainsPoint( p ) ) {
1884 return true;
1885 }
1886 }
1887 return false;
1888 }
1889 #endif
1890
1891 /*
1892 ====================
1893 OptimizeOptList
1894 ====================
1895 */
OptimizeOptList(optimizeGroup_t * opt)1896 static void OptimizeOptList( optimizeGroup_t *opt ) {
1897 optimizeGroup_t *oldNext;
1898
1899 // fix the t junctions among this single list
1900 // so we can match edges
1901 // can we avoid doing this if colinear vertexes break edges?
1902 oldNext = opt->nextGroup;
1903 opt->nextGroup = NULL;
1904 FixAreaGroupsTjunctions( opt );
1905 opt->nextGroup = oldNext;
1906
1907 // create the 2D vectors
1908 dmapGlobals.mapPlanes[opt->planeNum].Normal().NormalVectors( opt->axis[0], opt->axis[1] );
1909
1910 AddOriginalEdges( opt );
1911 SplitOriginalEdgesAtCrossings( opt );
1912
1913 #if 0
1914 // seperate any discontinuous areas for individual optimization
1915 // to reduce the scope of the problem
1916 SeparateIslands( opt );
1917 #else
1918 DontSeparateIslands( opt );
1919 #endif
1920
1921 // now free the hash verts
1922 FreeTJunctionHash();
1923
1924 // free the original list and use the new one
1925 FreeTriList( opt->triList );
1926 opt->triList = opt->regeneratedTris;
1927 opt->regeneratedTris = NULL;
1928 }
1929
1930
1931 /*
1932 ==================
1933 SetGroupTriPlaneNums
1934
1935 Copies the group planeNum to every triangle in each group
1936 ==================
1937 */
SetGroupTriPlaneNums(optimizeGroup_t * groups)1938 void SetGroupTriPlaneNums( optimizeGroup_t *groups ) {
1939 mapTri_t *tri;
1940 optimizeGroup_t *group;
1941
1942 for ( group = groups ; group ; group = group->nextGroup ) {
1943 for ( tri = group->triList ; tri ; tri = tri->next ) {
1944 tri->planeNum = group->planeNum;
1945 }
1946 }
1947 }
1948
1949
1950 /*
1951 ===================
1952 OptimizeGroupList
1953
1954 This will also fix tjunctions
1955
1956 ===================
1957 */
OptimizeGroupList(optimizeGroup_t * groupList)1958 void OptimizeGroupList( optimizeGroup_t *groupList ) {
1959 int c_in, c_edge, c_tjunc2;
1960 optimizeGroup_t *group;
1961
1962 if ( !groupList ) {
1963 return;
1964 }
1965
1966 c_in = CountGroupListTris( groupList );
1967
1968 // optimize and remove colinear edges, which will
1969 // re-introduce some t junctions
1970 for ( group = groupList ; group ; group = group->nextGroup ) {
1971 OptimizeOptList( group );
1972 }
1973 c_edge = CountGroupListTris( groupList );
1974
1975 // fix t junctions again
1976 FixAreaGroupsTjunctions( groupList );
1977 FreeTJunctionHash();
1978 c_tjunc2 = CountGroupListTris( groupList );
1979
1980 SetGroupTriPlaneNums( groupList );
1981
1982 common->Printf( "----- OptimizeAreaGroups Results -----\n" );
1983 common->Printf( "%6i tris in\n", c_in );
1984 common->Printf( "%6i tris after edge removal optimization\n", c_edge );
1985 common->Printf( "%6i tris after final t junction fixing\n", c_tjunc2 );
1986 }
1987
1988
1989 /*
1990 ==================
1991 OptimizeEntity
1992 ==================
1993 */
OptimizeEntity(uEntity_t * e)1994 void OptimizeEntity( uEntity_t *e ) {
1995 int i;
1996
1997 common->Printf( "----- OptimizeEntity -----\n" );
1998 for ( i = 0 ; i < e->numAreas ; i++ ) {
1999 OptimizeGroupList( e->areas[i].groups );
2000 }
2001 }
2002