Advertisement
illwieckz

denoised bloodmap's surface_meta.c 81ce7df

Jul 8th, 2018
403
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 60.36 KB | None | 0 0
  1. /* -------------------------------------------------------------------------------
  2.  
  3. Copyright (C) 1999-2006 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 SURFACE_META_C
  33.  
  34.  
  35.  
  36. /* dependencies */
  37. #include "q3map2.h"
  38.  
  39.  
  40.  
  41. #define LIGHTMAP_EXCEEDED   -1
  42. #define S_EXCEEDED          -2
  43. #define T_EXCEEDED          -3
  44. #define ST_EXCEEDED         -4
  45. #define UNSUITABLE_TRIANGLE -10
  46. #define VERTS_EXCEEDED      -1000
  47. #define INDEXES_EXCEEDED    -2000
  48.  
  49. #define GROW_META_VERTS     32768
  50. #define GROW_META_TRIANGLES 16384
  51.  
  52. int                 numMetaSurfaces, numPatchMetaSurfaces;
  53.  
  54. int                 maxMetaVerts = 0;
  55. int                 numMetaVerts = 0;
  56. int                 firstSearchMetaVert = 0;
  57. bspDrawVert_t       *metaVerts = NULL;
  58.  
  59. int                 maxMetaTriangles = 0;
  60. int                 numMetaTriangles = 0;
  61. metaTriangle_t      *metaTriangles = NULL;
  62.  
  63. /*
  64. ClearMetaVertexes()
  65. called before staring a new entity to clear out the triangle list
  66. */
  67.  
  68. void ClearMetaTriangles( void ) {
  69.     numMetaVerts = 0;
  70.     numMetaTriangles = 0;
  71. }
  72.  
  73.  
  74.  
  75. /*
  76. FindMetaVertex()
  77. finds a matching metavertex in the global list, returning its index
  78. */
  79.  
  80. static int FindMetaVertex( bspDrawVert_t *src ) {
  81.     bspDrawVert_t *temp;
  82.    
  83.     /* enough space? */
  84.     if ( numMetaVerts >= maxMetaVerts )     {
  85.         /* reallocate more room */
  86.         maxMetaVerts += GROW_META_VERTS;
  87.         temp = (bspDrawVert_t *)safe_malloc( maxMetaVerts * sizeof( bspDrawVert_t ) );
  88.         if ( metaVerts != NULL ) {
  89.             memcpy( temp, metaVerts, numMetaVerts * sizeof( bspDrawVert_t ) );
  90.             free( metaVerts );
  91.         }
  92.         metaVerts = temp;
  93.     }
  94.    
  95.     /* add the triangle */
  96.     memcpy( &metaVerts[ numMetaVerts ], src, sizeof( bspDrawVert_t ) );
  97.     numMetaVerts++;
  98.    
  99.     /* return the count */
  100.     return (numMetaVerts - 1);
  101. }
  102.  
  103.  
  104.  
  105. /*
  106. AddMetaTriangle()
  107. adds a new meta triangle, allocating more memory if necessary
  108. */
  109.  
  110. static int AddMetaTriangle( void ) {
  111.     metaTriangle_t  *temp;
  112.  
  113.  
  114.     /* enough space? */
  115.     if ( numMetaTriangles >= maxMetaTriangles ) {
  116.         /* reallocate more room */
  117.         maxMetaTriangles += GROW_META_TRIANGLES;
  118.         temp = (metaTriangle_t *)safe_malloc( maxMetaTriangles * sizeof( metaTriangle_t ) );
  119.         if ( metaTriangles != NULL ) {
  120.             memcpy( temp, metaTriangles, numMetaTriangles * sizeof( metaTriangle_t ) );
  121.             free( metaTriangles );
  122.         }
  123.         metaTriangles = temp;
  124.     }
  125.    
  126.     /* increment and return */
  127.     numMetaTriangles++;
  128.     return numMetaTriangles - 1;
  129. }
  130.  
  131.  
  132.  
  133. /*
  134. FindMetaTriangle()
  135. finds a matching metatriangle in the global list,
  136. otherwise adds it and returns the index to the metatriangle
  137. */
  138.  
  139. int FindMetaTriangle( metaTriangle_t *src, bspDrawVert_t *a, bspDrawVert_t *b, bspDrawVert_t *c, int planeNum ) {
  140.     int             triIndex;
  141.     float           ab, bc, ac, s;
  142.     vec3_t          dir;
  143.  
  144.  
  145.  
  146.     /* detect degenerate triangles fixme: do something proper here */
  147.     VectorSubtract( a->xyz, b->xyz, dir );
  148.     if ( VectorLength( dir ) < 0.125f ) {
  149.         return -1;
  150.     }
  151.     VectorSubtract( b->xyz, c->xyz, dir );
  152.     if ( VectorLength( dir ) < 0.125f ) {
  153.         return -1;
  154.     }
  155.     VectorSubtract( c->xyz, a->xyz, dir );
  156.     if ( VectorLength( dir ) < 0.125f ) {
  157.         return -1;
  158.     }
  159.    
  160.     /* find plane */
  161.     if ( planeNum >= 0 ) {
  162.         /* because of precision issues with small triangles, try to use the specified plane */
  163.         src->planeNum = planeNum;
  164.         VectorCopy( mapplanes[ planeNum ].normal, src->plane );
  165.         src->plane[ 3 ] = mapplanes[ planeNum ].dist;
  166.     }
  167.     else
  168.     {
  169.         /* calculate a plane from the triangle's points (and bail if a plane can't be constructed) */
  170.         src->planeNum = -1;
  171.         if ( PlaneFromPoints( src->plane, a->xyz, b->xyz, c->xyz ) == qfalse ) {
  172.             return -1;
  173.         }
  174.     }
  175.    
  176.     /* ydnar 2002-10-03: repair any bogus normals (busted ase import kludge) */
  177.     if ( VectorIsNull( a->normal ) ) {
  178.         VectorCopy( src->plane, a->normal );
  179.     }
  180.     if ( VectorIsNull( b->normal ) ) {
  181.         VectorCopy( src->plane, b->normal );
  182.     }
  183.     if ( VectorIsNull( c->normal ) ) {
  184.         VectorCopy( src->plane, c->normal );
  185.     }
  186.    
  187.     /* ydnar 2002-10-04: set lightmap axis if not already set */
  188.     if ( !(src->si->compileFlags & C_VERTEXLIT) &&
  189.          src->lightmapAxis[ 0 ] == 0.0f && src->lightmapAxis[ 1 ] == 0.0f && src->lightmapAxis[ 2 ] == 0.0f ) {
  190.         /* the shader can specify an explicit lightmap axis */
  191.         if ( src->si->lightmapAxis[ 0 ] || src->si->lightmapAxis[ 1 ] || src->si->lightmapAxis[ 2 ] ) {
  192.             VectorCopy( src->si->lightmapAxis, src->lightmapAxis );
  193.         }
  194.  
  195.         /* new axis-finding code */
  196.         else {
  197.             CalcLightmapAxis( src->plane, src->lightmapAxis );
  198.         }
  199.     }
  200.    
  201.     /* fill out the src triangle */
  202.     src->indexes[ 0 ] = FindMetaVertex( a );
  203.     src->indexes[ 1 ] = FindMetaVertex( b );
  204.     src->indexes[ 2 ] = FindMetaVertex( c );
  205.  
  206.     /* find area and bounds of meta triagle (for smoothing) */ 
  207.     VectorSubtract(a->xyz, b->xyz, dir);
  208.     ab = VectorLength(dir);
  209.     VectorSubtract(b->xyz, c->xyz, dir);
  210.     bc = VectorLength(dir);
  211.     VectorSubtract(a->xyz, c->xyz, dir);
  212.     ac = VectorLength(dir);
  213.     s = (ab + bc + ac)/2;
  214.     src->area = max(1, sqrt(max(1, s*(s - ab)*(s - bc)*(s - ac)))); // a max is here to fix bogus triangles
  215.  
  216.     /* find bounds of meta triagle */  
  217.     ClearBounds( src->mins, src->maxs );
  218.     AddPointToBounds( a->xyz, src->mins, src->maxs );
  219.     AddPointToBounds( b->xyz, src->mins, src->maxs );
  220.     AddPointToBounds( c->xyz, src->mins, src->maxs );
  221.  
  222.     /* try to find an existing triangle */
  223.     #ifdef USE_EXHAUSTIVE_SEARCH
  224.     {
  225.         int i;
  226.         metaTriangle_t *tri;
  227.  
  228.  
  229.         for ( i = 0, tri = metaTriangles; i < numMetaTriangles; i++, tri++ )
  230.         {
  231.             if ( memcmp( src, tri, sizeof( metaTriangle_t ) ) == 0 ) {
  232.                 return i;
  233.             }
  234.         }
  235.     }
  236.     #endif
  237.    
  238.     /* get a new triangle */
  239.     triIndex = AddMetaTriangle();
  240.    
  241.     /* add the triangle */
  242.     memcpy( &metaTriangles[ triIndex ], src, sizeof( metaTriangle_t ) );
  243.    
  244.     /* return the triangle index */
  245.     return triIndex;
  246. }
  247.  
  248.  
  249.  
  250. /*
  251. SurfaceToMetaTriangles()
  252. converts a classified surface to metatriangles
  253. */
  254.  
  255. static void SurfaceToMetaTriangles( mapDrawSurface_t *ds ) {
  256.     int             i;
  257.     metaTriangle_t  src;
  258.     bspDrawVert_t   a, b, c;
  259.  
  260.  
  261.     /* speed at the expense of memory */
  262.     firstSearchMetaVert = numMetaVerts;
  263.    
  264.     /* only handle valid surfaces */
  265.     if ( ds->type != SURFACE_BAD && ds->numVerts >= 3 && ds->numIndexes >= 3 ) {
  266.         /* walk the indexes and create triangles */
  267.         for ( i = 0; i < ds->numIndexes; i += 3 )
  268.         {
  269.             /* sanity check the indexes */
  270.             if ( ds->indexes[ i ] == ds->indexes[ i + 1 ] ||
  271.                  ds->indexes[ i ] == ds->indexes[ i + 2 ] ||
  272.                  ds->indexes[ i + 1 ] == ds->indexes[ i + 2 ] ) {
  273.                 continue;
  274.             }
  275.            
  276.             /* build a metatriangle */
  277.             src.ds = ds;
  278.             src.si = ds->shaderInfo;
  279.             src.side = (ds->sideRef != NULL ? ds->sideRef->side : NULL);
  280.             src.entityNum = ds->entityNum;
  281.             src.mapEntityNum = ds->mapEntityNum;
  282.             src.planeNum = ds->planeNum;
  283.             src.castShadows = ds->castShadows;
  284.             src.recvShadows = ds->recvShadows;
  285.             VectorCopy( ds->minlight, src.minlight );
  286.             VectorCopy( ds->ambient, src.ambient );
  287.             VectorCopy( ds->colormod, src.colormod );
  288.             src.smoothNormals = ds->smoothNormals;
  289.             src.fogNum = ds->fogNum;
  290.             src.sampleSize = ds->sampleSize;
  291.             VectorCopy( ds->lightmapAxis, src.lightmapAxis );
  292.  
  293.             /* copy drawverts */
  294.             memcpy( &a, &ds->verts[ ds->indexes[ i ] ], sizeof( a ) );
  295.             memcpy( &b, &ds->verts[ ds->indexes[ i + 1 ] ], sizeof( b ) );
  296.             memcpy( &c, &ds->verts[ ds->indexes[ i + 2 ] ], sizeof( c ) );
  297.             FindMetaTriangle( &src, &a, &b, &c, ds->planeNum );
  298.         }
  299.        
  300.         /* add to count */
  301.         numMetaSurfaces++;
  302.     }
  303.    
  304.     /* clear the surface (free verts and indexes, sets it to SURFACE_BAD) */
  305.     ClearSurface( ds );
  306. }
  307.  
  308.  
  309.  
  310. /*
  311. TriangulatePatchSurface()
  312. creates triangles from a patch
  313. */
  314.  
  315. void TriangulatePatchSurface( entity_t *e , mapDrawSurface_t *ds ) {
  316.     int                 iterations, x, y, pw[ 5 ], r;
  317.     mapDrawSurface_t    *dsNew;
  318.     mesh_t              src, *subdivided, *mesh;
  319.     int                 forcePatchMeta;
  320.     int                 patchQuality;
  321.     int                 patchSubdivision;
  322.  
  323.     /* vortex: _patchMeta, _patchQuality, _patchSubdivide support */
  324.     forcePatchMeta = IntForKey(e, "_patchMeta" );
  325.     if (!forcePatchMeta) {
  326.         forcePatchMeta = IntForKey(e, "patchMeta" );
  327.     }
  328.     if (!forcePatchMeta) {
  329.         forcePatchMeta = IntForKey(e, "_pm" );
  330.     }
  331.     patchQuality = IntForKey(e, "_patchQuality" );
  332.     if (!patchQuality) {
  333.         patchQuality = IntForKey(e, "patchQuality" );
  334.     }
  335.     if (!patchQuality) {
  336.         patchQuality = IntForKey(e, "_pq" );
  337.     }
  338.     if (!patchQuality) {
  339.         patchQuality = 1.0;
  340.     }
  341.     patchSubdivision = IntForKey(e, "_patchSubdivide" );
  342.     if (!patchSubdivision) {
  343.         patchSubdivision = IntForKey(e, "patchSubdivide" );
  344.     }
  345.     if (!patchSubdivision) {
  346.         patchSubdivision = IntForKey(e, "_ps" );
  347.     }
  348.  
  349.     /* try to early out */
  350.     if (ds->numVerts == 0 || ds->type != SURFACE_PATCH || ( patchMeta == qfalse && !forcePatchMeta) ) {
  351.         return;
  352.     }
  353.  
  354.     /* make a mesh from the drawsurf */
  355.     src.width = ds->patchWidth;
  356.     src.height = ds->patchHeight;
  357.     src.verts = ds->verts;
  358.     //% subdivided = SubdivideMesh( src, 8, 999 );
  359.     if (patchSubdivision) {
  360.         iterations = IterationsForCurve( ds->longestCurve, patchSubdivision );
  361.     }
  362.     else {
  363.         iterations = IterationsForCurve( ds->longestCurve, max(1, patchSubdivisions / patchQuality) );
  364.     }
  365.  
  366.     subdivided = SubdivideMesh2( src, iterations ); //% ds->maxIterations
  367.    
  368.     /* fit it to the curve and remove colinear verts on rows/columns */
  369.     PutMeshOnCurve( *subdivided );
  370.     mesh = RemoveLinearMeshColumnsRows( subdivided );
  371.     FreeMesh( subdivided );
  372.     //% MakeMeshNormals( mesh );
  373.    
  374.     /* make a copy of the drawsurface */
  375.     dsNew = AllocDrawSurface( SURFACE_META );
  376.     memcpy( dsNew, ds, sizeof( *ds ) );
  377.    
  378.     /* if the patch is nonsolid, then discard it */
  379.     if ( !(ds->shaderInfo->compileFlags & C_SOLID) ) {
  380.         ClearSurface( ds );
  381.     }
  382.    
  383.     /* set new pointer */
  384.     ds = dsNew;
  385.    
  386.     /* basic transmogrification */
  387.     ds->type = SURFACE_META;
  388.     ds->numIndexes = 0;
  389.     ds->indexes = (int *)safe_malloc( mesh->width * mesh->height * 6 * sizeof( int ) );
  390.    
  391.     /* copy the verts in */
  392.     ds->numVerts = (mesh->width * mesh->height);
  393.     ds->verts = mesh->verts;
  394.    
  395.     /* iterate through the mesh quads */
  396.     for ( y = 0; y < (mesh->height - 1); y++ )
  397.     {
  398.         for ( x = 0; x < (mesh->width - 1); x++ )
  399.         {
  400.             /* set indexes */
  401.             pw[ 0 ] = x + (y * mesh->width);
  402.             pw[ 1 ] = x + ((y + 1) * mesh->width);
  403.             pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
  404.             pw[ 3 ] = x + 1 + (y * mesh->width);
  405.             pw[ 4 ] = x + (y * mesh->width);    /* same as pw[ 0 ] */
  406.            
  407.             /* set radix */
  408.             r = (x + y) & 1;
  409.            
  410.             /* make first triangle */
  411.             ds->indexes[ ds->numIndexes++ ] = pw[ r + 0 ];
  412.             ds->indexes[ ds->numIndexes++ ] = pw[ r + 1 ];
  413.             ds->indexes[ ds->numIndexes++ ] = pw[ r + 2 ];
  414.            
  415.             /* make second triangle */
  416.             ds->indexes[ ds->numIndexes++ ] = pw[ r + 0 ];
  417.             ds->indexes[ ds->numIndexes++ ] = pw[ r + 2 ];
  418.             ds->indexes[ ds->numIndexes++ ] = pw[ r + 3 ];
  419.         }
  420.     }
  421.    
  422.     /* free the mesh, but not the verts */
  423.     free( mesh );
  424.    
  425.     /* add to count */
  426.     numPatchMetaSurfaces++;
  427.    
  428.     /* classify it */
  429.     ClassifySurfaces( 1, ds );
  430. }
  431.  
  432. /*
  433. FanFaceSurface() - ydnar
  434. creates a tri-fan from a brush face winding
  435. loosely based on SurfaceAsTriFan()
  436. */
  437.  
  438. void FanFaceSurface( mapDrawSurface_t *ds ) {
  439.     int             i, j, k, a, b, c, color[ MAX_LIGHTMAPS ][ 4 ];
  440.     bspDrawVert_t   *verts, *centroid, *dv;
  441.     double          iv;
  442.    
  443.    
  444.     /* try to early out */
  445.     if ( !ds->numVerts || (ds->type != SURFACE_FACE && ds->type != SURFACE_DECAL) ) {
  446.         return;
  447.     }
  448.    
  449.     /* add a new vertex at the beginning of the surface */
  450.     verts =(bspDrawVert_t *)safe_malloc( ( ds->numVerts + 1 ) * sizeof( bspDrawVert_t ) );
  451.     memset( verts, 0, sizeof( bspDrawVert_t ) );
  452.     memcpy( &verts[ 1 ], ds->verts, ds->numVerts * sizeof( bspDrawVert_t ) );
  453.     free( ds->verts );
  454.     ds->verts = verts;
  455.    
  456.     /* add up the drawverts to create a centroid */
  457.     centroid = &verts[ 0 ];
  458.     memset( color, 0,  4 * MAX_LIGHTMAPS * sizeof( int ) );
  459.     for ( i = 1, dv = &verts[ 1 ]; i < (ds->numVerts + 1); i++, dv++ )
  460.     {
  461.         VectorAdd( centroid->xyz, dv->xyz, centroid->xyz );
  462.         VectorAdd( centroid->normal, dv->normal, centroid->normal );
  463.         for ( j = 0; j < 4; j++ )
  464.         {
  465.             for ( k = 0; k < MAX_LIGHTMAPS; k++ )
  466.                 color[ k ][ j ] += dv->color[ k ][ j ];
  467.             if ( j < 2 ) {
  468.                 centroid->st[ j ] += dv->st[ j ];
  469.                 for ( k = 0; k < MAX_LIGHTMAPS; k++ )
  470.                     centroid->lightmap[ k ][ j ] += dv->lightmap[ k ][ j ];
  471.             }
  472.         }
  473.     }
  474.    
  475.     /* average the centroid */
  476.     iv = 1.0f / ds->numVerts;
  477.     VectorScale( centroid->xyz, iv, centroid->xyz );
  478.     if ( VectorNormalize( centroid->normal, centroid->normal ) <= 0 ) {
  479.         VectorCopy( verts[ 1 ].normal, centroid->normal );
  480.     }
  481.     for ( j = 0; j < 4; j++ )
  482.     {
  483.         for ( k = 0; k < MAX_LIGHTMAPS; k++ )
  484.         {
  485.             color[ k ][ j ] /= ds->numVerts;
  486.             centroid->color[ k ][ j ] = (color[ k ][ j ] < 255.0f ? color[ k ][ j ] : 255);
  487.         }
  488.         if ( j < 2 ) {
  489.             centroid->st[ j ] *= iv;
  490.             for ( k = 0; k < MAX_LIGHTMAPS; k++ )
  491.                 centroid->lightmap[ k ][ j ] *= iv;
  492.         }
  493.     }
  494.    
  495.     /* add to vert count */
  496.     ds->numVerts++;
  497.    
  498.     /* fill indexes in triangle fan order */
  499.     ds->numIndexes = 0;
  500.     ds->indexes = (int *)safe_malloc( ds->numVerts * 3 * sizeof( int ) );
  501.     for ( i = 1; i < ds->numVerts; i++ )
  502.     {
  503.         a = 0;
  504.         b = i;
  505.         c = (i + 1) % ds->numVerts;
  506.         c = c ? c : 1;
  507.         ds->indexes[ ds->numIndexes++ ] = a;
  508.         ds->indexes[ ds->numIndexes++ ] = b;
  509.         ds->indexes[ ds->numIndexes++ ] = c;
  510.     }
  511.    
  512.     /* add to count */
  513.     numFanSurfaces++;
  514.  
  515.     /* classify it */
  516.     ClassifySurfaces( 1, ds );
  517. }
  518.  
  519.  
  520.  
  521. /*
  522. StripFaceSurface() - ydnar
  523. attempts to create a valid tri-strip w/o degenerate triangles from a brush face winding
  524. based on SurfaceAsTriStrip()
  525. */
  526.  
  527. #define MAX_INDEXES     32768
  528.  
  529. void StripFaceSurface( mapDrawSurface_t *ds, qboolean onlyCreateIndexes ) {
  530.     int         i, r, least, rotate, numIndexes, ni, a, b, c, indexes[ MAX_INDEXES ];
  531.     vec_t       *v1, *v2;
  532.  
  533.  
  534.     /* try to early out  */
  535.     if ( !ds->numVerts || (ds->type != SURFACE_FACE && ds->type != SURFACE_DECAL) ) {
  536.         return;
  537.     }
  538.    
  539.     /* is this a simple triangle? */
  540.     if ( ds->numVerts == 3 ) {
  541.         numIndexes = 3;
  542.         VectorSet( indexes, 0, 1, 2 );
  543.     }
  544.     else
  545.     {
  546.         /* ydnar: find smallest coordinate */
  547.         least = 0;
  548.         if ( ds->shaderInfo != NULL && ds->shaderInfo->autosprite == qfalse ) {
  549.             for ( i = 0; i < ds->numVerts; i++ )
  550.             {
  551.                 /* get points */
  552.                 v1 = ds->verts[ i ].xyz;
  553.                 v2 = ds->verts[ least ].xyz;
  554.                
  555.                 /* compare */
  556.                 if ( v1[ 0 ] < v2[ 0 ] ||
  557.                     (v1[ 0 ] == v2[ 0 ] && v1[ 1 ] < v2[ 1 ]) ||
  558.                     (v1[ 0 ] == v2[ 0 ] && v1[ 1 ] == v2[ 1 ] && v1[ 2 ] < v2[ 2 ]) ) {
  559.                     least = i;
  560.                 }
  561.             }
  562.         }
  563.        
  564.         /* determine the triangle strip order */
  565.         numIndexes = (ds->numVerts - 2) * 3;
  566.         if ( numIndexes > MAX_INDEXES ) {
  567.             Error( "MAX_INDEXES exceeded for surface (%d > %d) (%d verts)", numIndexes, MAX_INDEXES, ds->numVerts );
  568.         }
  569.        
  570.         /* try all possible orderings of the points looking for a non-degenerate strip order */
  571.         for ( r = 0; r < ds->numVerts; r++ )
  572.         {
  573.             /* set rotation */
  574.             rotate = (r + least) % ds->numVerts;
  575.            
  576.             /* walk the winding in both directions */
  577.             for ( ni = 0, i = 0; i < ds->numVerts - 2 - i; i++ )
  578.             {
  579.                 /* make indexes */
  580.                 a = (ds->numVerts - 1 - i + rotate) % ds->numVerts;
  581.                 b = (i + rotate ) % ds->numVerts;
  582.                 c = (ds->numVerts - 2 - i + rotate) % ds->numVerts;
  583.                
  584.                 /* test this triangle */
  585.                 if ( ds->numVerts > 4 && IsTriangleDegenerate( ds->verts, a, b, c ) ) {
  586.                     break;
  587.                 }
  588.                 indexes[ ni++ ] = a;
  589.                 indexes[ ni++ ] = b;
  590.                 indexes[ ni++ ] = c;
  591.                
  592.                 /* handle end case */
  593.                 if ( i + 1 != ds->numVerts - 1 - i ) {
  594.                     /* make indexes */
  595.                     a = (ds->numVerts - 2 - i + rotate ) % ds->numVerts;
  596.                     b = (i + rotate ) % ds->numVerts;
  597.                     c = (i + 1 + rotate ) % ds->numVerts;
  598.                    
  599.                     /* test triangle */
  600.                     if ( ds->numVerts > 4 && IsTriangleDegenerate( ds->verts, a, b, c ) ) {
  601.                         break;
  602.                     }
  603.                     indexes[ ni++ ] = a;
  604.                     indexes[ ni++ ] = b;
  605.                     indexes[ ni++ ] = c;
  606.                 }
  607.             }
  608.            
  609.             /* valid strip? */
  610.             if ( ni == numIndexes ) {
  611.                 break;
  612.             }
  613.         }
  614.        
  615.         /* if any triangle in the strip is degenerate, render from a centered fan point instead */
  616.         if ( ni < numIndexes ) {
  617.             FanFaceSurface( ds );
  618.             return;
  619.         }
  620.     }
  621.    
  622.     /* copy strip triangle indexes */
  623.     ds->numIndexes = numIndexes;
  624.     ds->indexes = (int *)safe_malloc( ds->numIndexes * sizeof( int ) );
  625.     memcpy( ds->indexes, indexes, ds->numIndexes * sizeof( int ) );
  626.     if ( onlyCreateIndexes ) {
  627.         return;
  628.     }
  629.  
  630.     /* add to count */
  631.     numStripSurfaces++;
  632.    
  633.     /* classify it */
  634.     ClassifySurfaces( 1, ds );
  635. }
  636.  
  637.  
  638.  
  639. /*
  640.    EmitMetaStatictics
  641.    vortex: prints meta statistics in general output
  642. */
  643.  
  644. void EmitMetaStats(){
  645.     Sys_Printf( "--- EmitMetaStats ---\n" );
  646.     Sys_Printf( "%9d total meta surfaces\n", numMetaSurfaces );
  647.     Sys_Printf( "%9d stripped surfaces\n", numStripSurfaces );
  648.     Sys_Printf( "%9d fanned surfaces\n", numFanSurfaces );
  649.     Sys_Printf( "%9d patch meta surfaces\n", numPatchMetaSurfaces );
  650.     Sys_Printf( "%9d meta verts\n", numMetaVerts );
  651.     Sys_Printf( "%9d meta triangles\n", numMetaTriangles );
  652.     Sys_Printf( "%9d surfaces merged\n", numMergedSurfaces );
  653.     Sys_Printf( "%9d vertexes merged\n", numMergedVerts );
  654. }
  655.  
  656. /*
  657. MakeEntityMetaTriangles()
  658. builds meta triangles from brush faces (tristrips and fans)
  659. */
  660.  
  661. void MakeEntityMetaTriangles( entity_t *e ){
  662.     int                 i, f, fOld, start;
  663.     mapDrawSurface_t    *ds;
  664.    
  665.    
  666.     /* note it */
  667.     Sys_FPrintf( SYS_VRB, "--- MakeEntityMetaTriangles ---\n" );
  668.    
  669.     /* init pacifier */
  670.     fOld = -1;
  671.     start = I_FloatTime();
  672.    
  673.     /* walk the list of surfaces in the entity */
  674.     for ( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
  675.     {
  676.         /* print pacifier */
  677.         f = 10 * (i - e->firstDrawSurf) / (numMapDrawSurfs - e->firstDrawSurf);
  678.         if ( f != fOld ) {
  679.             fOld = f;
  680.             Sys_FPrintf( SYS_VRB, "%d...", f );
  681.         }
  682.        
  683.         /* get surface */
  684.         ds = &mapDrawSurfs[ i ];
  685.         if ( ds->numVerts <= 0 ) {
  686.             continue;
  687.         }
  688.  
  689.         /* ignore autosprite surfaces */
  690.         if ( ds->shaderInfo->autosprite ) {
  691.             continue;
  692.         }
  693.  
  694.         /* meta this surface? */
  695.         if ( ( meta == qfalse && ds->shaderInfo->forceMeta == qfalse ) || ds->shaderInfo->noMeta == qtrue) {
  696.             continue;
  697.         }
  698.  
  699.         /* switch on type */
  700.         switch( ds->type )
  701.         {
  702.             case SURFACE_FACE:
  703.             case SURFACE_DECAL:
  704.                 StripFaceSurface( ds, qfalse );
  705.                 SurfaceToMetaTriangles( ds );
  706.                 break;
  707.            
  708.             case SURFACE_PATCH:
  709.                 TriangulatePatchSurface(e, ds );
  710.                 break;
  711.            
  712.             case SURFACE_TRIANGLES:
  713.                 break;
  714.  
  715.             case SURFACE_FORCED_META:
  716.             case SURFACE_META:
  717.                 SurfaceToMetaTriangles( ds );
  718.                 break;
  719.            
  720.             default:
  721.                 break;
  722.         }
  723.     }
  724.    
  725.     /* print time */
  726.     if ( (numMapDrawSurfs - e->firstDrawSurf) ) {
  727.         Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
  728.     }
  729.    
  730.     /* emit some stats */
  731.     Sys_FPrintf( SYS_VRB, "%9d total meta surfaces\n", numMetaSurfaces );
  732.     Sys_FPrintf( SYS_VRB, "%9d stripped surfaces\n", numStripSurfaces );
  733.     Sys_FPrintf( SYS_VRB, "%9d fanned surfaces\n", numFanSurfaces );
  734.     Sys_FPrintf( SYS_VRB, "%9d patch meta surfaces\n", numPatchMetaSurfaces );
  735.     Sys_FPrintf( SYS_VRB, "%9d meta verts\n", numMetaVerts );
  736.     Sys_FPrintf( SYS_VRB, "%9d meta triangles\n", numMetaTriangles );
  737.    
  738.     /* tidy things up */
  739.     TidyEntitySurfaces( e );
  740. }
  741.  
  742.  
  743. /*
  744. ================================================================================
  745.  
  746.  TJunction Fixing
  747.  
  748. ================================================================================
  749. */
  750.  
  751. /*
  752. PointTriangleIntersect()
  753. assuming that all points lie in plane, determine if pt
  754. is inside the triangle abc
  755. code originally (c) 2001 softSurfer (www.softsurfer.com)
  756. */
  757.  
  758. #define MIN_OUTSIDE_EPSILON     -0.01f
  759. #define MAX_OUTSIDE_EPSILON     1.01f
  760.  
  761. static qboolean PointTriangleIntersect( vec3_t pt, vec4_t plane, vec3_t a, vec3_t b, vec3_t c, vec3_t bary )
  762. {
  763.     vec3_t  u, v, w;
  764.     float   uu, uv, vv, wu, wv, d;
  765.    
  766.    
  767.     /* make vectors */
  768.     VectorSubtract( b, a, u );
  769.     VectorSubtract( c, a, v );
  770.     VectorSubtract( pt, a, w );
  771.    
  772.     /* more setup */
  773.     uu = DotProduct( u, u );
  774.     uv = DotProduct( u, v );
  775.     vv = DotProduct( v, v );
  776.     wu = DotProduct( w, u );
  777.     wv = DotProduct( w, v );
  778.     d = uv * uv - uu * vv;
  779.    
  780.     /* calculate barycentric coordinates */
  781.     bary[ 1 ] = (uv * wv - vv * wu) / d;
  782.     if ( bary[ 1 ] < MIN_OUTSIDE_EPSILON || bary[ 1 ] > MAX_OUTSIDE_EPSILON )
  783.         return qfalse;
  784.     bary[ 2 ] = (uv * wv - uu * wv) / d;
  785.     if ( bary[ 2 ] < MIN_OUTSIDE_EPSILON || bary[ 2 ] > MAX_OUTSIDE_EPSILON )
  786.         return qfalse;
  787.     bary[ 0 ] = 1.0f - (bary[ 1 ] + bary[ 2 ]);
  788.    
  789.     /* point is in triangle */
  790.     return qtrue;
  791. }
  792.  
  793.  
  794.  
  795. /*
  796. CreateEdge()
  797. sets up an edge structure from a plane and 2 points that the edge ab falls lies in
  798. */
  799.  
  800. typedef struct edge_s
  801. {
  802.     vec3_t  origin, edge;
  803.     vec_t   length, kingpinLength;
  804.     int     kingpin;
  805.     vec4_t  plane;
  806. }
  807. edge_t;
  808.  
  809. void CreateEdge( vec4_t plane, vec3_t a, vec3_t b, edge_t *edge ) {
  810.     /* copy edge origin */
  811.     VectorCopy( a, edge->origin );
  812.    
  813.     /* create vector aligned with winding direction of edge */
  814.     VectorSubtract( b, a, edge->edge );
  815.    
  816.     if ( fabs( edge->edge[ 0 ] ) > fabs( edge->edge[ 1 ] ) && fabs( edge->edge[ 0 ] ) > fabs( edge->edge[ 2 ] ) ) {
  817.         edge->kingpin = 0;
  818.     }
  819.     else if ( fabs( edge->edge[ 1 ] ) > fabs( edge->edge[ 0 ] ) && fabs( edge->edge[ 1 ] ) > fabs( edge->edge[ 2 ] ) ) {
  820.         edge->kingpin = 1;
  821.     }
  822.     else {
  823.         edge->kingpin = 2;
  824.     }
  825.     edge->kingpinLength = edge->edge[ edge->kingpin ];
  826.    
  827.     VectorNormalize( edge->edge, edge->edge );
  828.     edge->edge[ 3 ] = DotProduct( a, edge->edge );
  829.     edge->length = DotProduct( b, edge->edge ) - edge->edge[ 3 ];
  830.    
  831.     /* create perpendicular plane that edge lies in */
  832.     CrossProduct( plane, edge->edge, edge->plane );
  833.     edge->plane[ 3 ] = DotProduct( a, edge->plane );
  834. }
  835.  
  836.  
  837.  
  838. /*
  839. FixMetaTJunctions()
  840. fixes t-junctions on meta triangles
  841. */
  842.  
  843. #define TJ_PLANE_EPSILON    (1.0f / 8.0f)
  844. #define TJ_EDGE_EPSILON     (1.0f / 8.0f)
  845. #define TJ_POINT_EPSILON    (1.0f / 8.0f)
  846.  
  847. void FixMetaTJunctions( void ){
  848.     int             i, j, k, f, fOld, start, vertIndex, triIndex, numTJuncs;
  849.     metaTriangle_t  *tri, *newTri;
  850.     shaderInfo_t    *si;
  851.     bspDrawVert_t   *a, *b, *c, junc;
  852.     float           dist, amount;
  853.     vec3_t          pt;
  854.     vec4_t          plane;
  855.     edge_t          edges[ 3 ];
  856.    
  857.    
  858.     /* this code is crap; revisit later */
  859.     return;
  860.    
  861.     /* note it */
  862.     Sys_FPrintf( SYS_VRB, "--- FixMetaTJunctions ---\n" );
  863.    
  864.     /* init pacifier */
  865.     fOld = -1;
  866.     start = I_FloatTime();
  867.    
  868.     /* walk triangle list */
  869.     numTJuncs = 0;
  870.     for ( i = 0; i < numMetaTriangles; i++ )
  871.     {
  872.         /* get triangle */
  873.         tri = &metaTriangles[ i ];
  874.        
  875.         /* print pacifier */
  876.         f = 10 * i / numMetaTriangles;
  877.         if ( f != fOld ) {
  878.             fOld = f;
  879.             Sys_FPrintf( SYS_VRB, "%d...", f );
  880.         }
  881.        
  882.         /* attempt to early out */
  883.         si = tri->si;
  884.         if ( (si->compileFlags & C_NODRAW) || si->autosprite || si->noTJunc ) {
  885.             continue;
  886.         }
  887.        
  888.         /* calculate planes */
  889.         VectorCopy( tri->plane, plane );
  890.         plane[ 3 ] = tri->plane[ 3 ];
  891.         CreateEdge( plane, metaVerts[ tri->indexes[ 0 ] ].xyz, metaVerts[ tri->indexes[ 1 ] ].xyz, &edges[ 0 ] );
  892.         CreateEdge( plane, metaVerts[ tri->indexes[ 1 ] ].xyz, metaVerts[ tri->indexes[ 2 ] ].xyz, &edges[ 1 ] );
  893.         CreateEdge( plane, metaVerts[ tri->indexes[ 2 ] ].xyz, metaVerts[ tri->indexes[ 0 ] ].xyz, &edges[ 2 ] );
  894.        
  895.         /* walk meta vert list */
  896.         for ( j = 0; j < numMetaVerts; j++ )
  897.         {
  898.             /* get vert */
  899.             VectorCopy( metaVerts[ j ].xyz, pt );
  900.  
  901.             /* debug code: darken verts */
  902.             //if ( i == 0 )
  903.             //  VectorSet( metaVerts[ j ].color[ 0 ], 8, 8, 8 );
  904.            
  905.             /* determine if point lies in the triangle's plane */
  906.             dist = DotProduct( pt, plane ) - plane[ 3 ];
  907.             if ( fabs( dist ) > TJ_PLANE_EPSILON ) {
  908.                 continue;
  909.             }
  910.            
  911.             /* skip this point if it already exists in the triangle */
  912.             for ( k = 0; k < 3; k++ )
  913.             {
  914.                 if ( fabs( pt[ 0 ] - metaVerts[ tri->indexes[ k ] ].xyz[ 0 ] ) <= TJ_POINT_EPSILON &&
  915.                      fabs( pt[ 1 ] - metaVerts[ tri->indexes[ k ] ].xyz[ 1 ] ) <= TJ_POINT_EPSILON &&
  916.                      fabs( pt[ 2 ] - metaVerts[ tri->indexes[ k ] ].xyz[ 2 ] ) <= TJ_POINT_EPSILON ) {
  917.                     break;
  918.                 }
  919.             }
  920.             if ( k < 3 ) {
  921.                 continue;
  922.             }
  923.            
  924.             /* walk edges */
  925.             for ( k = 0; k < 3; k++ )
  926.             {
  927.                 /* ignore bogus edges */
  928.                 if ( fabs( edges[ k ].kingpinLength ) < TJ_EDGE_EPSILON ) {
  929.                     continue;
  930.                 }
  931.                
  932.                 /* determine if point lies on the edge */
  933.                 dist = DotProduct( pt, edges[ k ].plane ) - edges[ k ].plane[ 3 ];
  934.                 if ( fabs( dist ) > TJ_EDGE_EPSILON ) {
  935.                     continue;
  936.                 }
  937.                
  938.                 /* determine how far along the edge the point lies */
  939.                 amount = (pt[ edges[ k ].kingpin ] - edges[ k ].origin[ edges[ k ].kingpin ]) / edges[ k ].kingpinLength;
  940.                 if ( amount <= 0.0f || amount >= 1.0f ) {
  941.                     continue;
  942.                 }
  943.                
  944.                 #if 0
  945.                 dist = DotProduct( pt, edges[ k ].edge ) - edges[ k ].edge[ 3 ];
  946.                 if ( dist <= -0.0f || dist >= edges[ k ].length ) {
  947.                     continue;
  948.                 }
  949.                 amount = dist / edges[ k ].length;
  950.                 #endif
  951.                
  952.                 /* debug code: brighten this point */
  953.                 //% metaVerts[ j ].color[ 0 ][ 0 ] += 5;
  954.                 //% metaVerts[ j ].color[ 0 ][ 1 ] += 4;
  955.                 VectorSet( metaVerts[ tri->indexes[ k ] ].color[ 0 ], 255, 204, 0 );
  956.                 VectorSet( metaVerts[ tri->indexes[ (k + 1) % 3 ] ].color[ 0 ], 255, 204, 0 );
  957.                
  958.  
  959.                 /* the edge opposite the zero-weighted vertex was hit, so use that as an amount */
  960.                 a = &metaVerts[ tri->indexes[ k % 3 ] ];
  961.                 b = &metaVerts[ tri->indexes[ (k + 1) % 3 ] ];
  962.                 c = &metaVerts[ tri->indexes[ (k + 2) % 3 ] ];
  963.                
  964.                 /* make new vert */
  965.                 LerpDrawVertAmount( a, b, amount, &junc );
  966.                 VectorCopy( pt, junc.xyz );
  967.                
  968.                 /* compare against existing verts */
  969.                 if ( VectorCompare( junc.xyz, a->xyz ) || VectorCompare( junc.xyz, b->xyz ) || VectorCompare( junc.xyz, c->xyz ) ) {
  970.                     continue;
  971.                 }
  972.                
  973.                 /* see if we can just re-use the existing vert */
  974.                 if ( !memcmp( &metaVerts[ j ], &junc, sizeof( junc ) ) ) {
  975.                     vertIndex = j;
  976.                 }
  977.                 else
  978.                 {
  979.                     /* find new vertex (note: a and b are invalid pointers after this) */
  980.                     firstSearchMetaVert = numMetaVerts;
  981.                     vertIndex = FindMetaVertex( &junc );
  982.                     if ( vertIndex < 0 ) {
  983.                         continue;
  984.                     }
  985.                 }
  986.                        
  987.                 /* make new triangle */
  988.                 triIndex = AddMetaTriangle();
  989.                 if ( triIndex < 0 ) {
  990.                     continue;
  991.                 }
  992.                
  993.                 /* get triangles */
  994.                 tri = &metaTriangles[ i ];
  995.                 newTri = &metaTriangles[ triIndex ];
  996.                
  997.                 /* copy the triangle */
  998.                 memcpy( newTri, tri, sizeof( *tri ) );
  999.                
  1000.                 /* fix verts */
  1001.                 tri->indexes[ (k + 1) % 3 ] = vertIndex;
  1002.                 newTri->indexes[ k ] = vertIndex;
  1003.                
  1004.                 /* recalculate edges */
  1005.                 CreateEdge( plane, metaVerts[ tri->indexes[ 0 ] ].xyz, metaVerts[ tri->indexes[ 1 ] ].xyz, &edges[ 0 ] );
  1006.                 CreateEdge( plane, metaVerts[ tri->indexes[ 1 ] ].xyz, metaVerts[ tri->indexes[ 2 ] ].xyz, &edges[ 1 ] );
  1007.                 CreateEdge( plane, metaVerts[ tri->indexes[ 2 ] ].xyz, metaVerts[ tri->indexes[ 0 ] ].xyz, &edges[ 2 ] );
  1008.                
  1009.                 /* debug code */
  1010.                 metaVerts[ vertIndex ].color[ 0 ][ 0 ] = 255;
  1011.                 metaVerts[ vertIndex ].color[ 0 ][ 1 ] = 204;
  1012.                 metaVerts[ vertIndex ].color[ 0 ][ 2 ] = 0;
  1013.                
  1014.                 /* add to counter and end processing of this vert */
  1015.                 numTJuncs++;
  1016.                 break;
  1017.             }
  1018.         }
  1019.     }
  1020.    
  1021.     /* print time */
  1022.     Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
  1023.    
  1024.     /* emit some stats */
  1025.     Sys_FPrintf( SYS_VRB, "%9d T-junctions added\n", numTJuncs );
  1026. }
  1027.  
  1028.  
  1029.  
  1030. /*
  1031.    SmoothMetaTriangles()
  1032.    averages coincident vertex normals in the meta triangles
  1033.  */
  1034.  
  1035. #define FIND_META_PLANE_EPSILON 0.1
  1036.  
  1037. typedef struct
  1038. {
  1039.     int   metaVert;
  1040.     float distance;
  1041.     float area;
  1042. }smoothVert_t;
  1043.  
  1044. int             maxShadeAngle = 0;
  1045. int             defaultShadeAngle = 0;
  1046. int             numComparisons = 0;
  1047. metaTriangle_t *metaTrianglePlaneTriangles;
  1048. float          *metaVertShadeAngles;
  1049. float          *metaVertAreas;
  1050.  
  1051. #define SMOOTH_MAX_SAMPLES 1024
  1052. #define SMOOTH_THETA_EPSILON 0.000001f
  1053. #define SMOOTH_EQUAL_NORMAL_EPSILON 0.1f
  1054. #define SMOOTH_ORIGIN_EPSILON 0.05f
  1055.  
  1056. /*
  1057. CompareMetaTrianglesPlane()
  1058. compare function for qsort (MetaTriangleFindAreaWeight)
  1059. */
  1060.  
  1061. static int CompareMetaTrianglesPlane( const void *a, const void *b )
  1062. {
  1063.     if ( ((metaTriangle_t*) a)->plane[ 3 ] < ((metaTriangle_t*) b)->plane[ 3 ] ) {
  1064.         return -1;
  1065.     }
  1066.     if ( ((metaTriangle_t*) a)->plane[ 3 ] > ((metaTriangle_t*) b)->plane[ 3 ] ) {
  1067.         return 1;
  1068.     }
  1069.     return 0;
  1070. }
  1071.  
  1072. /*
  1073. MetaTriangleFindAreaWeight()
  1074. find out meta triangle plane area (for area weighting)
  1075. */
  1076. void MetaTriangleFindAreaWeight( int metaTriangleNum )
  1077. {
  1078.     vec3_t mins, maxs, a, b, c, normal;
  1079.     float shadeAngle, area, planedist;
  1080.     int i, startTriangle, endTriangle;
  1081.     metaTriangle_t *tri, *tri2;
  1082.  
  1083.     /* get triangle */
  1084.     tri = &metaTrianglePlaneTriangles[ metaTriangleNum ];
  1085.     VectorCopy( metaVerts[ tri->indexes[ 0 ] ].xyz, a );
  1086.     VectorCopy( metaVerts[ tri->indexes[ 1 ] ].xyz, b );
  1087.     VectorCopy( metaVerts[ tri->indexes[ 2 ] ].xyz, c );
  1088.     VectorCopy( tri->mins, mins );
  1089.     VectorCopy( tri->maxs, maxs );
  1090.     VectorCopy( tri->plane, normal );
  1091.     planedist = tri->plane[ 3 ];
  1092.  
  1093.     /* find adjastent triangles */
  1094.     for (startTriangle = metaTriangleNum; startTriangle > 0; startTriangle--) {
  1095.         if ( ( planedist - metaTrianglePlaneTriangles[ startTriangle ].plane[ 3 ] ) > FIND_META_PLANE_EPSILON) {
  1096.             break;
  1097.         }
  1098.     }
  1099.     for (endTriangle = metaTriangleNum + 1; endTriangle < numMetaTriangles; endTriangle++)
  1100.         if ( ( planedist - metaTrianglePlaneTriangles[ endTriangle ].plane[ 3 ] ) < -FIND_META_PLANE_EPSILON) {
  1101.             break;
  1102.         }
  1103.     }
  1104.  
  1105.     /* get shade angle */
  1106.     shadeAngle = tri->smoothNormals;
  1107.     if (tri->si->noSmooth || (tri->si->compileFlags & C_NODRAW) || shadeAngle < 0) {
  1108.         shadeAngle = -1;
  1109.     }
  1110.     else if (shadeAngle > 0.0f) {
  1111.         shadeAngle = DEG2RAD( shadeAngle );
  1112.     }
  1113.     else if ( tri->si->shadeAngleDegrees > 0.0f ) {
  1114.         shadeAngle = DEG2RAD( tri->si->shadeAngleDegrees );
  1115.     }
  1116.     else {
  1117.         shadeAngle = defaultShadeAngle;
  1118.     }
  1119.  
  1120.     /* flag verts */
  1121.     maxShadeAngle = max(shadeAngle, maxShadeAngle);
  1122.     for ( i = 0; i < 3; i++ ) {
  1123.         if (metaVertShadeAngles[ tri->indexes[ i ] ] > 0 && shadeAngle >= 0) {
  1124.             metaVertShadeAngles[ tri->indexes[ i ] ] = max(shadeAngle, metaVertShadeAngles[ tri->indexes[ i ] ]);
  1125.         }
  1126.         else {
  1127.             metaVertShadeAngles[ tri->indexes[ i ] ] = shadeAngle;
  1128.         }
  1129.     }
  1130.  
  1131.     /* calculate adjastent surfaces area */
  1132.     area = tri->area;
  1133.     for ( i = startTriangle, tri2 = &metaTrianglePlaneTriangles[ startTriangle ]; i < endTriangle; i++, tri2++ ) {
  1134.         /* not same triangle */
  1135.         if ( i == metaTriangleNum ) {
  1136.             continue;
  1137.         }
  1138.  
  1139.         /* compare planes */
  1140.         if ( DotProduct( normal, tri2->plane ) < 0.99f ) {
  1141.             continue;
  1142.         }
  1143.  
  1144.         /* must share at least one vertex */
  1145.         if ( VectorCompareExt( metaVerts[ tri2->indexes[ 0 ] ].xyz, metaVerts[ tri->indexes[ 0 ] ].xyz, 0.1f ) == qtrue ) {
  1146.             goto shared;
  1147.         }
  1148.         if ( VectorCompareExt( metaVerts[ tri2->indexes[ 0 ] ].xyz, metaVerts[ tri->indexes[ 1 ] ].xyz, 0.1f ) == qtrue ) {
  1149.             goto shared;
  1150.         }
  1151.         if ( VectorCompareExt( metaVerts[ tri2->indexes[ 0 ] ].xyz, metaVerts[ tri->indexes[ 2 ] ].xyz, 0.1f ) == qtrue ) {
  1152.             goto shared;
  1153.         }
  1154.         if ( VectorCompareExt( metaVerts[ tri2->indexes[ 1 ] ].xyz, metaVerts[ tri->indexes[ 0 ] ].xyz, 0.1f ) == qtrue ) {
  1155.             goto shared;
  1156.         }
  1157.         if ( VectorCompareExt( metaVerts[ tri2->indexes[ 1 ] ].xyz, metaVerts[ tri->indexes[ 1 ] ].xyz, 0.1f ) == qtrue ) {
  1158.             goto shared;
  1159.         }
  1160.         if ( VectorCompareExt( metaVerts[ tri2->indexes[ 1 ] ].xyz, metaVerts[ tri->indexes[ 2 ] ].xyz, 0.1f ) == qtrue ) {
  1161.             goto shared;
  1162.         }
  1163.         if ( VectorCompareExt( metaVerts[ tri2->indexes[ 2 ] ].xyz, metaVerts[ tri->indexes[ 0 ] ].xyz, 0.1f ) == qtrue ) {
  1164.             goto shared;
  1165.         }
  1166.         if ( VectorCompareExt( metaVerts[ tri2->indexes[ 2 ] ].xyz, metaVerts[ tri->indexes[ 1 ] ].xyz, 0.1f ) == qtrue ) {
  1167.             goto shared;
  1168.         }
  1169.         if ( VectorCompareExt( metaVerts[ tri2->indexes[ 2 ] ].xyz, metaVerts[ tri->indexes[ 2 ] ].xyz, 0.1f ) == qtrue ) {
  1170.             goto shared;
  1171.         }
  1172.         continue;
  1173. shared:
  1174.  
  1175.         /* add area */
  1176.         area += tri2->area;
  1177.     }
  1178.  
  1179.     /* set area */
  1180.     metaVertAreas[ tri->indexes[ 0 ] ] = area;
  1181.     metaVertAreas[ tri->indexes[ 1 ] ] = area;
  1182.     metaVertAreas[ tri->indexes[ 2 ] ] = area;
  1183. }
  1184.  
  1185. /*
  1186. CompareSmoothVerts()
  1187. compare function for qsort (SmoothMetaTriangles)
  1188. */
  1189.  
  1190. static int CompareSmoothVerts( const void *a, const void *b ) {
  1191.     if ( ((smoothVert_t*) a)->distance < ((smoothVert_t*) b)->distance ) {
  1192.         return -1;
  1193.     }
  1194.     if ( ((smoothVert_t*) a)->distance > ((smoothVert_t*) b)->distance ) {
  1195.         return 1;
  1196.     }
  1197.     return 0;
  1198. }
  1199.  
  1200. /*
  1201. SmoothMetaTriangles()
  1202. averages coincident vertex normals in the meta triangles
  1203. */
  1204.  
  1205. void SmoothMetaTriangles( void ) {
  1206.     int i, j, f, fOld, start, vertIndex, numVerts, startVert, endVert, numSmoothed, numSmoothVerts;
  1207.     float shadeAngle, testAngle, vertDist;
  1208.     metaTriangle_t *tri;
  1209.     smoothVert_t *smoothVerts;
  1210.     vec3_t org, normal, average;
  1211.     int indexes[ SMOOTH_MAX_SAMPLES ];
  1212.  
  1213.     /* note it */
  1214.     Sys_FPrintf( SYS_VRB, "--- SmoothMetaTriangles ---\n" );
  1215.  
  1216.     /* init pacifier */
  1217.     fOld = -1;
  1218.     start = I_FloatTime();
  1219.  
  1220.     /* set default shade angle */
  1221.     defaultShadeAngle = DEG2RAD( npDegrees );
  1222.  
  1223.     /* allocate shade angle and area table */
  1224.     metaVertAreas = (float *)safe_malloc( numMetaVerts * sizeof( float ) );
  1225.     metaVertShadeAngles = (float *)safe_malloc( numMetaVerts * sizeof( float ) );
  1226.     memset( metaVertShadeAngles, 0, numMetaVerts * sizeof( float ) );
  1227.     memset( metaVertAreas, 0, numMetaVerts * sizeof( float ) );
  1228.  
  1229.     /* allocate plane-sorted metatriangles */
  1230.     metaTrianglePlaneTriangles = (metaTriangle_t *)safe_malloc( numMetaTriangles * sizeof( metaTriangle_t ) );
  1231.     memcpy( metaTrianglePlaneTriangles, metaTriangles, numMetaTriangles * sizeof( metaTriangle_t ) );
  1232.     qsort( metaTrianglePlaneTriangles, numMetaTriangles, sizeof( metaTriangle_t ), CompareMetaTrianglesPlane );
  1233.     maxShadeAngle = 0;
  1234.  
  1235.     /* find triangle area weights, build optimized smooth verts table */
  1236.     numSmoothVerts = 0;
  1237.     smoothVerts = (smoothVert_t *)safe_malloc( numMetaVerts * sizeof( smoothVert_t ) );
  1238.     RunThreadsOnIndividual( numMetaTriangles, qfalse, MetaTriangleFindAreaWeight );
  1239.     free( metaTrianglePlaneTriangles );
  1240.     for ( i = 0; i < numMetaTriangles; i++ ) {
  1241.         tri = &metaTriangles[ i ];
  1242.         tri->area = metaVertAreas[ tri->indexes[ 0 ] ];
  1243.         if ( metaVertShadeAngles[ tri->indexes[ 0 ] ] < 0 ) {
  1244.             continue;
  1245.         }
  1246.         for ( j = 0; j < 3; j++ ) {
  1247.             smoothVerts[ numSmoothVerts ].metaVert = tri->indexes[ j ];
  1248.             smoothVerts[ numSmoothVerts ].area = tri->area;
  1249.             smoothVerts[ numSmoothVerts++ ].distance = VectorLength( metaVerts[ tri->indexes[ j ] ].xyz);
  1250.         }
  1251.     }
  1252.     free( metaVertAreas );
  1253.    
  1254.     /* bail if no surfaces have a shade angle */
  1255.     if ( numSmoothVerts == 0 ) {
  1256.         free( metaVertShadeAngles );
  1257.         return;
  1258.     }
  1259.  
  1260.     /* sort smoothed weights by their distance */
  1261.     qsort( smoothVerts, numSmoothVerts, sizeof( smoothVert_t ), CompareSmoothVerts );
  1262.  
  1263.     /* go through the list of vertexes */
  1264.     numSmoothed = 0;
  1265.     for ( vertIndex = 0; vertIndex < numSmoothVerts; vertIndex++ ) {
  1266.         /* print pacifier */
  1267.         f = 10 * vertIndex / numSmoothVerts;
  1268.         if ( f != fOld ) {
  1269.             fOld = f;
  1270.             Sys_FPrintf( SYS_VRB, "%d...", f );
  1271.         }
  1272.        
  1273.         /* get vert */
  1274.         i = smoothVerts[ vertIndex ].metaVert;
  1275.         if ( metaVertShadeAngles[ i ] <= 0 ) {
  1276.             continue;
  1277.         }
  1278.  
  1279.         /* find coincident vertexes */
  1280.         vertDist = smoothVerts[ vertIndex ].distance;
  1281.         for (startVert = vertIndex; startVert > 0; startVert--) {
  1282.             if ( ( vertDist - smoothVerts[ startVert ].distance ) > SMOOTH_ORIGIN_EPSILON ) {
  1283.                 break;
  1284.             }
  1285.         }
  1286.         for (endVert = vertIndex + 1; endVert < numSmoothVerts; endVert++) {
  1287.             if ( ( smoothVerts[ endVert ].distance - vertDist ) > SMOOTH_ORIGIN_EPSILON ) {
  1288.                 break;
  1289.             }
  1290.         }
  1291.        
  1292.         /* initiate samples */
  1293.         defaultShadeAngle = metaVertShadeAngles[ i ];
  1294.         VectorCopy( metaVerts[ i ].xyz, org);
  1295.         VectorCopy( metaVerts[ i ].normal, normal);
  1296.         VectorScale( normal, smoothVerts[ vertIndex ].area, average );
  1297.         metaVertShadeAngles[ i ] = -1;
  1298.         indexes[ 0 ] = i;
  1299.         numVerts = 1;
  1300.  
  1301.         /* build a table of coincident vertexes */
  1302.         for ( ; startVert < endVert && numVerts < SMOOTH_MAX_SAMPLES; startVert++ ) {
  1303.             /* get vert */
  1304.             j = smoothVerts[ startVert ].metaVert;
  1305.  
  1306.             /* skip smoothed verts (vortex: but not flat shaded. this will prevent harsh edges between smoother and unsmoothed stuff) */
  1307.             if (metaVertShadeAngles[ j ] < 0 )
  1308.                 continue;
  1309.  
  1310.             /* test origin */
  1311.             if ( VectorCompareExt( org, metaVerts[ j ].xyz, SMOOTH_ORIGIN_EPSILON ) == qfalse ) {
  1312.                 continue;
  1313.             }
  1314.            
  1315.             /* use biggest shade angle */
  1316.             shadeAngle = max(defaultShadeAngle, metaVertShadeAngles[ j ]);
  1317.             if ( shadeAngle <= 0 ) {
  1318.                 continue;
  1319.             }
  1320.            
  1321.             /* test normal */
  1322.             testAngle = acos( DotProduct( normal, metaVerts[ j ].normal ) ) + SMOOTH_THETA_EPSILON;
  1323.             if ( testAngle >= shadeAngle ) {
  1324.                 continue;
  1325.             }
  1326.  
  1327.             /* add to the smooth votes */
  1328.             VectorMA( average, pow(metaTriangles[ j / 3 ].area, 1), metaVerts[ j ].normal, average );
  1329.  
  1330.             /* add to the smooth list */
  1331.             indexes[ numVerts++ ] = j;
  1332.         }
  1333.  
  1334.         /* average normal */
  1335.         if (VectorNormalize(average, average) < 0.1) {
  1336.             for ( j = 0; j < numVerts; j++ ) {
  1337.                 metaVertShadeAngles[ indexes[ j ] ] = -1;
  1338.             }
  1339.         }
  1340.         else {
  1341.             for ( j = 0; j < numVerts; j++ ) {
  1342.                 VectorCopy( average, metaVerts[ indexes[ j ] ].normal );
  1343.                 metaVertShadeAngles[ indexes[ j ] ] = -1;
  1344.             }
  1345.             numSmoothed++;
  1346.         }
  1347.     }
  1348.  
  1349.     /* free the tables */
  1350.     free( metaVertShadeAngles );
  1351.     free( smoothVerts );
  1352.    
  1353.     /* print time */
  1354.     Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
  1355.  
  1356.     /* emit some stats */
  1357.     Sys_FPrintf( SYS_VRB, "%9d smooth vertexes\n", numSmoothVerts );
  1358.     Sys_FPrintf( SYS_VRB, "%9d smooth points\n", numSmoothed );
  1359.     Sys_FPrintf( SYS_VRB, "%7.2f average vertexes per point\n", numSmoothVerts / (float) numSmoothed );
  1360. }
  1361.  
  1362. /*
  1363. ================================================================================
  1364.  
  1365.  Drawsurface Construction
  1366.  
  1367. ================================================================================
  1368. */
  1369.  
  1370. ThreadMutex AddMetaVertToSurfaceMutex     = { qfalse };
  1371. ThreadMutex AddMetaTriangleToSurfaceMutex = { qfalse };
  1372. ThreadMutex MetaTrianglesToSurfaceMutex   = { qfalse };
  1373. ThreadMutex MetaTrianglesToSurfaceMutex2  = { qfalse };
  1374. ThreadMutex MetaTrianglesToSurfaceMutex3  = { qfalse };
  1375.  
  1376. /* AddMetaVertToSurface */
  1377. #define ADD_META_VERT_ORIGIN_EPSILON 0.001f
  1378. #define ADD_META_VERT_COLOR_EPSILON 0.01f
  1379. #define ADD_META_VERT_NORMAL_EPSILON 0.01f
  1380.  
  1381. /* AddMetaTriangleToSurface */
  1382. #define AXIS_SCORE          100000
  1383. #define AXIS_MIN            100000
  1384. #define VERT_SCORE          10000
  1385. #define SURFACE_SCORE       1000
  1386. #define ST_SCORE            50
  1387. #define ST_SCORE2           (2 * (ST_SCORE))
  1388. #define ADEQUATE_SCORE      ((AXIS_MIN) + 1 * (VERT_SCORE))
  1389. #define GOOD_SCORE          ((AXIS_MIN) + 2 * (VERT_SCORE) + 4 * (ST_SCORE))
  1390. #define PERFECT_SCORE       ((AXIS_MIN) + 3 * (VERT_SCORE) + (SURFACE_SCORE) + 4 * (ST_SCORE))
  1391.  
  1392. /* GroupMetaTriangles */
  1393. typedef struct metaTriangleGroup_s
  1394. {
  1395.     int firstTriangle;
  1396.     int numTriangles;
  1397.     int nextTriangle;
  1398. }
  1399. metaTriangleGroup_t;
  1400. int maxMetaTrianglesInGroup;
  1401. int numMetaTriangleGroups;
  1402. metaTriangleGroup_t *metaTriangleGroups;
  1403.  
  1404. qboolean InitMergeMetaTriangles = qtrue;
  1405.  
  1406. /*
  1407. AddMetaVertToSurface()
  1408. adds a drawvert to a surface unless an existing vert matching already exists
  1409. returns the index of that vert (or < 0 on failure)
  1410. */
  1411.  
  1412. int AddMetaVertToSurface( mapDrawSurface_t *ds, bspDrawVert_t *dv1, int *coincident ) {
  1413.     int i;
  1414.     bspDrawVert_t *dv2;
  1415.    
  1416.     /* go through the verts and find a suitable candidate */
  1417.     for ( i = 0; i < ds->numVerts; i++ )
  1418.     {
  1419.         /* get test vert */
  1420.         dv2 = &ds->verts[ i ];
  1421.        
  1422.         /* compare xyz and normal */
  1423.         if ( !VectorCompareExt( dv1->xyz, dv2->xyz, ADD_META_VERT_ORIGIN_EPSILON ) ) {
  1424.             continue;
  1425.         }
  1426.         if ( !VectorCompareExt( dv1->normal, dv2->normal, ADD_META_VERT_NORMAL_EPSILON ) ) {
  1427.             continue;
  1428.         }
  1429.  
  1430.         /* good enough at this point */
  1431.         (*coincident)++;
  1432.  
  1433.         /* compare texture coordinates and color */
  1434.         if ( dv1->st[ 0 ] != dv2->st[ 0 ] || dv1->st[ 1 ] != dv2->st[ 1 ] ) {
  1435.             continue;
  1436.         }
  1437.         if ( fabs((float)(dv1->color[ 0 ][ 3 ] - dv2->color[ 0 ][ 3 ])) > ADD_META_VERT_COLOR_EPSILON ) {
  1438.             continue;
  1439.         }
  1440.        
  1441.         /* found a winner */
  1442.         ThreadMutexLock(&AddMetaVertToSurfaceMutex);
  1443.         numMergedVerts++;
  1444.         ThreadMutexUnlock(&AddMetaVertToSurfaceMutex);
  1445.         return i;
  1446.     }
  1447.  
  1448.     /* overflow check */
  1449.     if ( ds->numVerts >= ((ds->shaderInfo->compileFlags & C_VERTEXLIT) ? maxSurfaceVerts : maxLMSurfaceVerts) ) {
  1450.         return VERTS_EXCEEDED;
  1451.     }
  1452.    
  1453.     /* made it this far, add the vert and return */
  1454.     ThreadMutexLock(&AddMetaVertToSurfaceMutex);
  1455.     dv2 = &ds->verts[ ds->numVerts++ ];
  1456.     *dv2 = *dv1;
  1457.     ThreadMutexUnlock(&AddMetaVertToSurfaceMutex);
  1458.     return (ds->numVerts - 1);
  1459. }
  1460.  
  1461.  
  1462. /*
  1463. AddMetaTriangleToSurface()
  1464. attempts to add a metatriangle to a surface
  1465. returns the score of the triangle added
  1466. */
  1467.  
  1468.  
  1469. static int AddMetaTriangleToSurface( mapDrawSurface_t *ds, metaTriangle_t *tri, qboolean testAdd ) {
  1470.     int i, score, coincident, ai, bi, ci, oldTexRange[ 2 ];
  1471.     float lmMax, lmMax2;
  1472.     vec3_t mins, maxs;
  1473.     qboolean inTexRange, es, et;
  1474.     mapDrawSurface_t old;
  1475.  
  1476.  
  1477.     /* overflow check */
  1478.     if ( ds->numIndexes >= maxSurfaceIndexes ) {
  1479.         return 0;
  1480.     }
  1481.    
  1482.     /* test the triangle */
  1483.     if ( ds->entityNum != tri->entityNum ) {
  1484.         return 0;
  1485.     }
  1486.     if ( ds->castShadows != tri->castShadows || ds->recvShadows != tri->recvShadows ) {
  1487.         return 0;
  1488.     }
  1489.     if ( ds->shaderInfo != tri->si || ds->fogNum != tri->fogNum || ds->sampleSize != tri->sampleSize ) {
  1490.         return 0;
  1491.     }
  1492.     if ( ds->smoothNormals != tri->smoothNormals) {
  1493.         return 0;
  1494.     }
  1495.     if ( ds->minlight[ 0 ] != tri->minlight[ 0 ] || ds->minlight[ 1 ] != tri->minlight[ 1 ] || ds->minlight[ 2 ] != tri->minlight[ 2 ] ) {
  1496.         return 0;
  1497.     }
  1498.     if ( ds->ambient[ 0 ] != tri->ambient[ 0 ] || ds->ambient[ 1 ] != tri->ambient[ 1 ] || ds->ambient[ 2 ] != tri->ambient[ 2 ] ) {
  1499.         return 0;
  1500.     }
  1501.     if ( ds->colormod[ 0 ] != tri->colormod[ 0 ] || ds->colormod[ 1 ] != tri->colormod[ 1 ] || ds->colormod[ 2 ] != tri->colormod[ 2 ] ) {
  1502.         return 0;
  1503.     }
  1504.  
  1505.     /* planar surfaces will only merge with triangles in the same plane */
  1506.     if ( npDegrees <= 0.0f && ds->shaderInfo->nonplanar == qfalse && ds->smoothNormals == 0 && ds->planeNum >= 0 ) {
  1507.         if ( VectorCompare( mapplanes[ ds->planeNum ].normal, tri->plane ) == qfalse || mapplanes[ ds->planeNum ].dist != tri->plane[3] ) {
  1508.             return 0;
  1509.         }
  1510.         if ( tri->planeNum >= 0 && tri->planeNum != ds->planeNum ) {
  1511.             return 0;
  1512.         }
  1513.     }
  1514.    
  1515.     /* set initial score */
  1516.     /* vortex: added broad merge */
  1517.     score = (tri->ds == ds) ? SURFACE_SCORE : 0;
  1518.    
  1519.     /* score the the dot product of lightmap axis to plane */
  1520.     if ( (ds->shaderInfo->compileFlags & C_VERTEXLIT) || VectorCompare( ds->lightmapAxis, tri->lightmapAxis ) ) {
  1521.         score += AXIS_SCORE;
  1522.     }
  1523.     else {
  1524.         score += AXIS_SCORE * DotProduct( ds->lightmapAxis, tri->plane );
  1525.     }
  1526.    
  1527.     /* preserve old drawsurface if this fails */
  1528.     memcpy( &old, ds, sizeof( *ds ) );
  1529.    
  1530.     /* attempt to add the verts */
  1531.     coincident = 0;
  1532.     ai = AddMetaVertToSurface( ds, &metaVerts[ tri->indexes[ 0 ] ], &coincident );
  1533.     bi = AddMetaVertToSurface( ds, &metaVerts[ tri->indexes[ 1 ] ], &coincident );
  1534.     ci = AddMetaVertToSurface( ds, &metaVerts[ tri->indexes[ 2 ] ], &coincident );
  1535.    
  1536.     /* check vertex underflow */
  1537.     if ( ai < 0 || bi < 0 || ci < 0 )   {
  1538.         memcpy( ds, &old, sizeof( *ds ) );
  1539.         return 0;
  1540.     }
  1541.  
  1542.     /* score coincident vertex count (2003-02-14: changed so this only matters on planar surfaces) */
  1543.     score += (coincident * VERT_SCORE);
  1544.    
  1545.     /* add new vertex bounds to mins/maxs */
  1546.     VectorCopy( ds->mins, mins );
  1547.     VectorCopy( ds->maxs, maxs );
  1548.     AddPointToBounds( metaVerts[ tri->indexes[ 0 ] ].xyz, mins, maxs );
  1549.     AddPointToBounds( metaVerts[ tri->indexes[ 1 ] ].xyz, mins, maxs );
  1550.     AddPointToBounds( metaVerts[ tri->indexes[ 2 ] ].xyz, mins, maxs );
  1551.    
  1552.     /* check lightmap bounds overflow (after at least 1 triangle has been added) */
  1553.     if ( !( ds->shaderInfo->compileFlags & C_VERTEXLIT) &&
  1554.          ds->numIndexes > 0 && !VectorIsNull( ds->lightmapAxis ) &&
  1555.          (VectorCompare( ds->mins, mins ) == qfalse || VectorCompare( ds->maxs, maxs ) == qfalse) ) {
  1556.         /* set maximum size before lightmap scaling (normally 2032 units) */
  1557.         /* 2004-02-24: scale lightmap test size by 2 to catch larger brush faces */
  1558.         /* 2004-04-11: reverting to actual lightmap size */
  1559.         /* 2014-12-09: vortex: added lmMaxSurfaceSize */
  1560.         lmMax = (ds->sampleSize * (ds->shaderInfo->lmCustomWidth - 1));
  1561.         lmMax2 = (ds->sampleSize * (lmMaxSurfaceSize - 1));
  1562.         lmMax = min(lmMax, lmMax2);
  1563.         for ( i = 0; i < 3; i++ )
  1564.         {
  1565.             if ( (maxs[ i ] - mins[ i ]) > lmMax )          {
  1566.                 memcpy( ds, &old, sizeof( *ds ) );
  1567.                 return 0;
  1568.             }
  1569.         }
  1570.     }
  1571.    
  1572.     /* check texture range overflow */
  1573.     oldTexRange[ 0 ] = ds->texRange[ 0 ];
  1574.     oldTexRange[ 1 ] = ds->texRange[ 1 ];
  1575.     inTexRange = CalcSurfaceTextureRange( ds );
  1576.     es = (ds->texRange[ 0 ] > oldTexRange[ 0 ]) ? qtrue : qfalse;
  1577.     et = (ds->texRange[ 1 ] > oldTexRange[ 1 ]) ? qtrue : qfalse;
  1578.     if ( inTexRange == qfalse && ds->numIndexes > 0 ) {
  1579.         memcpy( ds, &old, sizeof( *ds ) );
  1580.         return UNSUITABLE_TRIANGLE;
  1581.     }
  1582.    
  1583.     /* score texture range */
  1584.     if ( ds->texRange[ 0 ] <= oldTexRange[ 0 ] ) {
  1585.         score += ST_SCORE2;
  1586.     }
  1587.     else if ( ds->texRange[ 0 ] > oldTexRange[ 0 ] && oldTexRange[ 1 ] > oldTexRange[ 0 ] ) {
  1588.         score += ST_SCORE;
  1589.     }
  1590.  
  1591.     if ( ds->texRange[ 1 ] <= oldTexRange[ 1 ] ) {
  1592.         score += ST_SCORE2;
  1593.     }
  1594.     else if ( ds->texRange[ 1 ] > oldTexRange[ 1 ] && oldTexRange[ 0 ] > oldTexRange[ 1 ] ) {
  1595.         score += ST_SCORE;
  1596.     }
  1597.  
  1598.  
  1599.     /* go through the indexes and try to find an existing triangle that matches abc */
  1600.     for ( i = 0; i < ds->numIndexes; i += 3 )
  1601.     {
  1602.         /* 2002-03-11 (birthday!): rotate the triangle 3x to find an existing triangle */
  1603.         if ( ( ai == ds->indexes[ i ] && bi == ds->indexes[ i + 1 ] && ci == ds->indexes[ i + 2 ] ) ||
  1604.             ( bi == ds->indexes[ i ] && ci == ds->indexes[ i + 1 ] && ai == ds->indexes[ i + 2 ] ) ||
  1605.             ( ci == ds->indexes[ i ] && ai == ds->indexes[ i + 1 ] && bi == ds->indexes[ i + 2 ] ) )    {
  1606.             /* triangle already present */
  1607.             memcpy( ds, &old, sizeof( *ds ) );
  1608.             tri->si = NULL;
  1609.             return 0;
  1610.         }
  1611.        
  1612.         /* rotate the triangle 3x to find an inverse triangle (error case) */
  1613.         if ( (ai == ds->indexes[ i ] && bi == ds->indexes[ i + 2 ] && ci == ds->indexes[ i + 1 ]) ||
  1614.             (bi == ds->indexes[ i ] && ci == ds->indexes[ i + 2 ] && ai == ds->indexes[ i + 1 ]) ||
  1615.             (ci == ds->indexes[ i ] && ai == ds->indexes[ i + 2 ] && bi == ds->indexes[ i + 1 ]) ) {
  1616.             /* warn about it */
  1617.             //Sys_Printf( "WARNING: Flipped triangle: (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f)\n",
  1618.             //  ds->verts[ ai ].xyz[ 0 ], ds->verts[ ai ].xyz[ 1 ], ds->verts[ ai ].xyz[ 2 ],
  1619.             //  ds->verts[ bi ].xyz[ 0 ], ds->verts[ bi ].xyz[ 1 ], ds->verts[ bi ].xyz[ 2 ],
  1620.             //  ds->verts[ ci ].xyz[ 0 ], ds->verts[ ci ].xyz[ 1 ], ds->verts[ ci ].xyz[ 2 ] );
  1621.            
  1622.             /* reverse triangle already present */
  1623.             memcpy( ds, &old, sizeof( *ds ) );
  1624.             tri->si = NULL;
  1625.             return 0;
  1626.         }
  1627.     }
  1628.    
  1629.     /* add the triangle indexes */
  1630.     if ( ds->numIndexes < maxSurfaceIndexes ) {
  1631.         ds->indexes[ ds->numIndexes++ ] = ai;
  1632.     }
  1633.     if ( ds->numIndexes < maxSurfaceIndexes ) {
  1634.         ds->indexes[ ds->numIndexes++ ] = bi;
  1635.     }
  1636.     if ( ds->numIndexes < maxSurfaceIndexes ) {
  1637.         ds->indexes[ ds->numIndexes++ ] = ci;
  1638.     }
  1639.    
  1640.     /* check index overflow */
  1641.     if ( ds->numIndexes >= maxSurfaceIndexes  ) {
  1642.         memcpy( ds, &old, sizeof( *ds ) );
  1643.         return 0;
  1644.     }
  1645.    
  1646.     /* sanity check the indexes */
  1647.     if ( ds->numIndexes >= 3 &&
  1648.         ( ds->indexes[ ds->numIndexes - 3 ] == ds->indexes[ ds->numIndexes - 2 ] ||
  1649.            ds->indexes[ ds->numIndexes - 3 ] == ds->indexes[ ds->numIndexes - 1 ] ||
  1650.            ds->indexes[ ds->numIndexes - 2 ] == ds->indexes[ ds->numIndexes - 1 ]) ) {
  1651.         Sys_Printf( "DEG:%d! ", ds->numVerts );
  1652.         }
  1653.    
  1654.     /* testing only? */
  1655.     if ( testAdd ) {
  1656.         memcpy( ds, &old, sizeof( *ds ) );
  1657.     }
  1658.     else
  1659.     {
  1660.         /* copy bounds back to surface */
  1661.         VectorCopy( mins, ds->mins );
  1662.         VectorCopy( maxs, ds->maxs );
  1663.        
  1664.         /* mark triangle as used */
  1665.         tri->si = NULL;
  1666.     }
  1667.  
  1668.     /* add a side reference */
  1669.     ThreadMutexLock(&AddMetaTriangleToSurfaceMutex);
  1670.     ds->sideRef = AllocSideRef( tri->side, ds->sideRef );
  1671.     ThreadMutexUnlock(&AddMetaTriangleToSurfaceMutex);
  1672.    
  1673.     /* return to sender */
  1674.     return score;
  1675. }
  1676.  
  1677.  
  1678.  
  1679. /*
  1680. MetaTrianglesToSurface()
  1681. creates map drawsurface(s) from the list of possibles
  1682. */
  1683.  
  1684. static void MetaTrianglesToSurface( int numPossibles, metaTriangle_t *possibles, bspDrawVert_t *verts, int *indexes )
  1685. {
  1686.     int                 i, j, best, score, bestScore;
  1687.     metaTriangle_t      *seed, *test;
  1688.     mapDrawSurface_t    *ds;
  1689.     qboolean            added;
  1690.    
  1691.     /* walk the list of triangles */
  1692.     for ( i = 0, seed = possibles; i < numPossibles; i++, seed++ )
  1693.     {
  1694.         /* skip this triangle if it has already been merged */
  1695.         if ( seed->si == NULL ) {
  1696.             continue;
  1697.         }
  1698.        
  1699.         /* -----------------------------------------------------------------
  1700.            initial drawsurf construction
  1701.            ----------------------------------------------------------------- */
  1702.        
  1703.         /* start a new drawsurface */
  1704.         ThreadMutexLock(&MetaTrianglesToSurfaceMutex);
  1705.         ds = AllocDrawSurface( SURFACE_META );
  1706.         ThreadMutexUnlock(&MetaTrianglesToSurfaceMutex);
  1707.  
  1708.         ds->entityNum = seed->entityNum;
  1709.         ds->mapEntityNum = seed->mapEntityNum;
  1710.         ds->castShadows = seed->castShadows;
  1711.         ds->recvShadows = seed->recvShadows;
  1712.  
  1713.         ds->shaderInfo = seed->si;
  1714.         ds->planeNum = seed->planeNum;
  1715.         ds->fogNum = seed->fogNum;
  1716.         ds->sampleSize = seed->sampleSize;
  1717.         VectorCopy( seed->minlight, ds->minlight );
  1718.         VectorCopy( seed->ambient, ds->ambient );
  1719.         VectorCopy( seed->colormod, ds->colormod );
  1720.         ds->smoothNormals = seed->smoothNormals;
  1721.         ds->verts = verts;
  1722.         ds->indexes = indexes;
  1723.         VectorCopy( seed->lightmapAxis, ds->lightmapAxis );
  1724.  
  1725.         ThreadMutexLock(&MetaTrianglesToSurfaceMutex2);
  1726.         ds->sideRef = AllocSideRef( seed->side, NULL );
  1727.         ThreadMutexUnlock(&MetaTrianglesToSurfaceMutex2);
  1728.  
  1729.         ClearBounds( ds->mins, ds->maxs );
  1730.        
  1731.         /* clear verts/indexes */
  1732.         memset( verts, 0, sizeof( verts ) );
  1733.         memset( indexes, 0, sizeof( indexes ) );
  1734.        
  1735.         /* add the first triangle */
  1736.         AddMetaTriangleToSurface( ds, seed, qfalse );
  1737.        
  1738.         /* -----------------------------------------------------------------
  1739.            add triangles
  1740.            ----------------------------------------------------------------- */
  1741.        
  1742.         /* progressively walk the list until no more triangles can be added */
  1743.         added = qtrue;
  1744.         while( added )
  1745.         {
  1746.             /* reset best score */
  1747.             best = -1;
  1748.             bestScore = 0;
  1749.             added = qfalse;
  1750.            
  1751.             /* walk the list of possible candidates for merging */
  1752.             for ( j = i + 1, test = &possibles[ j ]; j < numPossibles; j++, test++ )
  1753.             {
  1754.                 /* skip this triangle if it has already been merged */
  1755.                 if ( test->si == NULL ) {
  1756.                     continue;
  1757.                 }
  1758.                
  1759.                 /* score this triangle */
  1760.                 score = AddMetaTriangleToSurface( ds, test, qtrue );
  1761.                 if ( score > bestScore ) {
  1762.                     best = j;
  1763.                     bestScore = score;
  1764.                    
  1765.                     /* if we have a score over a certain threshold, just use it */
  1766.                     if ( bestScore >= GOOD_SCORE ) {
  1767.                         AddMetaTriangleToSurface( ds, &possibles[ best ], qfalse );
  1768.  
  1769.                         /* reset */
  1770.                         best = -1;
  1771.                         bestScore = 0;
  1772.                         added = qtrue;
  1773.                     }
  1774.                 }
  1775.             }
  1776.            
  1777.             /* add best candidate */
  1778.             if ( best >= 0 && bestScore > ADEQUATE_SCORE ){
  1779.                 AddMetaTriangleToSurface( ds, &possibles[ best ], qfalse );
  1780.             }
  1781.  
  1782.                 /* reset */
  1783.                 added = qtrue;
  1784.             }
  1785.         }
  1786.        
  1787.         /* copy the verts and indexes to the new surface */
  1788.         ds->verts = (bspDrawVert_t *)safe_malloc( ds->numVerts * sizeof( bspDrawVert_t ) );
  1789.         memcpy( ds->verts, verts, ds->numVerts * sizeof( bspDrawVert_t ) );
  1790.         ds->indexes = (int *)safe_malloc( ds->numIndexes * sizeof( int ) );
  1791.         memcpy( ds->indexes, indexes, ds->numIndexes * sizeof( int ) );
  1792.        
  1793.         /* classify the surface */
  1794.         ClassifySurfaces( 1, ds );
  1795.        
  1796.         /* add to count */
  1797.         ThreadMutexLock(&MetaTrianglesToSurfaceMutex3);
  1798.         numMergedSurfaces++;
  1799.         ThreadMutexUnlock(&MetaTrianglesToSurfaceMutex3);
  1800.     }
  1801. }
  1802.  
  1803.  
  1804.  
  1805. /*
  1806. CompareMetaTriangles()
  1807. compare function for qsort()
  1808. */
  1809.  
  1810. static int CompareMetaTriangles( const void *a, const void *b ) {
  1811.     int     i, j, av, bv;
  1812.     vec3_t  aMins, bMins;
  1813.    
  1814.     /* shader first */
  1815.     if ( ((metaTriangle_t*) a)->si < ((metaTriangle_t*) b)->si ) {
  1816.         return 1;
  1817.     }
  1818.     else if ( ((metaTriangle_t*) a)->si > ((metaTriangle_t*) b)->si ) {
  1819.         return -1;
  1820.     }
  1821.    
  1822.     /* then fog */
  1823.     else if ( ((metaTriangle_t*) a)->fogNum < ((metaTriangle_t*) b)->fogNum ) {
  1824.         return 1;
  1825.     }
  1826.     else if ( ((metaTriangle_t*) a)->fogNum > ((metaTriangle_t*) b)->fogNum ) {
  1827.         return -1;
  1828.     }
  1829.  
  1830.     /* then entity num */
  1831.     else if ( ((metaTriangle_t*) a)->entityNum < ((metaTriangle_t*) b)->entityNum ) {
  1832.         return 1;
  1833.     }
  1834.     else if ( ((metaTriangle_t*) a)->entityNum > ((metaTriangle_t*) b)->entityNum ) {
  1835.         return -1;
  1836.     }
  1837.  
  1838.     /* then map entity num */
  1839.     else if ( ((metaTriangle_t*) a)->mapEntityNum < ((metaTriangle_t*) b)->mapEntityNum ) {
  1840.         return 1;
  1841.     }
  1842.     else if ( ((metaTriangle_t*) a)->mapEntityNum > ((metaTriangle_t*) b)->mapEntityNum ) {
  1843.         return -1;
  1844.     }
  1845.  
  1846.     /* then cast shadows */
  1847.     else if ( ((metaTriangle_t*) a)->castShadows < ((metaTriangle_t*) b)->castShadows ) {
  1848.         return 1;
  1849.     }
  1850.     else if ( ((metaTriangle_t*) a)->castShadows > ((metaTriangle_t*) b)->castShadows ) {
  1851.         return -1;
  1852.     }
  1853.  
  1854.     /* then receive shadows */
  1855.     else if ( ((metaTriangle_t*) a)->recvShadows < ((metaTriangle_t*) b)->recvShadows ) {
  1856.         return 1;
  1857.     }
  1858.     else if ( ((metaTriangle_t*) a)->recvShadows > ((metaTriangle_t*) b)->recvShadows ) {
  1859.         return -1;
  1860.     }
  1861.  
  1862.     /* then sample size */
  1863.     else if ( ((metaTriangle_t*) a)->sampleSize < ((metaTriangle_t*) b)->sampleSize ) {
  1864.         return 1;
  1865.     }
  1866.     else if ( ((metaTriangle_t*) a)->sampleSize > ((metaTriangle_t*) b)->sampleSize ) {
  1867.         return -1;
  1868.     }
  1869.  
  1870.     /* then plane */
  1871.     #if 0
  1872.         else if ( npDegrees == 0.0f && ((metaTriangle_t*) a)->si->nonplanar == qfalse &&
  1873.             ((metaTriangle_t*) a)->planeNum >= 0 && ((metaTriangle_t*) a)->planeNum >= 0 ) {
  1874.             if ( ((metaTriangle_t*) a)->plane[ 3 ] < ((metaTriangle_t*) b)->plane[ 3 ] ) {
  1875.                 return 1;
  1876.             }
  1877.             else if ( ((metaTriangle_t*) a)->plane[ 3 ] > ((metaTriangle_t*) b)->plane[ 3 ] ) {
  1878.                 return -1;
  1879.             }
  1880.             else if ( ((metaTriangle_t*) a)->plane[ 0 ] < ((metaTriangle_t*) b)->plane[ 0 ] ) {
  1881.                 return 1;
  1882.             }
  1883.             else if ( ((metaTriangle_t*) a)->plane[ 0 ] > ((metaTriangle_t*) b)->plane[ 0 ] ) {
  1884.                 return -1;
  1885.             }
  1886.             else if ( ((metaTriangle_t*) a)->plane[ 1 ] < ((metaTriangle_t*) b)->plane[ 1 ] ) {
  1887.                 return 1;
  1888.             }
  1889.             else if ( ((metaTriangle_t*) a)->plane[ 1 ] > ((metaTriangle_t*) b)->plane[ 1 ] ) {
  1890.                 return -1;
  1891.             }
  1892.             else if ( ((metaTriangle_t*) a)->plane[ 2 ] < ((metaTriangle_t*) b)->plane[ 2 ] ) {
  1893.                 return 1;
  1894.             }
  1895.             else if ( ((metaTriangle_t*) a)->plane[ 2 ] > ((metaTriangle_t*) b)->plane[ 2 ] ) {
  1896.                 return -1;
  1897.             }
  1898.         }
  1899.     #endif
  1900.    
  1901.     /* then position in world */
  1902.    
  1903.     /* find mins */
  1904.     VectorSet( aMins, 999999, 999999, 999999 );
  1905.     VectorSet( bMins, 999999, 999999, 999999 );
  1906.     for ( i = 0; i < 3; i++ )
  1907.     {
  1908.         av = ((metaTriangle_t*) a)->indexes[ i ];
  1909.         bv = ((metaTriangle_t*) b)->indexes[ i ];
  1910.         for ( j = 0; j < 3; j++ )
  1911.         {
  1912.             if ( metaVerts[ av ].xyz[ j ] < aMins[ j ] ) {
  1913.                 aMins[ j ] = metaVerts[ av ].xyz[ j ];
  1914.             }
  1915.             if ( metaVerts[ bv ].xyz[ j ] < bMins[ j ] ) {
  1916.                 bMins[ j ] = metaVerts[ bv ].xyz[ j ];
  1917.             }
  1918.         }
  1919.     }
  1920.    
  1921.     /* test it */
  1922.     for ( i = 0; i < 3; i++ )
  1923.     {
  1924.         if ( aMins[ i ] < bMins[ i ] ) {
  1925.             return 1;
  1926.         }
  1927.         else if ( aMins[ i ] > bMins[ i ] ) {
  1928.             return -1;
  1929.         }
  1930.     }
  1931.    
  1932.     /* functionally equivalent */
  1933.     return 0;
  1934. }
  1935.  
  1936.  
  1937.  
  1938. /*
  1939.    MergeMetaTriangles()
  1940.    merges meta triangles into drawsurfaces
  1941. */
  1942.  
  1943. void GroupMetaTriangles( void ) {
  1944.     metaTriangle_t      *head, *end;
  1945.     metaTriangleGroup_t *group;
  1946.     int i, j, f, fOld, start;
  1947.  
  1948.     /* note it */
  1949.     Sys_FPrintf( SYS_VRB, "--- GroupMetaTriangles ---\n" );
  1950.  
  1951.     /* init pacifier */
  1952.     fOld = -1;
  1953.     start = I_FloatTime();
  1954.  
  1955.     /* allocate */
  1956.     numMetaTriangleGroups = 0;
  1957.     maxMetaTrianglesInGroup = 0;
  1958.     metaTriangleGroups = (metaTriangleGroup_t *)safe_malloc( sizeof(metaTriangleGroup_t) * numMetaTriangles );
  1959.  
  1960.     /* sort the triangles by shader major, fognum minor */
  1961.     qsort( metaTriangles, numMetaTriangles, sizeof( metaTriangle_t ), CompareMetaTriangles );
  1962.  
  1963.     /* find merge groups */
  1964.     for ( i = 0, j = 0; i < numMetaTriangles; i = j )
  1965.     {
  1966.         /* get head of list */
  1967.         head = &metaTriangles[ i ];
  1968.  
  1969.         /* print pacifier */
  1970.         f = 10 * i / numMetaTriangles;
  1971.         while( fOld < f ) {
  1972.             fOld++;
  1973.             Sys_FPrintf( SYS_VRB, "%d...", fOld );
  1974.         }
  1975.        
  1976.         /* find end */
  1977.         if ( j <= i ) {
  1978.             for ( j = i + 1; j < numMetaTriangles; j++ )
  1979.             {
  1980.                 /* get end of list */
  1981.                 end = &metaTriangles[ j ];
  1982.                 if ( head->si != end->si || head->fogNum != end->fogNum || head->entityNum != end->entityNum || head->mapEntityNum != end->mapEntityNum || head->castShadows != end->castShadows || head->recvShadows != end->recvShadows || head->sampleSize != end->sampleSize )
  1983.                     break;
  1984.             }
  1985.         }
  1986.        
  1987.         /* add group */
  1988.         group = &metaTriangleGroups[numMetaTriangleGroups++];
  1989.         group->firstTriangle = i;
  1990.         group->numTriangles = j - i;
  1991.         group->nextTriangle = group->firstTriangle + group->numTriangles;
  1992.         maxMetaTrianglesInGroup = max(maxMetaTrianglesInGroup, (j - i));
  1993.     }
  1994.  
  1995.     /* pacifier end */
  1996.     if ( i ) {
  1997.         while( fOld < 9 ) {
  1998.             fOld++;
  1999.             Sys_FPrintf( SYS_VRB, "%d...", fOld );
  2000.         }
  2001.         Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
  2002.     }
  2003.  
  2004.     /* emit some stats */
  2005.     Sys_FPrintf( SYS_VRB, "%9d groups\n", numMetaTriangleGroups );
  2006.     Sys_FPrintf( SYS_VRB, "%9d max triangles in group\n", maxMetaTrianglesInGroup );
  2007.  
  2008.     /* print out large groups (helps to optimize shaders) */
  2009.     for ( i = 0; i < numMetaTriangleGroups; i++) {
  2010.         group = &metaTriangleGroups[ i ];
  2011.        
  2012.         if ( group->numTriangles > 500 ) {
  2013.             head = &metaTriangles[ group->firstTriangle ];
  2014.             Sys_FPrintf( SYS_VRB, "          ent#%i %s - %i tris\n", head->entityNum, (head->si != NULL) ? head->si->shader : "noshader", group->numTriangles );
  2015.         }
  2016.     }
  2017. }
  2018.  
  2019. /*
  2020. MergeMetaTrianglesThread()
  2021. thread function for MergeMetaTriangles
  2022. */
  2023.  
  2024. void MergeMetaTrianglesThread( int threadnum )
  2025. {
  2026.     int work, i;
  2027.     metaTriangleGroup_t *group;
  2028.     metaTriangle_t *head;
  2029.     bspDrawVert_t *verts;
  2030.     int *indexes;
  2031.  
  2032.     /* allocate arrays */
  2033.     verts = (bspDrawVert_t *)safe_malloc( sizeof( *verts ) * maxMetaTrianglesInGroup * 3 );
  2034.     indexes = (int *)safe_malloc( sizeof( *indexes ) * maxMetaTrianglesInGroup * 3 );
  2035.  
  2036.     /* cycle */
  2037.     while( (work = GetThreadWork()) >= 0 ) {
  2038.         /* get group */
  2039.         group = &metaTriangleGroups[ work ];
  2040.  
  2041.         /* find out surfaces */
  2042.         for ( i = group->firstTriangle; i < group->nextTriangle; i++ ) {
  2043.             /* get head of list */
  2044.             head = &metaTriangles[ i ];
  2045.            
  2046.             /* skip this triangle if it has already been merged */
  2047.             if ( head->si == NULL ) {
  2048.                 continue
  2049.             };
  2050.  
  2051.             /* try to merge this list of possible merge candidates */
  2052.             MetaTrianglesToSurface( (group->nextTriangle - i), head, verts, indexes );
  2053.         }
  2054.     }
  2055.  
  2056.     /* free arrays */
  2057.     free( verts );
  2058.     free( indexes );
  2059. }
  2060.  
  2061. /*
  2062. MergeMetaTriangles()
  2063. merges meta triangles into drawsurfaces
  2064. */
  2065.  
  2066. void MergeMetaTriangles( void )
  2067. {
  2068.     int numTriangles;
  2069.  
  2070.     /* only do this if there are meta triangles */
  2071.     if ( numMetaTriangles <= 0 ) {
  2072.         return;
  2073.     }
  2074.  
  2075.     /* init mutexes */
  2076.     if ( InitMergeMetaTriangles == qtrue ) {
  2077.         ThreadMutexInit( &AddMetaVertToSurfaceMutex );
  2078.         ThreadMutexInit( &AddMetaTriangleToSurfaceMutex );
  2079.         ThreadMutexInit( &MetaTrianglesToSurfaceMutex );
  2080.         ThreadMutexInit( &MetaTrianglesToSurfaceMutex2 );
  2081.         ThreadMutexInit( &MetaTrianglesToSurfaceMutex3 );
  2082.         InitMergeMetaTriangles = qfalse;
  2083.     }
  2084.  
  2085.     /* group meta triangles */
  2086.     GroupMetaTriangles();
  2087.  
  2088.     /* note it */
  2089.     Sys_FPrintf( SYS_VRB, "--- MergeMetaTriangles ---\n" );
  2090.  
  2091.     /* run threaded */
  2092.     /* vortex: real threaded implemetation is still crashy */
  2093.     if ( numMetaTriangleGroups ) {
  2094.         RunSameThreadOn(numMetaTriangleGroups, verbose, MergeMetaTrianglesThread);
  2095.     }
  2096.  
  2097.     /* clear meta triangle list */
  2098.     numTriangles = numMetaTriangles;
  2099.     ClearMetaTriangles();
  2100.     free( metaTriangleGroups );
  2101.     metaTriangleGroups = NULL;
  2102.  
  2103.     /* emit some stats */
  2104.     Sys_FPrintf( SYS_VRB, "%9d drawsurfaces\n", numMergedSurfaces );
  2105.     Sys_FPrintf( SYS_VRB, "%9d drawverts\n", numMergedVerts );
  2106.     Sys_FPrintf( SYS_VRB, "%9d triangles processed\n", numTriangles );
  2107. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement