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