]> icculus.org git repositories - divverent/netradiant.git/blob - tools/quake3/q3map2/tjunction.c
Merge remote-tracking branch 'origin/divVerent/weird-shift-a'
[divverent/netradiant.git] / tools / quake3 / q3map2 / tjunction.c
1 /* -------------------------------------------------------------------------------
2
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
5
6 This file is part of GtkRadiant.
7
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21
22 ----------------------------------------------------------------------------------
23
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
26
27 ------------------------------------------------------------------------------- */
28
29
30
31 /* marker */
32 #define TJUNCTION_C
33
34
35
36 /* dependencies */
37 #include "q3map2.h"
38
39
40
41
42 typedef struct edgePoint_s {
43         float           intercept;
44         vec3_t          xyz;
45         struct edgePoint_s      *prev, *next;
46 } edgePoint_t;
47
48 typedef struct edgeLine_s {
49         vec3_t          normal1;
50         float           dist1;
51         
52         vec3_t          normal2;
53         float           dist2;
54         
55         vec3_t          origin;
56         vec3_t          dir;
57
58         edgePoint_t     *chain;         // unused element of doubly linked list
59 } edgeLine_t;
60
61 typedef struct {
62         float           length;
63         bspDrawVert_t   *dv[2];
64 } originalEdge_t;
65
66 originalEdge_t  *originalEdges = NULL;
67 int                             numOriginalEdges;
68 int                             allocatedOriginalEdges = 0;
69
70
71 edgeLine_t              *edgeLines = NULL;
72 int                             numEdgeLines;
73 int                             allocatedEdgeLines = 0;
74
75 int                             c_degenerateEdges;
76 int                             c_addedVerts;
77 int                             c_totalVerts;
78
79 int                             c_natural, c_rotate, c_cant;
80
81 // these should be whatever epsilon we actually expect,
82 // plus SNAP_INT_TO_FLOAT 
83 #define LINE_POSITION_EPSILON   0.25
84 #define POINT_ON_LINE_EPSILON   0.25
85
86 /*
87 ====================
88 InsertPointOnEdge
89 ====================
90 */
91 void InsertPointOnEdge( vec3_t v, edgeLine_t *e ) {
92         vec3_t          delta;
93         float           d;
94         edgePoint_t     *p, *scan;
95
96         VectorSubtract( v, e->origin, delta );
97         d = DotProduct( delta, e->dir );
98
99         p = safe_malloc( sizeof(edgePoint_t) );
100         p->intercept = d;
101         VectorCopy( v, p->xyz );
102
103         if ( e->chain->next == e->chain ) {
104                 e->chain->next = e->chain->prev = p;
105                 p->next = p->prev = e->chain;
106                 return;
107         }
108
109         scan = e->chain->next;
110         for ( ; scan != e->chain ; scan = scan->next ) {
111                 d = p->intercept - scan->intercept;
112                 if ( d > -LINE_POSITION_EPSILON && d < LINE_POSITION_EPSILON ) {
113                         free( p );
114                         return;         // the point is already set
115                 }
116
117                 if ( p->intercept < scan->intercept ) {
118                         // insert here
119                         p->prev = scan->prev;
120                         p->next = scan;
121                         scan->prev->next = p;
122                         scan->prev = p;
123                         return;
124                 }
125         }
126
127         // add at the end
128         p->prev = scan->prev;
129         p->next = scan;
130         scan->prev->next = p;
131         scan->prev = p;
132 }
133
134
135 /*
136 ====================
137 AddEdge
138 ====================
139 */
140 int AddEdge( vec3_t v1, vec3_t v2, qboolean createNonAxial ) {
141         int                     i;
142         edgeLine_t      *e;
143         float           d;
144         vec3_t          dir;
145
146         VectorSubtract( v2, v1, dir );
147         d = VectorNormalize( dir, dir );
148         if ( d < 0.1 ) {
149                 // if we added a 0 length vector, it would make degenerate planes
150                 c_degenerateEdges++;
151                 return -1;
152         }
153
154         if ( !createNonAxial ) {
155                 if ( fabs( dir[0] + dir[1] + dir[2] ) != 1.0 ) {
156                         AUTOEXPAND_BY_REALLOC(originalEdges, numOriginalEdges, allocatedOriginalEdges, 1024);
157                         originalEdges[ numOriginalEdges ].dv[0] = (bspDrawVert_t *)v1;
158                         originalEdges[ numOriginalEdges ].dv[1] = (bspDrawVert_t *)v2;
159                         originalEdges[ numOriginalEdges ].length = d;
160                         numOriginalEdges++;
161                         return -1;
162                 }
163         }
164
165         for ( i = 0 ; i < numEdgeLines ; i++ ) {
166                 e = &edgeLines[i];
167
168                 d = DotProduct( v1, e->normal1 ) - e->dist1;
169                 if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
170                         continue;
171                 }
172                 d = DotProduct( v1, e->normal2 ) - e->dist2;
173                 if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
174                         continue;
175                 }
176
177                 d = DotProduct( v2, e->normal1 ) - e->dist1;
178                 if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
179                         continue;
180                 }
181                 d = DotProduct( v2, e->normal2 ) - e->dist2;
182                 if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
183                         continue;
184                 }
185
186                 // this is the edge
187                 InsertPointOnEdge( v1, e );
188                 InsertPointOnEdge( v2, e );
189                 return i;
190         }
191
192         // create a new edge
193         AUTOEXPAND_BY_REALLOC(edgeLines, numEdgeLines, allocatedEdgeLines, 1024);
194
195         e = &edgeLines[ numEdgeLines ];
196         numEdgeLines++;
197
198         e->chain = safe_malloc( sizeof(edgePoint_t) );
199         e->chain->next = e->chain->prev = e->chain;
200
201         VectorCopy( v1, e->origin );
202         VectorCopy( dir, e->dir );
203
204         MakeNormalVectors( e->dir, e->normal1, e->normal2 );
205         e->dist1 = DotProduct( e->origin, e->normal1 );
206         e->dist2 = DotProduct( e->origin, e->normal2 );
207
208         InsertPointOnEdge( v1, e );
209         InsertPointOnEdge( v2, e );
210
211         return numEdgeLines - 1;
212 }
213
214
215
216 /*
217 AddSurfaceEdges()
218 adds a surface's edges
219 */
220
221 void AddSurfaceEdges( mapDrawSurface_t *ds )
222 {
223         int             i;
224         
225
226         for( i = 0; i < ds->numVerts; i++ )
227         {
228                 /* save the edge number in the lightmap field so we don't need to look it up again */
229                 ds->verts[i].lightmap[ 0 ][ 0 ] = 
230                         AddEdge( ds->verts[ i ].xyz, ds->verts[ (i + 1) % ds->numVerts ].xyz, qfalse );
231         }
232 }
233
234
235
236 /*
237 ColinearEdge()
238 determines if an edge is colinear
239 */
240
241 qboolean ColinearEdge( vec3_t v1, vec3_t v2, vec3_t v3 )
242 {
243         vec3_t  midpoint, dir, offset, on;
244         float   d;
245
246         VectorSubtract( v2, v1, midpoint );
247         VectorSubtract( v3, v1, dir );
248         d = VectorNormalize( dir, dir );
249         if ( d == 0 ) {
250                 return qfalse;  // degenerate
251         }
252
253         d = DotProduct( midpoint, dir );
254         VectorScale( dir, d, on );
255         VectorSubtract( midpoint, on, offset );
256         d = VectorLength ( offset );
257
258         if ( d < 0.1 ) {
259                 return qtrue;
260         }
261
262         return qfalse;
263 }
264
265
266
267 /*
268 ====================
269 AddPatchEdges
270
271 Add colinear border edges, which will fix some classes of patch to
272 brush tjunctions
273 ====================
274 */
275 void AddPatchEdges( mapDrawSurface_t *ds ) {
276         int             i;
277         float   *v1, *v2, *v3;
278
279         for ( i = 0 ; i < ds->patchWidth - 2; i+=2 ) {
280                 v1 = ds->verts[ i ].xyz;
281                 v2 = ds->verts[ i + 1 ].xyz;
282                 v3 = ds->verts[ i + 2 ].xyz;
283
284                 // if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
285                 if ( ColinearEdge( v1, v2, v3 ) ) {
286                         AddEdge( v1, v3, qfalse );
287                 }
288
289                 v1 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i ].xyz;
290                 v2 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i + 1 ].xyz;
291                 v3 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i + 2 ].xyz;
292
293                 // if v2 is on the v1 to v3 line, add an edge from v1 to v3
294                 if ( ColinearEdge( v1, v2, v3 ) ) {
295                         AddEdge( v1, v3, qfalse );
296                 }
297         }
298
299         for ( i = 0 ; i < ds->patchHeight - 2 ; i+=2 ) {
300                 v1 = ds->verts[ i * ds->patchWidth ].xyz;
301                 v2 = ds->verts[ ( i + 1 ) * ds->patchWidth ].xyz;
302                 v3 = ds->verts[ ( i + 2 ) * ds->patchWidth ].xyz;
303
304                 // if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
305                 if ( ColinearEdge( v1, v2, v3 ) ) {
306                         AddEdge( v1, v3, qfalse );
307                 }
308
309                 v1 = ds->verts[ ( ds->patchWidth - 1 ) + i * ds->patchWidth ].xyz;
310                 v2 = ds->verts[ ( ds->patchWidth - 1 ) + ( i + 1 ) * ds->patchWidth ].xyz;
311                 v3 = ds->verts[ ( ds->patchWidth - 1 ) + ( i + 2 ) * ds->patchWidth ].xyz;
312
313                 // if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
314                 if ( ColinearEdge( v1, v2, v3 ) ) {
315                         AddEdge( v1, v3, qfalse );
316                 }
317         }
318
319
320 }
321
322
323 /*
324 ====================
325 FixSurfaceJunctions
326 ====================
327 */
328 #define MAX_SURFACE_VERTS       256
329 void FixSurfaceJunctions( mapDrawSurface_t *ds ) {
330         int                     i, j, k;
331         edgeLine_t      *e;
332         edgePoint_t     *p;
333         int                     originalVerts;
334         int                     counts[MAX_SURFACE_VERTS];
335         int                     originals[MAX_SURFACE_VERTS];
336         int                     firstVert[MAX_SURFACE_VERTS];
337         bspDrawVert_t   verts[MAX_SURFACE_VERTS], *v1, *v2;
338         int                     numVerts;
339         float           start, end, frac, c;
340         vec3_t          delta;
341         
342         
343         originalVerts = ds->numVerts;
344         
345         numVerts = 0;
346         for ( i = 0 ; i < ds->numVerts ; i++ )
347         {
348                 counts[i] = 0;
349                 firstVert[i] = numVerts;
350
351                 // copy first vert
352                 if ( numVerts == MAX_SURFACE_VERTS ) {
353                         Error( "MAX_SURFACE_VERTS" );
354                 }
355                 verts[numVerts] = ds->verts[i];
356                 originals[numVerts] = i;
357                 numVerts++;
358
359                 // check to see if there are any t junctions before the next vert
360                 v1 = &ds->verts[i];
361                 v2 = &ds->verts[ (i+1) % ds->numVerts ];
362
363                 j = (int)ds->verts[i].lightmap[ 0 ][ 0 ];
364                 if ( j == -1 ) {
365                         continue;               // degenerate edge
366                 }
367                 e = &edgeLines[ j ];
368                 
369                 VectorSubtract( v1->xyz, e->origin, delta );
370                 start = DotProduct( delta, e->dir );
371
372                 VectorSubtract( v2->xyz, e->origin, delta );
373                 end = DotProduct( delta, e->dir );
374
375
376                 if ( start < end ) {
377                         p = e->chain->next;
378                 } else {
379                         p = e->chain->prev;
380                 }
381
382                 for (  ; p != e->chain ;  ) {
383                         if ( start < end ) {
384                                 if ( p->intercept > end - ON_EPSILON ) {
385                                         break;
386                                 }
387                         } else {
388                                 if ( p->intercept < end + ON_EPSILON ) {
389                                         break;
390                                 }
391                         }
392
393                         if ( 
394                                 ( start < end && p->intercept > start + ON_EPSILON ) ||
395                                 ( start > end && p->intercept < start - ON_EPSILON ) ) {
396                                 // insert this point
397                                 if ( numVerts == MAX_SURFACE_VERTS ) {
398                                         Error( "MAX_SURFACE_VERTS" );
399                                 }
400                                 
401                                 /* take the exact intercept point */
402                                 VectorCopy( p->xyz, verts[ numVerts ].xyz );
403                                 
404                                 /* interpolate the texture coordinates */
405                                 frac = ( p->intercept - start ) / ( end - start );
406                                 for ( j = 0 ; j < 2 ; j++ ) {
407                                         verts[ numVerts ].st[j] = v1->st[j] + 
408                                                 frac * ( v2->st[j] - v1->st[j] );
409                                 }
410                                 
411                                 /* copy the normal (FIXME: what about nonplanar surfaces? */
412                                 VectorCopy( v1->normal, verts[ numVerts ].normal );
413                                 
414                                 /* ydnar: interpolate the color */
415                                 for( k = 0; k < MAX_LIGHTMAPS; k++ )
416                                 {
417                                         for( j = 0; j < 4; j++ )
418                                         {
419                                                 c = (float) v1->color[ k ][ j ] + frac * ((float) v2->color[ k ][ j ] - (float) v1->color[ k ][ j ]);
420                                                 verts[ numVerts ].color[ k ][ j ] = (byte) (c < 255.0f ? c : 255);
421                                         }
422                                 }
423                                 
424                                 /* next... */
425                                 originals[ numVerts ] = i;
426                                 numVerts++;
427                                 counts[ i ]++;
428                         }
429
430                         if ( start < end ) {
431                                 p = p->next;
432                         } else {
433                                 p = p->prev;
434                         }
435                 }
436         }
437
438         c_addedVerts += numVerts - ds->numVerts;
439         c_totalVerts += numVerts;
440
441
442         // FIXME: check to see if the entire surface degenerated
443         // after snapping
444
445         // rotate the points so that the initial vertex is between
446         // two non-subdivided edges
447         for ( i = 0 ; i < numVerts ; i++ ) {
448                 if ( originals[ (i+1) % numVerts ] == originals[ i ] ) {
449                         continue;
450                 }
451                 j = (i + numVerts - 1 ) % numVerts;
452                 k = (i + numVerts - 2 ) % numVerts;
453                 if ( originals[ j ] == originals[ k ] ) {
454                         continue;
455                 }
456                 break;
457         }
458
459         if ( i == 0 ) {
460                 // fine the way it is
461                 c_natural++;
462
463                 ds->numVerts = numVerts;
464                 ds->verts = safe_malloc( numVerts * sizeof( *ds->verts ) );
465                 memcpy( ds->verts, verts, numVerts * sizeof( *ds->verts ) );
466
467                 return;
468         }
469         if ( i == numVerts ) {
470                 // create a vertex in the middle to start the fan
471                 c_cant++;
472
473 /*
474                 memset ( &verts[numVerts], 0, sizeof( verts[numVerts] ) );
475                 for ( i = 0 ; i < numVerts ; i++ ) {
476                         for ( j = 0 ; j < 10 ; j++ ) {
477                                 verts[numVerts].xyz[j] += verts[i].xyz[j];
478                         }
479                 }
480                 for ( j = 0 ; j < 10 ; j++ ) {
481                         verts[numVerts].xyz[j] /= numVerts;
482                 }
483
484                 i = numVerts;
485                 numVerts++;
486 */
487         } else {
488                 // just rotate the vertexes
489                 c_rotate++;
490
491         }
492
493         ds->numVerts = numVerts;
494         ds->verts = safe_malloc( numVerts * sizeof( *ds->verts ) );
495
496         for ( j = 0 ; j < ds->numVerts ; j++ ) {
497                 ds->verts[j] = verts[ ( j + i ) % ds->numVerts ];
498         }
499 }
500
501
502
503
504
505 /*
506 FixBrokenSurface() - ydnar
507 removes nearly coincident verts from a planar winding surface
508 returns qfalse if the surface is broken
509 */
510
511 extern void SnapWeldVector( vec3_t a, vec3_t b, vec3_t out );
512
513 #define DEGENERATE_EPSILON      0.1
514
515 int             c_broken = 0;
516
517 qboolean FixBrokenSurface( mapDrawSurface_t *ds )
518 {
519         bspDrawVert_t   *dv1, *dv2, avg;
520         int                     i, j, k;
521         float           dist;
522         
523         
524         /* dummy check */
525         if( ds == NULL )
526                 return qfalse;
527         if( ds->type != SURFACE_FACE )
528                 return qfalse;
529         
530         /* check all verts */
531         for( i = 0; i < ds->numVerts; i++ )
532         {
533                 /* get verts */
534                 dv1 = &ds->verts[ i ];
535                 dv2 = &ds->verts[ (i + 1) % ds->numVerts ];
536                 
537                 /* degenerate edge? */
538                 VectorSubtract( dv1->xyz, dv2->xyz, avg.xyz );
539                 dist = VectorLength( avg.xyz );
540                 if( dist < DEGENERATE_EPSILON )
541                 {
542                         Sys_FPrintf( SYS_VRB, "WARNING: Degenerate T-junction edge found, fixing...\n" );
543
544                         /* create an average drawvert */
545                         /* ydnar 2002-01-26: added nearest-integer welding preference */
546                         SnapWeldVector( dv1->xyz, dv2->xyz, avg.xyz );
547                         VectorAdd( dv1->normal, dv2->normal, avg.normal );
548                         VectorNormalize( avg.normal, avg.normal );
549                         avg.st[ 0 ] = (dv1->st[ 0 ] + dv2->st[ 0 ]) * 0.5f;
550                         avg.st[ 1 ] = (dv1->st[ 1 ] + dv2->st[ 1 ]) * 0.5f;
551                         
552                         /* lightmap st/colors */
553                         for( k = 0; k < MAX_LIGHTMAPS; k++ )
554                         {
555                                 avg.lightmap[ k ][ 0 ] = (dv1->lightmap[ k ][ 0 ] + dv2->lightmap[ k ][ 0 ]) * 0.5f;
556                                 avg.lightmap[ k ][ 1 ] = (dv1->lightmap[ k ][ 1 ] + dv2->lightmap[ k ][ 1 ]) * 0.5f;
557                                 for( j = 0; j < 4; j++ )
558                                         avg.color[ k ][ j ] = (int) (dv1->color[ k ][ j ] + dv2->color[ k ][ j ]) >> 1;
559                         }
560                         
561                         /* ydnar: der... */
562                         memcpy( dv1, &avg, sizeof( avg ) );
563                         
564                         /* move the remaining verts */
565                         for( k = i + 2; k < ds->numVerts; k++ )
566                         {
567                                 /* get verts */
568                                 dv1 = &ds->verts[ k ];
569                                 dv2 = &ds->verts[ k - 1 ];
570                                 
571                                 /* copy */
572                                 memcpy( dv2, dv1, sizeof( bspDrawVert_t ) );
573                         }
574                         ds->numVerts--;
575                         
576                         /* after welding, we have to consider the same vertex again, as it now has a new neighbor dv2 */
577                         --i;
578
579                         /* should ds->numVerts have become 0, then i is now -1. In the next iteration, the loop will abort. */
580                 }
581         }
582         
583         /* one last check and return */
584         return ds->numVerts >= 3;
585 }
586
587
588
589
590
591
592
593
594
595 /*
596 ================
597 EdgeCompare
598 ================
599 */
600 int EdgeCompare( const void *elem1, const void *elem2 ) {
601         float   d1, d2;
602
603         d1 = ((const originalEdge_t *)elem1)->length;
604         d2 = ((const originalEdge_t *)elem2)->length;
605
606         if ( d1 < d2 ) {
607                 return -1;
608         }
609         if ( d2 > d1 ) {
610                 return 1;
611         }
612         return 0;
613 }
614
615
616
617 /*
618 FixTJunctions
619 call after the surface list has been pruned
620 */
621
622 void FixTJunctions( entity_t *ent )
623 {
624         int                                     i;
625         mapDrawSurface_t        *ds;
626         shaderInfo_t            *si;
627         int                                     axialEdgeLines;
628         originalEdge_t          *e;
629         bspDrawVert_t   *dv;
630         
631         /* meta mode has its own t-junction code (currently not as good as this code) */
632         //%     if( meta )
633         //%             return; 
634         
635         /* note it */
636         Sys_FPrintf( SYS_VRB, "--- FixTJunctions ---\n" );
637         numEdgeLines = 0;
638         numOriginalEdges = 0;
639         
640         // add all the edges
641         // this actually creates axial edges, but it
642         // only creates originalEdge_t structures
643         // for non-axial edges
644         for ( i = ent->firstDrawSurf ; i < numMapDrawSurfs ; i++ )
645         {
646                 /* get surface and early out if possible */
647                 ds = &mapDrawSurfs[ i ];
648                 si = ds->shaderInfo;
649                 if( (si->compileFlags & C_NODRAW) || si->autosprite || si->notjunc || ds->numVerts == 0 )
650                         continue;
651                 
652                 /* ydnar: gs mods: handle the various types of surfaces */
653                 switch( ds->type )
654                 {
655                         /* handle brush faces */
656                         case SURFACE_FACE:
657                                 AddSurfaceEdges( ds );
658                                 break;
659                         
660                         /* handle patches */
661                         case SURFACE_PATCH:
662                                 AddPatchEdges( ds );
663                                 break;
664                         
665                         /* fixme: make triangle surfaces t-junction */
666                         default:
667                                 break;
668                 }
669         }
670
671         axialEdgeLines = numEdgeLines;
672
673         // sort the non-axial edges by length
674         qsort( originalEdges, numOriginalEdges, sizeof(originalEdges[0]), EdgeCompare );
675
676         // add the non-axial edges, longest first
677         // this gives the most accurate edge description
678         for ( i = 0 ; i < numOriginalEdges ; i++ ) {
679                 e = &originalEdges[i];
680                 dv = e->dv[0]; // e might change during AddEdge
681                 dv->lightmap[ 0 ][ 0 ] = AddEdge( e->dv[ 0 ]->xyz, e->dv[ 1 ]->xyz, qtrue );
682         }
683
684         Sys_FPrintf( SYS_VRB, "%9d axial edge lines\n", axialEdgeLines );
685         Sys_FPrintf( SYS_VRB, "%9d non-axial edge lines\n", numEdgeLines - axialEdgeLines );
686         Sys_FPrintf( SYS_VRB, "%9d degenerate edges\n", c_degenerateEdges );
687
688         // insert any needed vertexes
689         for( i = ent->firstDrawSurf; i < numMapDrawSurfs ; i++ )
690         {
691                 /* get surface and early out if possible */
692                 ds = &mapDrawSurfs[ i ];
693                 si = ds->shaderInfo;
694                 if( (si->compileFlags & C_NODRAW) || si->autosprite || si->notjunc || ds->numVerts == 0 || ds->type != SURFACE_FACE )
695                         continue;
696                 
697                 /* ydnar: gs mods: handle the various types of surfaces */
698                 switch( ds->type )
699                 {
700                         /* handle brush faces */
701                         case SURFACE_FACE:
702                                 FixSurfaceJunctions( ds );
703                                 if( FixBrokenSurface( ds ) == qfalse )
704                                 {
705                                         c_broken++;
706                                         ClearSurface( ds );
707                                 }
708                                 break;
709                         
710                         /* fixme: t-junction triangle models and patches */
711                         default:
712                                 break;
713                 }
714         }
715         
716         /* emit some statistics */
717         Sys_FPrintf( SYS_VRB, "%9d verts added for T-junctions\n", c_addedVerts );
718         Sys_FPrintf( SYS_VRB, "%9d total verts\n", c_totalVerts );
719         Sys_FPrintf( SYS_VRB, "%9d naturally ordered\n", c_natural );
720         Sys_FPrintf( SYS_VRB, "%9d rotated orders\n", c_rotate );
721         Sys_FPrintf( SYS_VRB, "%9d can't order\n", c_cant );
722         Sys_FPrintf( SYS_VRB, "%9d broken (degenerate) surfaces removed\n", c_broken );
723 }