]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/surface.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / tools / quake3 / q3map2 / surface.c
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 \r
21 ----------------------------------------------------------------------------------\r
22 \r
23 This code has been altered significantly from its original form, to support\r
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."\r
25 \r
26 ------------------------------------------------------------------------------- */\r
27 \r
28 \r
29 \r
30 /* marker */\r
31 #define SURFACE_C\r
32 \r
33 \r
34 \r
35 /* dependencies */\r
36 #include "q3map2.h"\r
37 \r
38 \r
39 \r
40 /*\r
41 \r
42 this section handles drawsurface allocation and creation\r
43 \r
44 */\r
45 \r
46 /*\r
47 AllocDrawSurface()\r
48 ydnar: gs mods: changed to force an explicit type when allocating\r
49 */\r
50 \r
51 mapDrawSurface_t *AllocDrawSurface( surfaceType_t type )\r
52 {\r
53         mapDrawSurface_t        *ds;\r
54         \r
55         \r
56         /* ydnar: gs mods: only allocate valid types */\r
57         if( type <= SURFACE_BAD || type >= NUM_SURFACE_TYPES )\r
58                 Error( "AllocDrawSurface: Invalid surface type %d specified", type );\r
59         \r
60         /* bounds check */\r
61         if( numMapDrawSurfs >= MAX_MAP_DRAW_SURFS )\r
62                 Error( "MAX_MAP_DRAW_SURFS (%d) exceeded", MAX_MAP_DRAW_SURFS );\r
63         ds = &mapDrawSurfs[ numMapDrawSurfs ];\r
64         numMapDrawSurfs++;\r
65         \r
66         /* ydnar: do initial surface setup */\r
67         memset( ds, 0, sizeof( mapDrawSurface_t ) );\r
68         ds->type = type;\r
69         ds->planeNum = -1;\r
70         ds->fogNum = defaultFogNum;                             /* ydnar 2003-02-12 */\r
71         ds->outputNum = -1;                                             /* ydnar 2002-08-13 */\r
72         ds->surfaceNum = numMapDrawSurfs - 1;   /* ydnar 2003-02-16 */\r
73         \r
74         return ds;\r
75 }\r
76 \r
77 \r
78 \r
79 /*\r
80 FinishSurface()\r
81 ydnar: general surface finish pass\r
82 */\r
83 \r
84 void FinishSurface( mapDrawSurface_t *ds )\r
85 {\r
86         /* dummy check */\r
87         if( ds == NULL || ds->shaderInfo == NULL )\r
88                 return;\r
89         \r
90         /* ydnar: rocking tek-fu celshading */\r
91         if( ds->celShader != NULL )\r
92                 MakeCelSurface( ds, ds->celShader );\r
93         \r
94         /* ydnar: rocking surface cloning (fur baby yeah!) */\r
95         if( ds->shaderInfo->cloneShader[ 0 ] != '\0' )\r
96                 CloneSurface( ds, ShaderInfoForShader( ds->shaderInfo->cloneShader ) );\r
97 }\r
98 \r
99 \r
100 \r
101 /*\r
102 CloneSurface()\r
103 clones a map drawsurface, using the specified shader\r
104 */\r
105 \r
106 mapDrawSurface_t *CloneSurface( mapDrawSurface_t *src, shaderInfo_t *si )\r
107 {\r
108         mapDrawSurface_t        *ds;\r
109         \r
110         \r
111         /* dummy check */\r
112         if( src == NULL || si == NULL )\r
113                 return NULL;\r
114         \r
115         /* allocate a new surface */\r
116         ds = AllocDrawSurface( src->type );\r
117         if( ds == NULL )\r
118                 return NULL;\r
119         \r
120         /* copy it */\r
121         memcpy( ds, src, sizeof( *ds ) );\r
122         \r
123         /* destroy side reference */\r
124         ds->sideRef = NULL;\r
125         \r
126         /* set shader */\r
127         ds->shaderInfo = si;\r
128         \r
129         /* copy verts */\r
130         if( ds->numVerts > 0 )\r
131         {\r
132                 ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );\r
133                 memcpy( ds->verts, src->verts, ds->numVerts * sizeof( *ds->verts ) );\r
134         }\r
135         \r
136         /* copy indexes */\r
137         if( ds->numIndexes <= 0 )\r
138                 return ds;\r
139         ds->indexes = safe_malloc( ds->numIndexes * sizeof( *ds->indexes ) );\r
140         memcpy( ds->indexes, src->indexes, ds->numIndexes * sizeof( *ds->indexes ) );\r
141         \r
142         /* return the surface */\r
143         return ds;\r
144 }\r
145 \r
146 \r
147 \r
148 /*\r
149 MakeCelSurface() - ydnar\r
150 makes a copy of a surface, but specific to cel shading\r
151 */\r
152 \r
153 mapDrawSurface_t *MakeCelSurface( mapDrawSurface_t *src, shaderInfo_t *si )\r
154 {\r
155         mapDrawSurface_t        *ds;\r
156         \r
157         \r
158         /* dummy check */\r
159         if( src == NULL || si == NULL )\r
160                 return NULL;\r
161         \r
162         /* don't create cel surfaces for certain types of shaders */\r
163         if( (src->shaderInfo->compileFlags & C_TRANSLUCENT) ||\r
164                 (src->shaderInfo->compileFlags & C_SKY) )\r
165                 return NULL;\r
166         \r
167         /* make a copy */\r
168         ds = CloneSurface( src, si );\r
169         if( ds == NULL )\r
170                 return NULL;\r
171         \r
172         /* do some fixups for celshading */\r
173         ds->planar = qfalse;\r
174         ds->planeNum = -1;\r
175         \r
176         /* return the surface */\r
177         return ds;\r
178 }\r
179 \r
180 \r
181 \r
182 /*\r
183 MakeSkyboxSurface() - ydnar\r
184 generates a skybox surface, viewable from everywhere there is sky\r
185 */\r
186 \r
187 mapDrawSurface_t *MakeSkyboxSurface( mapDrawSurface_t *src )\r
188 {\r
189         int                                     i;\r
190         mapDrawSurface_t        *ds;\r
191         \r
192         \r
193         /* dummy check */\r
194         if( src == NULL )\r
195                 return NULL;\r
196         \r
197         /* make a copy */\r
198         ds = CloneSurface( src, src->shaderInfo );\r
199         if( ds == NULL )\r
200                 return NULL;\r
201         \r
202         /* set parent */\r
203         ds->parent = src;\r
204         \r
205         /* scale the surface vertexes */\r
206         for( i = 0; i < ds->numVerts; i++ )\r
207         {\r
208                 m4x4_transform_point( skyboxTransform, ds->verts[ i ].xyz );\r
209                 \r
210                 /* debug code */\r
211                 //%     bspDrawVerts[ bspDrawSurfaces[ ds->outputNum ].firstVert + i ].color[ 0 ][ 1 ] = 0;\r
212                 //%     bspDrawVerts[ bspDrawSurfaces[ ds->outputNum ].firstVert + i ].color[ 0 ][ 2 ] = 0;\r
213         }\r
214         \r
215         /* so backface culling creep doesn't bork the surface */\r
216         VectorClear( ds->lightmapVecs[ 2 ] );\r
217         \r
218         /* return the surface */\r
219         return ds;\r
220 }\r
221 \r
222 \r
223 \r
224 /*\r
225 IsTriangleDegenerate\r
226 returns qtrue if all three points are colinear, backwards, or the triangle is just plain bogus\r
227 */\r
228 \r
229 #define TINY_AREA       1.0f\r
230 \r
231 qboolean IsTriangleDegenerate( bspDrawVert_t *points, int a, int b, int c )\r
232 {\r
233         vec3_t          v1, v2, v3;\r
234         float           d;\r
235         \r
236         \r
237         /* calcuate the area of the triangle */\r
238         VectorSubtract( points[ b ].xyz, points[ a ].xyz, v1 );\r
239         VectorSubtract( points[ c ].xyz, points[ a ].xyz, v2 );\r
240         CrossProduct( v1, v2, v3 );\r
241         d = VectorLength( v3 );\r
242         \r
243         /* assume all very small or backwards triangles will cause problems */\r
244         if( d < TINY_AREA )\r
245                 return qtrue;\r
246         \r
247         /* must be a good triangle */\r
248         return qfalse;\r
249 }\r
250 \r
251 \r
252 \r
253 /*\r
254 ClearSurface() - ydnar\r
255 clears a surface and frees any allocated memory\r
256 */\r
257 \r
258 void ClearSurface( mapDrawSurface_t *ds )\r
259 {\r
260         ds->type = SURFACE_BAD;\r
261         ds->planar = qfalse;\r
262         ds->planeNum = -1;\r
263         ds->numVerts = 0;\r
264         if( ds->verts != NULL )\r
265                 free( ds->verts );\r
266         ds->verts = NULL;\r
267         ds->numIndexes = 0;\r
268         if( ds->indexes != NULL )\r
269                 free( ds->indexes );\r
270         ds->indexes = NULL;\r
271         numClearedSurfaces++;\r
272 }\r
273 \r
274 \r
275 \r
276 /*\r
277 TidyEntitySurfaces() - ydnar\r
278 deletes all empty or bad surfaces from the surface list\r
279 */\r
280 \r
281 void TidyEntitySurfaces( entity_t *e )\r
282 {\r
283         int                                     i, j, deleted;\r
284         mapDrawSurface_t        *out, *in;\r
285         \r
286         \r
287         /* note it */\r
288         Sys_FPrintf( SYS_VRB, "--- TidyEntitySurfaces ---\n" );\r
289         \r
290         /* walk the surface list */\r
291         deleted = 0;\r
292         for( i = e->firstDrawSurf, j = e->firstDrawSurf; j < numMapDrawSurfs; i++, j++ )\r
293         {\r
294                 /* get out surface */\r
295                 out = &mapDrawSurfs[ i ];\r
296                 \r
297                 /* walk the surface list again until a proper surface is found */\r
298                 for( j; j < numMapDrawSurfs; j++ )\r
299                 {\r
300                         /* get in surface */\r
301                         in = &mapDrawSurfs[ j ];\r
302                         \r
303                         /* this surface ok? */\r
304                         if( in->type == SURFACE_FLARE || in->type == SURFACE_SHADER ||\r
305                                 (in->type != SURFACE_BAD && in->numVerts > 0) )\r
306                                 break;\r
307                         \r
308                         /* nuke it */\r
309                         ClearSurface( in );\r
310                         deleted++;\r
311                 }\r
312                 \r
313                 /* copy if necessary */\r
314                 if( i != j )\r
315                         memcpy( out, in, sizeof( mapDrawSurface_t ) );\r
316         }\r
317         \r
318         /* set the new number of drawsurfs */\r
319         numMapDrawSurfs = i;\r
320         \r
321         /* emit some stats */\r
322         Sys_FPrintf( SYS_VRB, "%9d empty or malformed surfaces deleted\n", deleted );\r
323 }\r
324 \r
325 \r
326 \r
327 /*\r
328 CalcSurfaceTextureRange() - ydnar\r
329 calculates the clamped texture range for a given surface, returns qtrue if it's within [-texRange,texRange]\r
330 */\r
331 \r
332 qboolean CalcSurfaceTextureRange( mapDrawSurface_t *ds )\r
333 {\r
334         int             i, j, v, size[ 2 ];\r
335         float   mins[ 2 ], maxs[ 2 ];\r
336         \r
337         \r
338         /* try to early out */\r
339         if( ds->numVerts <= 0 )\r
340                 return qtrue;\r
341         \r
342         /* walk the verts and determine min/max st values */\r
343         mins[ 0 ] = 999999;\r
344         mins[ 1 ] = 999999;\r
345         maxs[ 0 ] = -999999;\r
346         maxs[ 1 ] = -999999;\r
347         for( i = 0; i < ds->numVerts; i++ )\r
348         {\r
349                 for( j = 0; j < 2; j++ )\r
350                 {\r
351                         if( ds->verts[ i ].st[ j ] < mins[ j ] )\r
352                                 mins[ j ] = ds->verts[ i ].st[ j ];\r
353                         if( ds->verts[ i ].st[ j ] > maxs[ j ] )\r
354                                 maxs[ j ] = ds->verts[ i ].st[ j ];\r
355                 }\r
356         }\r
357         \r
358         /* clamp to integer range and calculate surface bias values */\r
359         for( j = 0; j < 2; j++ )\r
360                 ds->bias[ j ] = -floor( 0.5f * (mins[ j ] + maxs[ j ]) );\r
361         \r
362         /* find biased texture coordinate mins/maxs */\r
363         size[ 0 ] = ds->shaderInfo->shaderWidth;\r
364         size[ 1 ] = ds->shaderInfo->shaderHeight;\r
365         ds->texMins[ 0 ] = 999999;\r
366         ds->texMins[ 1 ] = 999999;\r
367         ds->texMaxs[ 0 ] = -999999;\r
368         ds->texMaxs[ 1 ] = -999999;\r
369         for( i = 0; i < ds->numVerts; i++ )\r
370         {\r
371                 for( j = 0; j < 2; j++ )\r
372                 {\r
373                         v = ((float) ds->verts[ i ].st[ j ] + ds->bias[ j ]) * size[ j ];\r
374                         if( v < ds->texMins[ j ] )\r
375                                 ds->texMins[ j ] = v;\r
376                         if( v > ds->texMaxs[ j ] )\r
377                                 ds->texMaxs[ j ] = v;\r
378                 }\r
379         }\r
380         \r
381         /* calc ranges */\r
382         for( j = 0; j < 2; j++ )\r
383                 ds->texRange[ j ] = (ds->texMaxs[ j ] - ds->texMins[ j ]);\r
384         \r
385         /* if range is zero, then assume unlimited precision */\r
386         if( texRange == 0 )\r
387                 return qtrue;\r
388         \r
389         /* within range? */\r
390         for( j = 0; j < 2; j++ )\r
391         {\r
392                 if( ds->texMins[ j ] < -texRange || ds->texMaxs[ j ] > texRange )\r
393                         return qfalse;\r
394         }\r
395         \r
396         /* within range */\r
397         return qtrue;\r
398 }\r
399 \r
400 \r
401 \r
402 /*\r
403 CalcLightmapAxis() - ydnar\r
404 gives closed lightmap axis for a plane normal\r
405 */\r
406 \r
407 qboolean CalcLightmapAxis( vec3_t normal, vec3_t axis )\r
408 {\r
409         vec3_t  absolute;\r
410                 \r
411         \r
412         /* test */\r
413         if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && normal[ 2 ] == 0.0f )\r
414         {\r
415                 VectorClear( axis );\r
416                 return qfalse;\r
417         }\r
418         \r
419         /* get absolute normal */\r
420         absolute[ 0 ] = fabs( normal[ 0 ] );\r
421         absolute[ 1 ] = fabs( normal[ 1 ] );\r
422         absolute[ 2 ] = fabs( normal[ 2 ] );\r
423         \r
424         /* test and set */\r
425         if( absolute[ 2 ] > absolute[ 0 ] - 0.0001f && absolute[ 2 ] > absolute[ 1 ] - 0.0001f )\r
426         {\r
427                 if( normal[ 2 ] > 0.0f )\r
428                         VectorSet( axis, 0.0f, 0.0f, 1.0f );\r
429                 else\r
430                         VectorSet( axis, 0.0f, 0.0f, -1.0f );\r
431         }\r
432         else if( absolute[ 0 ] > absolute[ 1 ] - 0.0001f && absolute[ 0 ] > absolute[ 2 ] - 0.0001f )\r
433         {\r
434                 if( normal[ 0 ] > 0.0f )\r
435                         VectorSet( axis, 1.0f, 0.0f, 0.0f );\r
436                 else\r
437                         VectorSet( axis, -1.0f, 0.0f, 0.0f );\r
438         }\r
439         else\r
440         {\r
441                 if( normal[ 1 ] > 0.0f )\r
442                         VectorSet( axis, 0.0f, 1.0f, 0.0f );\r
443                 else\r
444                         VectorSet( axis, 0.0f, -1.0f, 0.0f );\r
445         }\r
446         \r
447         /* return ok */\r
448         return qtrue;\r
449 }\r
450 \r
451 \r
452 \r
453 /*\r
454 ClassifySurfaces() - ydnar\r
455 fills out a bunch of info in the surfaces, including planar status, lightmap projection, and bounding box\r
456 */\r
457 \r
458 #define PLANAR_EPSILON  0.5f    //% 0.126f 0.25f\r
459 \r
460 void ClassifySurfaces( int numSurfs, mapDrawSurface_t *ds )\r
461 {\r
462         int                                     i, bestAxis;\r
463         float                           dist;\r
464         vec4_t                          plane;\r
465         shaderInfo_t            *si;\r
466         static vec3_t           axii[ 6 ] =\r
467                                                 {\r
468                                                         { 0, 0, -1 },\r
469                                                         { 0, 0, 1 },\r
470                                                         { -1, 0, 0 },\r
471                                                         { 1, 0, 0 },\r
472                                                         { 0, -1, 0 },\r
473                                                         { 0, 1, 0 }\r
474                                                 };\r
475         \r
476         \r
477         /* walk the list of surfaces */\r
478         for( numSurfs; numSurfs > 0; numSurfs--, ds++ )\r
479         {\r
480                 /* ignore bogus (or flare) surfaces */\r
481                 if( ds->type == SURFACE_BAD || ds->numVerts <= 0 )\r
482                         continue;\r
483                 \r
484                 /* get shader */\r
485                 si = ds->shaderInfo;\r
486                 \r
487                 /* -----------------------------------------------------------------\r
488                    force meta if vertex count is too high or shader requires it\r
489                    ----------------------------------------------------------------- */\r
490                 \r
491                 if( ds->type != SURFACE_PATCH && ds->type != SURFACE_FACE )\r
492                 {\r
493                         if( ds->numVerts > SHADER_MAX_VERTEXES )\r
494                                 ds->type = SURFACE_FORCED_META;\r
495                 }\r
496                 \r
497                 /* -----------------------------------------------------------------\r
498                    plane and bounding box classification \r
499                    ----------------------------------------------------------------- */\r
500                 \r
501                 /* set surface bounding box */\r
502                 ClearBounds( ds->mins, ds->maxs );\r
503                 for( i = 0; i < ds->numVerts; i++ )\r
504                         AddPointToBounds( ds->verts[ i ].xyz, ds->mins, ds->maxs );\r
505                 \r
506                 /* try to get an existing plane */\r
507                 if( ds->planeNum >= 0 )\r
508                 {\r
509                         VectorCopy( mapplanes[ ds->planeNum ].normal, plane );\r
510                         plane[ 3 ] = mapplanes[ ds->planeNum ].dist;\r
511                 }\r
512                 \r
513                 /* construct one from the first vert with a valid normal */\r
514                 else\r
515                 {\r
516                         VectorClear( plane );\r
517                         plane[ 3 ] = 0.0f;\r
518                         for( i = 0; i < ds->numVerts; i++ )\r
519                         {\r
520                                 if( ds->verts[ i ].normal[ 0 ] != 0.0f && ds->verts[ i ].normal[ 1 ] != 0.0f && ds->verts[ i ].normal[ 2 ] != 0.0f )\r
521                                 {\r
522                                         VectorCopy( ds->verts[ i ].normal, plane );\r
523                                         plane[ 3 ] = DotProduct( ds->verts[ i ].xyz, plane );\r
524                                         break;\r
525                                 }\r
526                         }\r
527                 }\r
528                 \r
529                 /* test for bogus plane */\r
530                 if( VectorLength( plane ) <= 0.0f )\r
531                 {\r
532                         ds->planar = qfalse;\r
533                         ds->planeNum = -1;\r
534                 }\r
535                 else\r
536                 {\r
537                         /* determine if surface is planar */\r
538                         ds->planar = qtrue;\r
539                         \r
540                         /* test each vert */\r
541                         for( i = 0; i < ds->numVerts; i++ )\r
542                         {\r
543                                 /* point-plane test */\r
544                                 dist = DotProduct( ds->verts[ i ].xyz, plane ) - plane[ 3 ];\r
545                                 if( fabs( dist ) > PLANAR_EPSILON )\r
546                                 {\r
547                                         //%     if( ds->planeNum >= 0 )\r
548                                         //%     {\r
549                                         //%             Sys_Printf( "WARNING: Planar surface marked unplanar (%f > %f)\n", fabs( dist ), PLANAR_EPSILON );\r
550                                         //%             ds->verts[ i ].color[ 0 ][ 0 ] = ds->verts[ i ].color[ 0 ][ 2 ] = 0;\r
551                                         //%     }\r
552                                         ds->planar = qfalse;\r
553                                         break;\r
554                                 }\r
555                         }\r
556                 }\r
557                 \r
558                 /* find map plane if necessary */\r
559                 if( ds->planar )\r
560                 {\r
561                         if( ds->planeNum < 0 )\r
562                                 ds->planeNum = FindFloatPlane( plane, plane[ 3 ], 1, &ds->verts[ 0 ].xyz );\r
563                         VectorCopy( plane, ds->lightmapVecs[ 2 ] );\r
564                 }\r
565                 else\r
566                 {\r
567                         ds->planeNum = -1;\r
568                         VectorClear( ds->lightmapVecs[ 2 ] );\r
569                         //% if( ds->type == SURF_META || ds->type == SURF_FACE )\r
570                         //%             Sys_Printf( "WARNING: Non-planar face (%d): %s\n", ds->planeNum, ds->shaderInfo->shader );\r
571                 }\r
572                 \r
573                 /* -----------------------------------------------------------------\r
574                    lightmap bounds and axis projection\r
575                    ----------------------------------------------------------------- */\r
576                 \r
577                 /* vertex lit surfaces don't need this information */\r
578                 if( si->compileFlags & C_VERTEXLIT || ds->type == SURFACE_TRIANGLES )\r
579                 {\r
580                         VectorClear( ds->lightmapAxis );\r
581                         //%     VectorClear( ds->lightmapVecs[ 2 ] );\r
582                         ds->sampleSize = 0;\r
583                         continue;\r
584                 }\r
585                 \r
586                 /* the shader can specify an explicit lightmap axis */\r
587                 if( si->lightmapAxis[ 0 ] || si->lightmapAxis[ 1 ] || si->lightmapAxis[ 2 ] )\r
588                         VectorCopy( si->lightmapAxis, ds->lightmapAxis );\r
589                 else if( ds->type == SURFACE_FORCED_META )\r
590                         VectorClear( ds->lightmapAxis );\r
591                 else if( ds->planar )\r
592                         CalcLightmapAxis( plane, ds->lightmapAxis );\r
593                 else\r
594                 {\r
595                         /* find best lightmap axis */\r
596                         for( bestAxis = 0; bestAxis < 6; bestAxis++ )\r
597                         {\r
598                                 for( i = 0; i < ds->numVerts && bestAxis < 6; i++ )\r
599                                 {\r
600                                         //% Sys_Printf( "Comparing %1.3f %1.3f %1.3f to %1.3f %1.3f %1.3f\n",\r
601                                         //%     ds->verts[ i ].normal[ 0 ], ds->verts[ i ].normal[ 1 ], ds->verts[ i ].normal[ 2 ],\r
602                                         //%     axii[ bestAxis ][ 0 ], axii[ bestAxis ][ 1 ], axii[ bestAxis ][ 2 ] );\r
603                                         if( DotProduct( ds->verts[ i ].normal, axii[ bestAxis ] ) < 0.25f )     /* fixme: adjust this tolerance to taste */\r
604                                                 break;\r
605                                 }\r
606                                 \r
607                                 if( i == ds->numVerts )\r
608                                         break;\r
609                         }\r
610                         \r
611                         /* set axis if possible */\r
612                         if( bestAxis < 6 )\r
613                         {\r
614                                 //% if( ds->type == SURFACE_PATCH )\r
615                                 //%     Sys_Printf( "Mapped axis %d onto patch\n", bestAxis );\r
616                                 VectorCopy( axii[ bestAxis ], ds->lightmapAxis );\r
617                         }\r
618                         \r
619                         /* debug code */\r
620                         //% if( ds->type == SURFACE_PATCH )\r
621                         //%     Sys_Printf( "Failed to map axis %d onto patch\n", bestAxis );\r
622                 }\r
623                 \r
624                 /* get lightmap sample size */\r
625                 if( ds->sampleSize <= 0 )\r
626                 {\r
627                         ds->sampleSize = sampleSize;\r
628                         if( ds->shaderInfo->lightmapSampleSize )\r
629                                 ds->sampleSize = ds->shaderInfo->lightmapSampleSize;\r
630                         if( ds->lightmapScale > 0 )\r
631                                 ds->sampleSize *= ds->lightmapScale;\r
632                         if( ds->sampleSize <= 0 )\r
633                                 ds->sampleSize = 1;\r
634                         else if( ds->sampleSize > 16384 )       /* powers of 2 are preferred */\r
635                                 ds->sampleSize = 16384;\r
636                 }\r
637         }\r
638 }\r
639 \r
640 \r
641 \r
642 /*\r
643 ClassifyEntitySurfaces() - ydnar\r
644 classifies all surfaces in an entity\r
645 */\r
646 \r
647 void ClassifyEntitySurfaces( entity_t *e )\r
648 {\r
649         int             i;\r
650         \r
651         \r
652         /* note it */\r
653         Sys_FPrintf( SYS_VRB, "--- ClassifyEntitySurfaces ---\n" );\r
654         \r
655         /* walk the surface list */\r
656         for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )\r
657                 ClassifySurfaces( 1, &mapDrawSurfs[ i ] );\r
658         \r
659         /* tidy things up */\r
660         TidyEntitySurfaces( e );\r
661 }\r
662 \r
663 \r
664 \r
665 /*\r
666 GetShaderIndexForPoint() - ydnar\r
667 for shader-indexed surfaces (terrain), find a matching index from the indexmap\r
668 */\r
669 \r
670 byte GetShaderIndexForPoint( indexMap_t *im, vec3_t eMins, vec3_t eMaxs, vec3_t point )\r
671 {\r
672         int                     i, x, y;\r
673         float           s, t;\r
674         vec3_t          mins, maxs, size;\r
675         \r
676         \r
677         /* early out if no indexmap */\r
678         if( im == NULL )\r
679                 return 0;\r
680         \r
681         /* this code is really broken */\r
682         #if 0\r
683                 /* legacy precision fudges for terrain */\r
684                 for( i = 0; i < 3; i++ )\r
685                 {\r
686                         mins[ i ] = floor( eMins[ i ] + 0.1 );\r
687                         maxs[ i ] = floor( eMaxs[ i ] + 0.1 );\r
688                         size[ i ] = maxs[ i ] - mins[ i ];\r
689                 }\r
690                 \r
691                 /* find st (fixme: support more than just z-axis projection) */\r
692                 s = floor( point[ 0 ] + 0.1f - mins[ 0 ] ) / size[ 0 ];\r
693                 t = floor( maxs[ 1 ] - point[ 1 ] + 0.1f ) / size[ 1 ];\r
694                 if( s < 0.0f )\r
695                         s = 0.0f;\r
696                 else if( s > 1.0f )\r
697                         s = 1.0f;\r
698                 if( t < 0.0f )\r
699                         t = 0.0f;\r
700                 else if( t > 1.0f )\r
701                         t = 1.0f;\r
702                 \r
703                 /* make xy */\r
704                 x = (im->w - 1) * s;\r
705                 y = (im->h - 1) * t;\r
706         #else\r
707                 /* get size */\r
708                 for( i = 0; i < 3; i++ )\r
709                 {\r
710                         mins[ i ] = eMins[ i ];\r
711                         maxs[ i ] = eMaxs[ i ];\r
712                         size[ i ] = maxs[ i ] - mins[ i ];\r
713                 }\r
714                 \r
715                 /* calc st */\r
716                 s = (point[ 0 ] - mins[ 0 ]) / size[ 0 ];\r
717                 t = (maxs[ 1 ] - point[ 1 ]) / size[ 1 ];\r
718                 \r
719                 /* calc xy */\r
720                 x = s * im->w;\r
721                 y = t * im->h;\r
722                 if( x < 0 )\r
723                         x = 0;\r
724                 else if( x > (im->w - 1) )\r
725                         x = (im->w - 1);\r
726                 if( y < 0 )\r
727                         y = 0;\r
728                 else if( y > (im->h - 1) )\r
729                         y = (im->h - 1);\r
730         #endif\r
731         \r
732         /* return index */\r
733         return im->pixels[ y * im->w + x ];\r
734 }\r
735 \r
736 \r
737 \r
738 /*\r
739 GetIndexedShader() - ydnar\r
740 for a given set of indexes and an indexmap, get a shader and set the vertex alpha in-place\r
741 this combines a couple different functions from terrain.c\r
742 */\r
743 \r
744 shaderInfo_t *GetIndexedShader( shaderInfo_t *parent, indexMap_t *im, int numPoints, byte *shaderIndexes )\r
745 {\r
746         int                             i;\r
747         byte                    minShaderIndex, maxShaderIndex;\r
748         char                    shader[ MAX_QPATH ];\r
749         shaderInfo_t    *si;\r
750         \r
751         \r
752         /* early out if bad data */\r
753         if( im == NULL || numPoints <= 0 || shaderIndexes == NULL )\r
754                 return ShaderInfoForShader( "default" );\r
755         \r
756         /* determine min/max index */\r
757         minShaderIndex = 255;\r
758         maxShaderIndex = 0;\r
759         for( i = 0; i < numPoints; i++ )\r
760         {\r
761                 if( shaderIndexes[ i ] < minShaderIndex )\r
762                         minShaderIndex = shaderIndexes[ i ];\r
763                 if( shaderIndexes[ i ] > maxShaderIndex )\r
764                         maxShaderIndex = shaderIndexes[ i ];\r
765         }\r
766         \r
767         /* set alpha inline */\r
768         for( i = 0; i < numPoints; i++ )\r
769         {\r
770                 /* straight rip from terrain.c */\r
771                 if( shaderIndexes[ i ] < maxShaderIndex )\r
772                         shaderIndexes[ i ] = 0;\r
773                 else\r
774                         shaderIndexes[ i ] = 255;\r
775         }\r
776         \r
777         /* make a shader name */\r
778         if( minShaderIndex == maxShaderIndex )\r
779                 sprintf( shader, "textures/%s_%d", im->shader, maxShaderIndex );\r
780         else\r
781                 sprintf( shader, "textures/%s_%dto%d", im->shader, minShaderIndex, maxShaderIndex );\r
782         \r
783         /* get the shader */\r
784         si = ShaderInfoForShader( shader );\r
785         \r
786         /* inherit a few things from parent shader */\r
787         if( parent->globalTexture )\r
788                 si->globalTexture = qtrue;\r
789         if( parent->forceMeta )\r
790                 si->forceMeta = qtrue;\r
791         if( parent->nonplanar )\r
792                 si->nonplanar = qtrue;\r
793         if( si->shadeAngleDegrees == 0.0 )\r
794                 si->shadeAngleDegrees = parent->shadeAngleDegrees;\r
795         if( parent->tcGen && si->tcGen == qfalse )\r
796         {\r
797                 /* set xy texture projection */\r
798                 si->tcGen = qtrue;\r
799                 VectorCopy( parent->vecs[ 0 ], si->vecs[ 0 ] );\r
800                 VectorCopy( parent->vecs[ 1 ], si->vecs[ 1 ] );\r
801         }\r
802         if( VectorLength( parent->lightmapAxis ) > 0.0f && VectorLength( si->lightmapAxis ) <= 0.0f )\r
803         {\r
804                 /* set lightmap projection axis */\r
805                 VectorCopy( parent->lightmapAxis, si->lightmapAxis );\r
806         }\r
807         \r
808         /* return the shader */\r
809         return si;\r
810 }\r
811 \r
812 \r
813 \r
814 \r
815 /*\r
816 DrawSurfaceForSide()\r
817 creates a SURF_FACE drawsurface from a given brush side and winding\r
818 */\r
819 \r
820 #define SNAP_FLOAT_TO_INT       8\r
821 #define SNAP_INT_TO_FLOAT       (1.0 / SNAP_FLOAT_TO_INT)\r
822 \r
823 mapDrawSurface_t *DrawSurfaceForSide( entity_t *e, brush_t *b, side_t *s, winding_t *w )\r
824 {\r
825         int                                     i, j, k;\r
826         mapDrawSurface_t        *ds;\r
827         shaderInfo_t            *si, *parent;\r
828         bspDrawVert_t           *dv;\r
829         vec3_t                          texX, texY;\r
830         vec_t                           x, y;\r
831         vec3_t                          vTranslated;\r
832         qboolean                        indexed;\r
833         byte                            shaderIndexes[ 256 ];\r
834         float                           offsets[ 256 ];\r
835         char                            tempShader[ MAX_QPATH ];\r
836 \r
837         \r
838         /* ydnar: don't make a drawsurf for culled sides */\r
839         if( s->culled )\r
840                 return NULL;\r
841         \r
842         /* range check */\r
843         if( w->numpoints > MAX_POINTS_ON_WINDING )\r
844                 Error( "DrawSurfaceForSide: w->numpoints = %d (> %d)", w->numpoints, MAX_POINTS_ON_WINDING );\r
845         \r
846         /* get shader */\r
847         si = s->shaderInfo;\r
848         \r
849         /* ydnar: gs mods: check for indexed shader */\r
850         if( si->indexed && b->im != NULL )\r
851         {\r
852                 /* indexed */\r
853                 indexed = qtrue;\r
854                 \r
855                 /* get shader indexes for each point */\r
856                 for( i = 0; i < w->numpoints; i++ )\r
857                 {\r
858                         shaderIndexes[ i ] = GetShaderIndexForPoint( b->im, b->eMins, b->eMaxs, w->p[ i ] );\r
859                         offsets[ i ] = b->im->offsets[ shaderIndexes[ i ] ];\r
860                         //%     Sys_Printf( "%f ", offsets[ i ] );\r
861                 }\r
862                 \r
863                 /* get matching shader and set alpha */\r
864                 parent = si;\r
865                 si = GetIndexedShader( parent, b->im, w->numpoints, shaderIndexes );\r
866         }\r
867         else\r
868                 indexed = qfalse;\r
869         \r
870         /* ydnar: sky hack/fix for GL_CLAMP borders on ati cards */\r
871         if( skyFixHack && si->skyParmsImageBase[ 0 ] != '\0' )\r
872         {\r
873                 //%     Sys_FPrintf( SYS_VRB, "Enabling sky hack for shader %s using env %s\n", si->shader, si->skyParmsImageBase );\r
874                 sprintf( tempShader, "%s_lf", si->skyParmsImageBase );\r
875                 DrawSurfaceForShader( tempShader );\r
876                 sprintf( tempShader, "%s_rt", si->skyParmsImageBase );\r
877                 DrawSurfaceForShader( tempShader );\r
878                 sprintf( tempShader, "%s_ft", si->skyParmsImageBase );\r
879                 DrawSurfaceForShader( tempShader );\r
880                 sprintf( tempShader, "%s_bk", si->skyParmsImageBase );\r
881                 DrawSurfaceForShader( tempShader );\r
882                 sprintf( tempShader, "%s_up", si->skyParmsImageBase );\r
883                 DrawSurfaceForShader( tempShader );\r
884                 sprintf( tempShader, "%s_dn", si->skyParmsImageBase );\r
885                 DrawSurfaceForShader( tempShader );\r
886         }\r
887         \r
888         /* ydnar: gs mods */\r
889         ds = AllocDrawSurface( SURFACE_FACE );\r
890         ds->entityNum = b->entityNum;\r
891         ds->castShadows = b->castShadows;\r
892         ds->recvShadows = b->recvShadows;\r
893         \r
894         ds->planar = qtrue;\r
895         ds->planeNum = s->planenum;\r
896         VectorCopy( mapplanes[ s->planenum ].normal, ds->lightmapVecs[ 2 ] );\r
897         \r
898         ds->shaderInfo = si;\r
899         ds->mapBrush = b;\r
900         ds->sideRef = AllocSideRef( s, NULL );\r
901         ds->fogNum = -1;\r
902         ds->lightmapScale = b->lightmapScale;\r
903         ds->numVerts = w->numpoints;\r
904         ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );\r
905         memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );\r
906         \r
907         /* compute s/t coordinates from brush primitive texture matrix (compute axis base) */\r
908         ComputeAxisBase( mapplanes[ s->planenum ].normal, texX, texY );\r
909         \r
910         /* create the vertexes */\r
911         for( j = 0; j < w->numpoints; j++ )\r
912         {\r
913                 /* get the drawvert */\r
914                 dv = ds->verts + j;\r
915                 \r
916                 /* copy xyz and do potential z offset */\r
917                 VectorCopy( w->p[ j ], dv->xyz );\r
918                 if( indexed )\r
919                         dv->xyz[ 2 ] += offsets[ j ];\r
920                 \r
921                 /* round the xyz to a given precision and translate by origin */\r
922                 for( i = 0 ; i < 3 ; i++ )\r
923                         dv->xyz[ i ] = SNAP_INT_TO_FLOAT * floor( dv->xyz[ i ] * SNAP_FLOAT_TO_INT + 0.5f );\r
924                 VectorAdd( dv->xyz, e->origin, vTranslated );\r
925                 \r
926                 /* ydnar: tek-fu celshading support for flat shaded shit */\r
927                 if( flat )\r
928                 {\r
929                         dv->st[ 0 ] = si->stFlat[ 0 ];\r
930                         dv->st[ 1 ] = si->stFlat[ 1 ];\r
931                 }\r
932                 \r
933                 /* ydnar: gs mods: added support for explicit shader texcoord generation */\r
934                 else if( si->tcGen )\r
935                 {\r
936                         dv->st[ 0 ] = DotProduct( si->vecs[ 0 ], vTranslated );\r
937                         dv->st[ 1 ] = DotProduct( si->vecs[ 1 ], vTranslated );\r
938                 }\r
939                 \r
940                 /* old quake-style texturing */\r
941                 else if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )\r
942                 {\r
943                         /* nearest-axial projection */\r
944                         dv->st[ 0 ] = s->vecs[ 0 ][ 3 ] + DotProduct( s->vecs[ 0 ], vTranslated );\r
945                         dv->st[ 1 ] = s->vecs[ 1 ][ 3 ] + DotProduct( s->vecs[ 1 ], vTranslated );\r
946                         dv->st[ 0 ] /= si->shaderWidth;\r
947                         dv->st[ 1 ] /= si->shaderHeight;\r
948                 }\r
949                 \r
950                 /* brush primitive texturing */\r
951                 else\r
952                 {\r
953                         /* calculate texture s/t from brush primitive texture matrix */\r
954                         x = DotProduct( vTranslated, texX );\r
955                         y = DotProduct( vTranslated, texY );\r
956                         dv->st[ 0 ] = s->texMat[ 0 ][ 0 ] * x + s->texMat[ 0 ][ 1 ] * y + s->texMat[ 0 ][ 2 ];\r
957                         dv->st[ 1 ] = s->texMat[ 1 ][ 0 ] * x + s->texMat[ 1 ][ 1 ] * y + s->texMat[ 1 ][ 2 ];\r
958                 }\r
959                 \r
960                 /* copy normal */\r
961                 VectorCopy( mapplanes[ s->planenum ].normal, dv->normal );\r
962                 \r
963                 /* ydnar: set color */\r
964                 for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
965                 {\r
966                         dv->color[ k ][ 0 ] = 255;\r
967                         dv->color[ k ][ 1 ] = 255;\r
968                         dv->color[ k ][ 2 ] = 255;\r
969                         \r
970                         /* ydnar: gs mods: handle indexed shader blending */\r
971                         dv->color[ k ][ 3 ] = (indexed ? shaderIndexes[ j ] : 255);\r
972                 }\r
973         }\r
974         \r
975         /* set cel shader */\r
976         ds->celShader = b->celShader;\r
977         \r
978         /* finish surface */\r
979         FinishSurface( ds );\r
980         \r
981         /* ydnar: gs mods: moved st biasing elsewhere */\r
982         return ds;\r
983 }\r
984 \r
985 \r
986 \r
987 /*\r
988 DrawSurfaceForMesh()\r
989 moved here from patch.c\r
990 */\r
991 \r
992 #define YDNAR_NORMAL_EPSILON 0.50f\r
993 \r
994 qboolean VectorCompareExt( vec3_t n1, vec3_t n2, float epsilon )\r
995 {\r
996         int             i;\r
997         \r
998         \r
999         /* test */\r
1000         for( i= 0; i < 3; i++ )\r
1001                 if( fabs( n1[ i ] - n2[ i ]) > epsilon )\r
1002                         return qfalse;\r
1003         return qtrue;\r
1004 }\r
1005 \r
1006 mapDrawSurface_t *DrawSurfaceForMesh( entity_t *e, parseMesh_t *p, mesh_t *mesh )\r
1007 {\r
1008         int                                     i, k, numVerts;\r
1009         vec4_t                          plane;\r
1010         qboolean                        planar;\r
1011         float                           dist;\r
1012         mapDrawSurface_t        *ds;\r
1013         shaderInfo_t            *si, *parent;\r
1014         bspDrawVert_t           *dv;\r
1015         vec3_t                          vTranslated;\r
1016         mesh_t                          *copy;\r
1017         qboolean                        indexed;\r
1018         byte                            shaderIndexes[ MAX_EXPANDED_AXIS * MAX_EXPANDED_AXIS ];\r
1019         float                           offsets[ MAX_EXPANDED_AXIS * MAX_EXPANDED_AXIS ];\r
1020         \r
1021         \r
1022         /* get mesh and shader shader */\r
1023         if( mesh == NULL )\r
1024                 mesh = &p->mesh;\r
1025         si = p->shaderInfo;\r
1026         if( mesh == NULL || si == NULL )\r
1027                 return NULL;\r
1028         \r
1029         /* get vertex count */\r
1030         numVerts = mesh->width * mesh->height;\r
1031         \r
1032         /* to make valid normals for patches with degenerate edges,\r
1033            we need to make a copy of the mesh and put the aproximating\r
1034            points onto the curve */\r
1035         \r
1036         /* create a copy of the mesh */\r
1037         copy = CopyMesh( mesh );\r
1038         \r
1039         /* store off the original (potentially bad) normals */\r
1040         MakeMeshNormals( *copy );\r
1041         for( i = 0; i < numVerts; i++ )\r
1042                 VectorCopy( copy->verts[ i ].normal, mesh->verts[ i ].normal );\r
1043         \r
1044         /* put the mesh on the curve */\r
1045         PutMeshOnCurve( *copy );\r
1046 \r
1047         /* find new normals (to take into account degenerate/flipped edges */\r
1048         MakeMeshNormals( *copy );\r
1049         for( i = 0; i < numVerts; i++ )\r
1050         {\r
1051                 /* ydnar: only copy normals that are significantly different from the originals */\r
1052                 if( DotProduct( copy->verts[ i ].normal, mesh->verts[ i ].normal ) < 0.75f )\r
1053                         VectorCopy( copy->verts[ i ].normal, mesh->verts[ i ].normal );\r
1054         }\r
1055         \r
1056         /* free the old mesh */\r
1057         FreeMesh( copy );\r
1058         \r
1059         /* ydnar: gs mods: check for indexed shader */\r
1060         if( si->indexed && p->im != NULL )\r
1061         {\r
1062                 /* indexed */\r
1063                 indexed = qtrue;\r
1064 \r
1065                 /* get shader indexes for each point */\r
1066                 for( i = 0; i < numVerts; i++ )\r
1067                 {\r
1068                         shaderIndexes[ i ] = GetShaderIndexForPoint( p->im, p->eMins, p->eMaxs, mesh->verts[ i ].xyz );\r
1069                         offsets[ i ] = p->im->offsets[ shaderIndexes[ i ] ];\r
1070                 }\r
1071                 \r
1072                 /* get matching shader and set alpha */\r
1073                 parent = si;\r
1074                 si = GetIndexedShader( parent, p->im, numVerts, shaderIndexes );\r
1075         }\r
1076         else\r
1077                 indexed = qfalse;\r
1078         \r
1079         \r
1080         /* ydnar: gs mods */\r
1081         ds = AllocDrawSurface( SURFACE_PATCH );\r
1082         ds->entityNum = p->entityNum;\r
1083         ds->castShadows = p->castShadows;\r
1084         ds->recvShadows = p->recvShadows;\r
1085         \r
1086         ds->shaderInfo = si;\r
1087         ds->mapMesh = p;\r
1088         ds->lightmapScale = p->lightmapScale;   /* ydnar */\r
1089         ds->patchWidth = mesh->width;\r
1090         ds->patchHeight = mesh->height;\r
1091         ds->numVerts = ds->patchWidth * ds->patchHeight;\r
1092         ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );\r
1093         memcpy( ds->verts, mesh->verts, ds->numVerts * sizeof( *ds->verts ) );\r
1094         \r
1095         ds->fogNum = -1;\r
1096         ds->planeNum = -1;\r
1097         \r
1098         ds->longestCurve = p->longestCurve;\r
1099         ds->maxIterations = p->maxIterations;\r
1100         \r
1101         /* construct a plane from the first vert */\r
1102         VectorCopy( mesh->verts[ 0 ].normal, plane );\r
1103         plane[ 3 ] = DotProduct( mesh->verts[ 0 ].xyz, plane );\r
1104         planar = qtrue;\r
1105         \r
1106         /* spew forth errors */\r
1107         if( VectorLength( plane ) < 0.001f )\r
1108                 Sys_Printf( "BOGUS " );\r
1109         \r
1110         /* test each vert */\r
1111         for( i = 1; i < ds->numVerts && planar; i++ )\r
1112         {\r
1113                 /* normal test */\r
1114                 if( VectorCompare( plane, mesh->verts[ i ].normal ) == qfalse )\r
1115                         planar = qfalse;\r
1116                 \r
1117                 /* point-plane test */\r
1118                 dist = DotProduct( mesh->verts[ i ].xyz, plane ) - plane[ 3 ];\r
1119                 if( fabs( dist ) > EQUAL_EPSILON )\r
1120                         planar = qfalse;\r
1121         }\r
1122         \r
1123         /* add a map plane */\r
1124         if( planar )\r
1125         {\r
1126                 /* make a map plane */\r
1127                 ds->planeNum = FindFloatPlane( plane, plane[ 3 ], 1, &mesh->verts[ 0 ].xyz );\r
1128                 VectorCopy( plane, ds->lightmapVecs[ 2 ] );\r
1129                 \r
1130                 /* push this normal to all verts (ydnar 2003-02-14: bad idea, small patches get screwed up) */\r
1131                 for( i = 0; i < ds->numVerts; i++ )\r
1132                         VectorCopy( plane, ds->verts[ i ].normal );\r
1133         }\r
1134         \r
1135         /* walk the verts to do special stuff */\r
1136         for( i = 0; i < ds->numVerts; i++ )\r
1137         {\r
1138                 /* get the drawvert */\r
1139                 dv = &ds->verts[ i ];\r
1140                 \r
1141                 /* ydnar: tek-fu celshading support for flat shaded shit */\r
1142                 if( flat )\r
1143                 {\r
1144                         dv->st[ 0 ] = si->stFlat[ 0 ];\r
1145                         dv->st[ 1 ] = si->stFlat[ 1 ];\r
1146                 }\r
1147                 \r
1148                 /* ydnar: gs mods: added support for explicit shader texcoord generation */\r
1149                 else if( si->tcGen )\r
1150                 {\r
1151                         /* translate by origin and project the texture */\r
1152                         VectorAdd( dv->xyz, e->origin, vTranslated );\r
1153                         dv->st[ 0 ] = DotProduct( si->vecs[ 0 ], vTranslated );\r
1154                         dv->st[ 1 ] = DotProduct( si->vecs[ 1 ], vTranslated );\r
1155                 }\r
1156                 \r
1157                 /* ydnar: set color */\r
1158                 for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
1159                 {\r
1160                         dv->color[ k ][ 0 ] = 255;\r
1161                         dv->color[ k ][ 1 ] = 255;\r
1162                         dv->color[ k ][ 2 ] = 255;\r
1163                         \r
1164                         /* ydnar: gs mods: handle indexed shader blending */\r
1165                         dv->color[ k ][ 3 ] = (indexed ? shaderIndexes[ i ] : 255);\r
1166                 }\r
1167                 \r
1168                 /* ydnar: offset */\r
1169                 if( indexed )\r
1170                         dv->xyz[ 2 ] += offsets[ i ];\r
1171         }\r
1172         \r
1173         /* set cel shader */\r
1174         ds->celShader = p->celShader;\r
1175         \r
1176         /* finish surface */\r
1177         FinishSurface( ds );\r
1178         \r
1179         /* return the drawsurface */\r
1180         return ds;\r
1181 }\r
1182 \r
1183 \r
1184 \r
1185 /*\r
1186 DrawSurfaceForFlare() - ydnar\r
1187 creates a flare draw surface\r
1188 */\r
1189 \r
1190 mapDrawSurface_t *DrawSurfaceForFlare( int entNum, vec3_t origin, vec3_t normal, vec3_t color, char *flareShader, int lightStyle )\r
1191 {\r
1192         mapDrawSurface_t        *ds;\r
1193         \r
1194         \r
1195         /* emit flares? */\r
1196         if( emitFlares == qfalse )\r
1197                 return NULL;\r
1198         \r
1199         /* allocate drawsurface */\r
1200         ds = AllocDrawSurface( SURFACE_FLARE );\r
1201         ds->entityNum = entNum;\r
1202         \r
1203         /* set it up */\r
1204         if( flareShader != NULL && flareShader[ 0 ] != '\0' )\r
1205                 ds->shaderInfo = ShaderInfoForShader( flareShader );\r
1206         else\r
1207                 ds->shaderInfo = ShaderInfoForShader( game->flareShader );\r
1208         if( origin != NULL )\r
1209                 VectorCopy( origin, ds->lightmapOrigin );\r
1210         if( normal != NULL )\r
1211                 VectorCopy( normal, ds->lightmapVecs[ 2 ] );\r
1212         if( color != NULL )\r
1213                 VectorCopy( color, ds->lightmapVecs[ 0 ] );\r
1214         \r
1215         /* store light style */\r
1216         ds->lightStyle = lightStyle;\r
1217         if( ds->lightStyle < 0 || ds->lightStyle >= LS_NONE )\r
1218                 ds->lightStyle = LS_NORMAL;\r
1219         \r
1220         /* fixme: fog */\r
1221         \r
1222         /* return to sender */\r
1223         return ds;\r
1224 }\r
1225 \r
1226 \r
1227 \r
1228 /*\r
1229 DrawSurfaceForShader() - ydnar\r
1230 creates a bogus surface to forcing the game to load a shader\r
1231 */\r
1232 \r
1233 mapDrawSurface_t *DrawSurfaceForShader( char *shader )\r
1234 {\r
1235         int                                     i;\r
1236         shaderInfo_t            *si;\r
1237         mapDrawSurface_t        *ds;\r
1238         \r
1239         \r
1240         /* get shader */\r
1241         si = ShaderInfoForShader( shader );\r
1242 \r
1243         /* find existing surface */\r
1244         for( i = 0; i < numMapDrawSurfs; i++ )\r
1245         {\r
1246                 /* get surface */\r
1247                 ds = &mapDrawSurfs[ i ];\r
1248                 \r
1249                 /* check it */\r
1250                 if( ds->shaderInfo == si )\r
1251                         return ds;\r
1252         }\r
1253         \r
1254         /* create a new surface */\r
1255         ds = AllocDrawSurface( SURFACE_SHADER );\r
1256         ds->entityNum = 0;\r
1257         ds->shaderInfo = ShaderInfoForShader( shader );\r
1258         \r
1259         /* return to sender */\r
1260         return ds;\r
1261 }\r
1262 \r
1263 \r
1264 \r
1265 /*\r
1266 AddSurfaceFlare() - ydnar\r
1267 creates flares (coronas) centered on surfaces\r
1268 */\r
1269 \r
1270 static void AddSurfaceFlare( mapDrawSurface_t *ds, vec3_t entityOrigin )\r
1271 {\r
1272         vec3_t                          origin;\r
1273         int                                     i;\r
1274         \r
1275         \r
1276         /* find centroid */\r
1277         VectorClear( origin );\r
1278         for ( i = 0; i < ds->numVerts; i++ )\r
1279                 VectorAdd( origin, ds->verts[ i ].xyz, origin );\r
1280         VectorScale( origin, (1.0f / ds->numVerts), origin );\r
1281         if( entityOrigin != NULL )\r
1282                 VectorAdd( origin, entityOrigin, origin );\r
1283         \r
1284         /* push origin off surface a bit */\r
1285         VectorMA( origin, 2.0f,  ds->lightmapVecs[ 2 ], origin );\r
1286         \r
1287         /* create the drawsurface */\r
1288         DrawSurfaceForFlare( ds->entityNum, origin, ds->lightmapVecs[ 2 ], ds->shaderInfo->color, ds->shaderInfo->flareShader, ds->shaderInfo->lightStyle );\r
1289 }\r
1290 \r
1291 \r
1292 \r
1293 /*\r
1294 SubdivideFace()\r
1295 subdivides a face surface until it is smaller than the specified size (subdivisions)\r
1296 */\r
1297 \r
1298 static void SubdivideFace( entity_t *e, brush_t *brush, side_t *side, winding_t *w, int fogNum, float subdivisions )\r
1299 {\r
1300         int                                     i;\r
1301         int                                     axis;\r
1302         vec3_t                          bounds[ 2 ];\r
1303         const float                     epsilon = 0.1;\r
1304         int                                     subFloor, subCeil;\r
1305         winding_t                       *frontWinding, *backWinding;\r
1306         mapDrawSurface_t        *ds;\r
1307         \r
1308         \r
1309         /* dummy check */\r
1310         if( w == NULL )\r
1311                 return;\r
1312         if( w->numpoints < 3 )\r
1313                 Error( "SubdivideFaceSurface: Bad w->numpoints" );\r
1314         \r
1315         /* determine surface bounds */\r
1316         ClearBounds( bounds[ 0 ], bounds[ 1 ] );\r
1317         for( i = 0; i < w->numpoints; i++ )\r
1318                 AddPointToBounds( w->p[ i ], bounds[ 0 ], bounds[ 1 ] );\r
1319         \r
1320         /* split the face */\r
1321         for( axis = 0; axis < 3; axis++ )\r
1322         {\r
1323                 vec3_t                  planePoint = { 0, 0, 0 };\r
1324                 vec3_t                  planeNormal = { 0, 0, 0 };\r
1325                 float                   d;\r
1326                 \r
1327                 \r
1328                 /* create an axial clipping plane */\r
1329                 subFloor = floor( bounds[ 0 ][ axis ] / subdivisions) * subdivisions;\r
1330                 subCeil = ceil( bounds[ 1 ][ axis ] / subdivisions) * subdivisions;\r
1331                 planePoint[ axis ] = subFloor + subdivisions;\r
1332                 planeNormal[ axis ] = -1;\r
1333                 d = DotProduct( planePoint, planeNormal );\r
1334 \r
1335                 /* subdivide if necessary */\r
1336                 if( (subCeil - subFloor) > subdivisions )\r
1337                 {\r
1338                         /* clip the winding */\r
1339                         ClipWindingEpsilon( w, planeNormal, d, epsilon, &frontWinding, &backWinding );\r
1340 \r
1341                         /* the clip may not produce two polygons if it was epsilon close */\r
1342                         if( frontWinding == NULL )\r
1343                                 w = backWinding;\r
1344                         else if( backWinding == NULL )\r
1345                                 w = frontWinding;\r
1346                         else\r
1347                         {\r
1348                                 SubdivideFace( e, brush, side, frontWinding, fogNum, subdivisions );\r
1349                                 SubdivideFace( e, brush, side, backWinding, fogNum, subdivisions );\r
1350                                 return;\r
1351                         }\r
1352                 }\r
1353         }\r
1354         \r
1355         /* create a face surface */\r
1356         ds = DrawSurfaceForSide( e, brush, side, w );\r
1357         \r
1358         /* set correct fog num */\r
1359         ds->fogNum = fogNum;\r
1360 }\r
1361 \r
1362 \r
1363 \r
1364 /*\r
1365 SubdivideFaceSurfaces()\r
1366 chop up brush face surfaces that have subdivision attributes\r
1367 ydnar: and subdivide surfaces that exceed specified texture coordinate range\r
1368 */\r
1369 \r
1370 void SubdivideFaceSurfaces( entity_t *e, tree_t *tree )\r
1371 {\r
1372         int                                     i, j, numBaseDrawSurfs, fogNum;\r
1373         mapDrawSurface_t        *ds;\r
1374         brush_t                         *brush;\r
1375         side_t                          *side;\r
1376         shaderInfo_t            *si;\r
1377         winding_t                       *w;\r
1378         float                           range, size, subdivisions, s2;\r
1379         \r
1380         \r
1381         /* note it */\r
1382         Sys_FPrintf( SYS_VRB, "--- SubdivideFaceSurfaces ---\n" );\r
1383         \r
1384         /* walk the list of surfaces */\r
1385         numBaseDrawSurfs = numMapDrawSurfs;\r
1386         for( i = e->firstDrawSurf; i < numBaseDrawSurfs; i++ )\r
1387         {\r
1388                 /* get surface */\r
1389                 ds = &mapDrawSurfs[ i ];\r
1390 \r
1391                 /* only subdivide brush sides */\r
1392                 if( ds->type != SURFACE_FACE || ds->mapBrush == NULL || ds->sideRef == NULL || ds->sideRef->side == NULL )\r
1393                         continue;\r
1394                 \r
1395                 /* get bits */\r
1396                 brush = ds->mapBrush;\r
1397                 side = ds->sideRef->side;\r
1398                 \r
1399                 /* check subdivision for shader */\r
1400                 si = side->shaderInfo;\r
1401                 if( si == NULL )\r
1402                         continue;\r
1403                 \r
1404                 /* ydnar: don't subdivide sky surfaces */\r
1405                 if( si->compileFlags & C_SKY )\r
1406                         continue;\r
1407                 \r
1408                 /* do texture coordinate range check */\r
1409                 ClassifySurfaces( 1, ds );\r
1410                 if( CalcSurfaceTextureRange( ds ) == qfalse )\r
1411                 {\r
1412                         /* calculate subdivisions texture range (this code is shit) */\r
1413                         range = (ds->texRange[ 0 ] > ds->texRange[ 1 ] ? ds->texRange[ 0 ] : ds->texRange[ 1 ]);\r
1414                         size = ds->maxs[ 0 ] - ds->mins[ 0 ];\r
1415                         for( j = 1; j < 3; j++ )\r
1416                                 if( (ds->maxs[ j ] - ds->mins[ j ]) > size )\r
1417                                         size = ds->maxs[ j ] - ds->mins[ j ];\r
1418                         subdivisions = (size / range) * texRange;\r
1419                         subdivisions = ceil( subdivisions / 2 ) * 2;\r
1420                         for( j = 1; j < 8; j++ )\r
1421                         {\r
1422                                 s2 = ceil( (float) texRange / j );\r
1423                                 if( fabs( subdivisions - s2 ) <= 4.0 )\r
1424                                 {\r
1425                                         subdivisions = s2;\r
1426                                         break;\r
1427                                 }\r
1428                         }\r
1429                 }\r
1430                 else\r
1431                         subdivisions = si->subdivisions;\r
1432                 \r
1433                 /* get subdivisions from shader */\r
1434                 if(     si->subdivisions > 0 && si->subdivisions < subdivisions )\r
1435                         subdivisions = si->subdivisions;\r
1436                 if( subdivisions < 1.0f )\r
1437                         continue;\r
1438                 \r
1439                 /* preserve fog num */\r
1440                 fogNum = ds->fogNum;\r
1441                 \r
1442                 /* make a winding and free the surface */\r
1443                 w = WindingFromDrawSurf( ds );\r
1444                 ClearSurface( ds );\r
1445                 \r
1446                 /* subdivide it */\r
1447                 SubdivideFace( e, brush, side, w, fogNum, subdivisions );\r
1448         }\r
1449 }\r
1450 \r
1451 \r
1452 \r
1453 /*\r
1454 ====================\r
1455 ClipSideIntoTree_r\r
1456 \r
1457 Adds non-opaque leaf fragments to the convex hull\r
1458 ====================\r
1459 */\r
1460 \r
1461 void ClipSideIntoTree_r( winding_t *w, side_t *side, node_t *node )\r
1462 {\r
1463         plane_t                 *plane;\r
1464         winding_t               *front, *back;\r
1465 \r
1466         if ( !w ) {\r
1467                 return;\r
1468         }\r
1469 \r
1470         if ( node->planenum != PLANENUM_LEAF ) {\r
1471                 if ( side->planenum == node->planenum ) {\r
1472                         ClipSideIntoTree_r( w, side, node->children[0] );\r
1473                         return;\r
1474                 }\r
1475                 if ( side->planenum == ( node->planenum ^ 1) ) {\r
1476                         ClipSideIntoTree_r( w, side, node->children[1] );\r
1477                         return;\r
1478                 }\r
1479 \r
1480                 plane = &mapplanes[ node->planenum ];\r
1481                 ClipWindingEpsilon ( w, plane->normal, plane->dist,\r
1482                                 ON_EPSILON, &front, &back );\r
1483                 FreeWinding( w );\r
1484 \r
1485                 ClipSideIntoTree_r( front, side, node->children[0] );\r
1486                 ClipSideIntoTree_r( back, side, node->children[1] );\r
1487 \r
1488                 return;\r
1489         }\r
1490 \r
1491         // if opaque leaf, don't add\r
1492         if ( !node->opaque ) {\r
1493                 AddWindingToConvexHull( w, &side->visibleHull, mapplanes[ side->planenum ].normal );\r
1494         }\r
1495 \r
1496         FreeWinding( w );\r
1497         return;\r
1498 }\r
1499 \r
1500 \r
1501 \r
1502 \r
1503 \r
1504 static int g_numHiddenFaces, g_numCoinFaces;\r
1505 \r
1506 \r
1507 \r
1508 /*\r
1509 CullVectorCompare() - ydnar\r
1510 compares two vectors with an epsilon\r
1511 */\r
1512 \r
1513 #define CULL_EPSILON 0.1f\r
1514 \r
1515 qboolean CullVectorCompare( const vec3_t v1, const vec3_t v2 )\r
1516 {\r
1517         int             i;\r
1518         \r
1519         \r
1520         for( i = 0; i < 3; i++ )\r
1521                 if( fabs( v1[ i ] - v2[ i ] ) > CULL_EPSILON )\r
1522                         return qfalse;\r
1523         return qtrue;\r
1524 }\r
1525 \r
1526 \r
1527 \r
1528 /*\r
1529 SideInBrush() - ydnar\r
1530 determines if a brushside lies inside another brush\r
1531 */\r
1532 \r
1533 qboolean SideInBrush( side_t *side, brush_t *b )\r
1534 {\r
1535         int                     i, s;\r
1536         plane_t         *plane;\r
1537         \r
1538         \r
1539         /* ignore sides w/o windings or shaders */\r
1540         if( side->winding == NULL || side->shaderInfo == NULL )\r
1541                 return qtrue;\r
1542 \r
1543         /* ignore culled sides and translucent brushes */\r
1544         if( side->culled == qtrue || (b->compileFlags & C_TRANSLUCENT) )\r
1545                 return qfalse;\r
1546 \r
1547         /* side iterator */\r
1548         for( i = 0; i < b->numsides; i++ )\r
1549         {\r
1550                 /* fail if any sides are caulk */\r
1551                 if( b->sides[ i ].compileFlags & C_NODRAW )\r
1552                         return qfalse;\r
1553 \r
1554                 /* check if side's winding is on or behind the plane */\r
1555                 plane = &mapplanes[ b->sides[ i ].planenum ];\r
1556                 s = WindingOnPlaneSide( side->winding, plane->normal, plane->dist );\r
1557                 if( s == SIDE_FRONT || s == SIDE_CROSS )\r
1558                         return qfalse;\r
1559         }\r
1560         \r
1561         /* don't cull autosprite or polygonoffset surfaces */\r
1562         if( side->shaderInfo )\r
1563         {\r
1564                 if( side->shaderInfo->autosprite || side->shaderInfo->polygonOffset )\r
1565                         return qfalse;\r
1566         }\r
1567         \r
1568         /* inside */\r
1569         side->culled = qtrue;\r
1570         g_numHiddenFaces++;\r
1571         return qtrue;\r
1572 }\r
1573 \r
1574 \r
1575 /*\r
1576 CullSides() - ydnar\r
1577 culls obscured or buried brushsides from the map\r
1578 */\r
1579 \r
1580 void CullSides( entity_t *e )\r
1581 {\r
1582         int                     numPoints;\r
1583         int                     i, j, k, l, first, second, dir;\r
1584         winding_t       *w1, *w2;\r
1585         brush_t *b1, *b2;\r
1586         side_t          *side1, *side2;\r
1587         \r
1588         \r
1589         /* note it */\r
1590         Sys_FPrintf( SYS_VRB, "--- CullSides ---\n" );\r
1591         \r
1592         g_numHiddenFaces = 0;\r
1593         g_numCoinFaces = 0;\r
1594         \r
1595         /* brush interator 1 */\r
1596         for( b1 = e->brushes; b1; b1 = b1->next )\r
1597         {\r
1598                 /* sides check */\r
1599                 if( b1->numsides < 1 )\r
1600                         continue;\r
1601 \r
1602                 /* brush iterator 2 */\r
1603                 for( b2 = b1->next; b2; b2 = b2->next )\r
1604                 {\r
1605                         /* sides check */\r
1606                         if( b2->numsides < 1 )\r
1607                                 continue;\r
1608                         \r
1609                         /* original check */\r
1610                         if( b1->original == b2->original && b1->original != NULL )\r
1611                                 continue;\r
1612                         \r
1613                         /* bbox check */\r
1614                         j = 0;\r
1615                         for( i = 0; i < 3; i++ )\r
1616                                 if( b1->mins[ i ] > b2->maxs[ i ] || b1->maxs[ i ] < b2->mins[ i ] )\r
1617                                         j++;\r
1618                         if( j )\r
1619                                 continue;\r
1620 \r
1621                         /* cull inside sides */\r
1622                         for( i = 0; i < b1->numsides; i++ )\r
1623                                 SideInBrush( &b1->sides[ i ], b2 );\r
1624                         for( i = 0; i < b2->numsides; i++ )\r
1625                                 SideInBrush( &b2->sides[ i ], b1 );\r
1626                         \r
1627                         /* side iterator 1 */\r
1628                         for( i = 0; i < b1->numsides; i++ )\r
1629                         {\r
1630                                 /* winding check */\r
1631                                 side1 = &b1->sides[ i ];\r
1632                                 w1 = side1->winding;\r
1633                                 if( w1 == NULL )\r
1634                                         continue;\r
1635                                 numPoints = w1->numpoints;\r
1636                                 if( side1->shaderInfo == NULL )\r
1637                                         continue;\r
1638                                 \r
1639                                 /* side iterator 2 */\r
1640                                 for( j = 0; j < b2->numsides; j++ )\r
1641                                 {\r
1642                                         /* winding check */\r
1643                                         side2 = &b2->sides[ j ];\r
1644                                         w2 = side2->winding;\r
1645                                         if( w2 == NULL )\r
1646                                                 continue;\r
1647                                         if( side2->shaderInfo == NULL )\r
1648                                                 continue;\r
1649                                         if( w1->numpoints != w2->numpoints )\r
1650                                                 continue;\r
1651                                         if( side1->culled == qtrue && side2->culled == qtrue )\r
1652                                                 continue;\r
1653                                         \r
1654                                         /* compare planes */\r
1655                                         if( (side1->planenum & ~0x00000001) != (side2->planenum & ~0x00000001) )\r
1656                                                 continue;\r
1657                                         \r
1658                                         /* get autosprite and polygonoffset status */\r
1659                                         if( side1->shaderInfo &&\r
1660                                                 (side1->shaderInfo->autosprite || side1->shaderInfo->polygonOffset) )\r
1661                                                 continue;\r
1662                                         if( side2->shaderInfo &&\r
1663                                                 (side2->shaderInfo->autosprite || side2->shaderInfo->polygonOffset) )\r
1664                                                 continue;\r
1665                                         \r
1666                                         /* find first common point */\r
1667                                         first = -1;\r
1668                                         for( k = 0; k < numPoints; k++ )\r
1669                                         {\r
1670                                                 if( VectorCompare( w1->p[ 0 ], w2->p[ k ] ) )\r
1671                                                 {\r
1672                                                         first = k;\r
1673                                                         k = numPoints;\r
1674                                                 }\r
1675                                         }\r
1676                                         if( first == -1 )\r
1677                                                 continue;\r
1678                                         \r
1679                                         /* find second common point (regardless of winding order) */\r
1680                                         second = -1;\r
1681                                         dir = 0;\r
1682                                         if( (first + 1) < numPoints )\r
1683                                                 second = first + 1;\r
1684                                         else\r
1685                                                 second = 0;\r
1686                                         if( CullVectorCompare( w1->p[ 1 ], w2->p[ second ] ) )\r
1687                                                 dir = 1;\r
1688                                         else\r
1689                                         {\r
1690                                                 if( first > 0 )\r
1691                                                         second = first - 1;\r
1692                                                 else\r
1693                                                         second = numPoints - 1;\r
1694                                                 if( CullVectorCompare( w1->p[ 1 ], w2->p[ second ] ) )\r
1695                                                         dir = -1;\r
1696                                         }\r
1697                                         if( dir == 0 )\r
1698                                                 continue;\r
1699                                         \r
1700                                         /* compare the rest of the points */\r
1701                                         l = first;\r
1702                                         for( k = 0; k < numPoints; k++ )\r
1703                                         {\r
1704                                                 if( !CullVectorCompare( w1->p[ k ], w2->p[ l ] ) )\r
1705                                                         k = 100000;\r
1706                                                 \r
1707                                                 l += dir;\r
1708                                                 if( l < 0 )\r
1709                                                         l = numPoints - 1;\r
1710                                                 else if( l >= numPoints )\r
1711                                                         l = 0;\r
1712                                         }\r
1713                                         if( k >= 100000 )\r
1714                                                 continue;\r
1715                                         \r
1716                                         /* cull face 1 */\r
1717                                         if( !side2->culled && !(side2->compileFlags & C_TRANSLUCENT) && !(side2->compileFlags & C_NODRAW) )\r
1718                                         {\r
1719                                                 side1->culled = qtrue;\r
1720                                                 g_numCoinFaces++;\r
1721                                         }\r
1722                                         \r
1723                                         if( side1->planenum == side2->planenum && side1->culled == qtrue )\r
1724                                                 continue;\r
1725                                         \r
1726                                         /* cull face 2 */\r
1727                                         if( !side1->culled && !(side1->compileFlags & C_TRANSLUCENT) && !(side1->compileFlags & C_NODRAW) )\r
1728                                         {\r
1729                                                 side2->culled = qtrue;\r
1730                                                 g_numCoinFaces++;\r
1731                                         }\r
1732                                 }\r
1733                         }\r
1734                 }\r
1735         }\r
1736         \r
1737         /* emit some stats */\r
1738         Sys_FPrintf( SYS_VRB, "%9d hidden faces culled\n", g_numHiddenFaces );\r
1739         Sys_FPrintf( SYS_VRB, "%9d coincident faces culled\n", g_numCoinFaces );\r
1740 }\r
1741 \r
1742 \r
1743 \r
1744 \r
1745 /*\r
1746 ClipSidesIntoTree()\r
1747 \r
1748 creates side->visibleHull for all visible sides\r
1749 \r
1750 the drawsurf for a side will consist of the convex hull of\r
1751 all points in non-opaque clusters, which allows overlaps\r
1752 to be trimmed off automatically.\r
1753 */\r
1754 \r
1755 void ClipSidesIntoTree( entity_t *e, tree_t *tree )\r
1756 {\r
1757         brush_t         *b;\r
1758         int                             i;\r
1759         winding_t               *w;\r
1760         side_t                  *side, *newSide;\r
1761         shaderInfo_t    *si;\r
1762   \r
1763         \r
1764         /* ydnar: cull brush sides */\r
1765         CullSides( e );\r
1766         \r
1767         /* note it */\r
1768         Sys_FPrintf( SYS_VRB, "--- ClipSidesIntoTree ---\n" );\r
1769         \r
1770         /* walk the brush list */\r
1771         for( b = e->brushes; b; b = b->next )\r
1772         {\r
1773                 /* walk the brush sides */\r
1774                 for( i = 0; i < b->numsides; i++ )\r
1775                 {\r
1776                         /* get side */\r
1777                         side = &b->sides[ i ];\r
1778                         if( side->winding == NULL )\r
1779                                 continue;\r
1780                         \r
1781                         /* copy the winding */\r
1782                         w = CopyWinding( side->winding );\r
1783                         side->visibleHull = NULL;\r
1784                         ClipSideIntoTree_r( w, side, tree->headnode );\r
1785                         \r
1786                         /* anything left? */\r
1787                         w = side->visibleHull;\r
1788                         if( w == NULL )\r
1789                                 continue;\r
1790                         \r
1791                         /* shader? */\r
1792                         si = side->shaderInfo;\r
1793                         if( si == NULL )\r
1794                                 continue;\r
1795                         \r
1796                         /* don't create faces for non-visible sides */\r
1797                         /* ydnar: except indexed shaders, like common/terrain and nodraw fog surfaces */\r
1798                         if( (si->compileFlags & C_NODRAW) && si->indexed == qfalse && !(si->compileFlags & C_FOG) )\r
1799                                 continue;\r
1800                         \r
1801                         /* always use the original winding for autosprites and noclip faces */\r
1802                         if( si->autosprite || si->noClip )\r
1803                                 w = side->winding;\r
1804                         \r
1805                         /* save this winding as a visible surface */\r
1806                         DrawSurfaceForSide( e, b, side, w );\r
1807 \r
1808                         /* make a back side for fog */\r
1809                         if( !(si->compileFlags & C_FOG) )\r
1810                                 continue;\r
1811                         \r
1812                         /* duplicate the up-facing side */\r
1813                         w = ReverseWinding( w );\r
1814                         newSide = safe_malloc( sizeof( *side ) );\r
1815                         *newSide = *side;\r
1816                         newSide->visibleHull = w;\r
1817                         newSide->planenum ^= 1;\r
1818                         \r
1819                         /* save this winding as a visible surface */\r
1820                         DrawSurfaceForSide( e, b, newSide, w );\r
1821                 }\r
1822         }\r
1823 }\r
1824 \r
1825 \r
1826 \r
1827 /*\r
1828 \r
1829 this section deals with filtering drawsurfaces into the bsp tree,\r
1830 adding references to each leaf a surface touches\r
1831 \r
1832 */\r
1833 \r
1834 /*\r
1835 AddReferenceToLeaf() - ydnar\r
1836 adds a reference to surface ds in the bsp leaf node\r
1837 */\r
1838 \r
1839 int AddReferenceToLeaf( mapDrawSurface_t *ds, node_t *node )\r
1840 {\r
1841         drawSurfRef_t   *dsr;\r
1842         \r
1843         \r
1844         /* dummy check */\r
1845         if( node->planenum != PLANENUM_LEAF || node->opaque )\r
1846                 return 0;\r
1847         \r
1848         /* try to find an existing reference */\r
1849         for( dsr = node->drawSurfReferences; dsr; dsr = dsr->nextRef )\r
1850         {\r
1851                 if( dsr->outputNum == numBSPDrawSurfaces )\r
1852                         return 0;\r
1853         }\r
1854         \r
1855         /* add a new reference */\r
1856         dsr = safe_malloc( sizeof( *dsr ) );\r
1857         dsr->outputNum = numBSPDrawSurfaces;\r
1858         dsr->nextRef = node->drawSurfReferences;\r
1859         node->drawSurfReferences = dsr;\r
1860         \r
1861         /* ydnar: sky/skybox surfaces */\r
1862         if( node->skybox )\r
1863                 ds->skybox = qtrue;\r
1864         if( ds->shaderInfo->compileFlags & C_SKY )\r
1865                 node->sky = qtrue;\r
1866         \r
1867         /* return */\r
1868         return 1;\r
1869 }\r
1870 \r
1871 \r
1872 \r
1873 /*\r
1874 AddReferenceToTree_r() - ydnar\r
1875 adds a reference to the specified drawsurface to every leaf in the tree\r
1876 */\r
1877 \r
1878 int AddReferenceToTree_r( mapDrawSurface_t *ds, node_t *node, qboolean skybox )\r
1879 {\r
1880         int             i, refs = 0;\r
1881         \r
1882         \r
1883         /* dummy check */\r
1884         if( node == NULL )\r
1885                 return 0;\r
1886         \r
1887         /* is this a decision node? */\r
1888         if( node->planenum != PLANENUM_LEAF )\r
1889         {\r
1890                 /* add to child nodes and return */\r
1891                 refs += AddReferenceToTree_r( ds, node->children[ 0 ], skybox );\r
1892                 refs += AddReferenceToTree_r( ds, node->children[ 1 ], skybox );\r
1893                 return refs;\r
1894         }\r
1895         \r
1896         /* ydnar */\r
1897         if( skybox )\r
1898         {\r
1899                 /* skybox surfaces only get added to sky leaves */\r
1900                 if( !node->sky )\r
1901                         return 0;\r
1902                 \r
1903                 /* increase the leaf bounds */\r
1904                 for( i = 0; i < ds->numVerts; i++ )\r
1905                         AddPointToBounds( ds->verts[ i ].xyz, node->mins, node->maxs );\r
1906         }\r
1907         \r
1908         /* add a reference */\r
1909         return AddReferenceToLeaf( ds, node );\r
1910 }\r
1911 \r
1912 \r
1913 \r
1914 /*\r
1915 FilterPointIntoTree_r() - ydnar\r
1916 filters a single point from a surface into the tree\r
1917 */\r
1918 \r
1919 int FilterPointIntoTree_r( vec3_t point, mapDrawSurface_t *ds, node_t *node )\r
1920 {\r
1921         float                   d;\r
1922         plane_t                 *plane;\r
1923         int                             refs = 0;\r
1924         \r
1925         \r
1926         /* is this a decision node? */\r
1927         if( node->planenum != PLANENUM_LEAF )\r
1928         {\r
1929                 /* classify the point in relation to the plane */\r
1930                 plane = &mapplanes[ node->planenum ];\r
1931                 d = DotProduct( point, plane->normal ) - plane->dist;\r
1932                 \r
1933                 /* filter by this plane */\r
1934                 refs = 0;\r
1935                 if( d >= -ON_EPSILON )\r
1936                         refs += FilterPointIntoTree_r( point, ds, node->children[ 0 ] );\r
1937                 if( d <= ON_EPSILON )\r
1938                         refs += FilterPointIntoTree_r( point, ds, node->children[ 1 ] );\r
1939                 \r
1940                 /* return */\r
1941                 return refs;\r
1942         }\r
1943         \r
1944         /* add a reference */\r
1945         return AddReferenceToLeaf( ds, node );\r
1946 }\r
1947 \r
1948 \r
1949 \r
1950 /*\r
1951 FilterWindingIntoTree_r() - ydnar\r
1952 filters a winding from a drawsurface into the tree\r
1953 */\r
1954 \r
1955 int FilterWindingIntoTree_r( winding_t *w, mapDrawSurface_t *ds, node_t *node )\r
1956 {\r
1957         int                             i, refs = 0;\r
1958         plane_t                 *p1, *p2;\r
1959         vec4_t                  plane1, plane2, reverse;\r
1960         winding_t               *fat, *front, *back;\r
1961         shaderInfo_t    *si;\r
1962         \r
1963         \r
1964         /* get shaderinfo */\r
1965         si = ds->shaderInfo;\r
1966         \r
1967         /* ydnar: is this the head node? */\r
1968         if( node->parent == NULL && si != NULL &&\r
1969                 (si->mins[ 0 ] != 0.0f || si->maxs[ 0 ] != 0.0f ||\r
1970                 si->mins[ 1 ] != 0.0f || si->maxs[ 1 ] != 0.0f ||\r
1971                 si->mins[ 2 ] != 0.0f || si->maxs[ 2 ] != 0.0f) )\r
1972         {\r
1973                 /* 'fatten' the winding by the shader mins/maxs (parsed from vertexDeform move) */\r
1974                 /* note this winding is completely invalid (concave, nonplanar, etc) */\r
1975                 fat = AllocWinding( w->numpoints * 3 );\r
1976                 fat->numpoints = w->numpoints * 3;\r
1977                 for( i = 0; i < w->numpoints; i++ )\r
1978                 {\r
1979                         VectorCopy( w->p[ i ], fat->p[ i ] );\r
1980                         VectorAdd( w->p[ i ], si->mins, fat->p[ i * 2 ] );\r
1981                         VectorAdd( w->p[ i ], si->maxs, fat->p[ i * 3 ] );\r
1982                 }\r
1983                 \r
1984                 FreeWinding( w );\r
1985                 w = fat;\r
1986         }\r
1987         \r
1988         /* is this a decision node? */\r
1989         if( node->planenum != PLANENUM_LEAF )\r
1990         {       \r
1991                 /* get node plane */\r
1992                 p1 = &mapplanes[ node->planenum ];\r
1993                 VectorCopy( p1->normal, plane1 );\r
1994                 plane1[ 3 ] = p1->dist;\r
1995                 \r
1996                 /* check if surface is planar */\r
1997                 if( ds->planeNum >= 0 )\r
1998                 {\r
1999                         /* get surface plane */\r
2000                         p2 = &mapplanes[ ds->planeNum ];\r
2001                         VectorCopy( p2->normal, plane2 );\r
2002                         plane2[ 3 ] = p2->dist;\r
2003                         \r
2004                         #if 1\r
2005                                 /* invert surface plane */\r
2006                                 VectorSubtract( vec3_origin, plane2, reverse );\r
2007                                 reverse[ 3 ] = -plane2[ 3 ];\r
2008                                 \r
2009                                 /* compare planes */\r
2010                                 if( DotProduct( plane1, plane2 ) > 0.999f && fabs( plane1[ 3 ] - plane2[ 3 ] ) < 0.001f )\r
2011                                         return FilterWindingIntoTree_r( w, ds, node->children[ 0 ] );\r
2012                                 if( DotProduct( plane1, reverse ) > 0.999f && fabs( plane1[ 3 ] - reverse[ 3 ] ) < 0.001f )\r
2013                                         return FilterWindingIntoTree_r( w, ds, node->children[ 1 ] );\r
2014                         #else\r
2015                                 /* the drawsurf might have an associated plane, if so, force a filter here */\r
2016                                 if( ds->planeNum == node->planenum )\r
2017                                         return FilterWindingIntoTree_r( w, ds, node->children[ 0 ] );\r
2018                                 if( ds->planeNum == (node->planenum ^ 1) )\r
2019                                         return FilterWindingIntoTree_r( w, ds, node->children[ 1 ] );\r
2020                         #endif\r
2021                 }\r
2022                 \r
2023                 /* clip the winding by this plane */\r
2024                 ClipWindingEpsilon( w, plane1, plane1[ 3 ], ON_EPSILON, &front, &back );\r
2025                 \r
2026                 /* filter by this plane */\r
2027                 refs = 0;\r
2028                 if( front != NULL )\r
2029                         refs += FilterWindingIntoTree_r( front, ds, node->children[ 0 ] );\r
2030                 if( back != NULL )\r
2031                         refs += FilterWindingIntoTree_r( back, ds, node->children[ 1 ] );\r
2032                 FreeWinding( w );\r
2033                 \r
2034                 /* return */\r
2035                 return refs;\r
2036         }\r
2037         \r
2038         /* add a reference */\r
2039         return AddReferenceToLeaf( ds, node );\r
2040 }\r
2041 \r
2042 \r
2043 \r
2044 /*\r
2045 FilterFaceIntoTree()\r
2046 filters a planar winding face drawsurface into the bsp tree\r
2047 */\r
2048 \r
2049 int     FilterFaceIntoTree( mapDrawSurface_t *ds, tree_t *tree )\r
2050 {\r
2051         winding_t       *w;\r
2052         int                     refs = 0;\r
2053         \r
2054         \r
2055         /* make a winding and filter it into the tree */\r
2056         w = WindingFromDrawSurf( ds );\r
2057         refs = FilterWindingIntoTree_r( w, ds, tree->headnode );\r
2058         \r
2059         /* return */\r
2060         return refs;\r
2061 }\r
2062 \r
2063 \r
2064 \r
2065 /*\r
2066 FilterPatchIntoTree()\r
2067 subdivides a patch into an approximate curve and filters it into the tree\r
2068 */\r
2069 \r
2070 #define FILTER_SUBDIVISION              8\r
2071 \r
2072 static int FilterPatchIntoTree( mapDrawSurface_t *ds, tree_t *tree )\r
2073 {\r
2074         int                                     i, x, y, refs;\r
2075         mesh_t                          src, *mesh;\r
2076         winding_t                       *w;\r
2077         \r
2078         \r
2079         /* subdivide the surface */\r
2080         src.width = ds->patchWidth;\r
2081         src.height = ds->patchHeight;\r
2082         src.verts = ds->verts;\r
2083         mesh = SubdivideMesh( src, FILTER_SUBDIVISION, 32 );\r
2084         \r
2085         \r
2086         /* filter each quad into the tree (fixme: use new patch x-triangulation code?) */\r
2087         refs = 0;\r
2088         for( y = 0; y < (mesh->height - 1); y++ )\r
2089         {\r
2090                 for( x = 0; x < (mesh->width - 1); x++ )\r
2091                 {\r
2092                         /* triangle 1 */\r
2093                         w = AllocWinding( 3 );\r
2094                         w->numpoints = 3;\r
2095                         VectorCopy( mesh->verts[ y * mesh->width + x ].xyz, w->p[ 0 ] );\r
2096                         VectorCopy( mesh->verts[ y * mesh->width + x + 1 ].xyz, w->p[ 1 ] );\r
2097                         VectorCopy( mesh->verts[ (y + 1) * mesh->width + x ].xyz, w->p[ 2 ] );\r
2098                         refs += FilterWindingIntoTree_r( w, ds, tree->headnode );\r
2099                         \r
2100                         /* triangle 2 */\r
2101                         w = AllocWinding( 3 );\r
2102                         w->numpoints = 3;\r
2103                         VectorCopy( mesh->verts[ y * mesh->width + x + 1 ].xyz, w->p[ 0 ] );\r
2104                         VectorCopy( mesh->verts[ (y + 1 ) * mesh->width + x + 1 ].xyz, w->p[ 1 ] );\r
2105                         VectorCopy( mesh->verts[ (y + 1 ) * mesh->width + x ].xyz, w->p[ 2 ] );\r
2106                         refs += FilterWindingIntoTree_r( w, ds, tree->headnode );\r
2107                 }\r
2108         }\r
2109         \r
2110         /* use point filtering as well */\r
2111         for( i = 0; i < (mesh->width * mesh->height); i++ )\r
2112                 refs += FilterPointIntoTree_r( mesh->verts[ i ].xyz, ds, tree->headnode );\r
2113         \r
2114         /* free the subdivided mesh and return */\r
2115         FreeMesh( mesh );\r
2116         return refs;\r
2117 }\r
2118 \r
2119 \r
2120 \r
2121 /*\r
2122 FilterTrianglesIntoTree()\r
2123 filters a triangle surface (meta, model) into the bsp\r
2124 */\r
2125 \r
2126 static int FilterTrianglesIntoTree( mapDrawSurface_t *ds, tree_t *tree )\r
2127 {\r
2128         int                     i, refs;\r
2129         winding_t       *w;\r
2130         \r
2131         \r
2132         /* ydnar: gs mods: this was creating bogus triangles before */\r
2133         refs = 0;\r
2134         for( i = 0; i < ds->numIndexes; i += 3 )\r
2135         {\r
2136                 /* error check */\r
2137                 if( ds->indexes[ i ] >= ds->numVerts ||\r
2138                         ds->indexes[ i + 1 ] >= ds->numVerts ||\r
2139                         ds->indexes[ i + 2 ] >= ds->numVerts )\r
2140                         Error( "Index %d greater than vertex count %d", ds->indexes[ i ], ds->numVerts );\r
2141                 \r
2142                 /* make a triangle winding and filter it into the tree */\r
2143                 w = AllocWinding( 3 );\r
2144                 w->numpoints = 3;\r
2145                 VectorCopy( ds->verts[ ds->indexes[ i ] ].xyz, w->p[ 0 ] );\r
2146                 VectorCopy( ds->verts[ ds->indexes[ i + 1 ] ].xyz, w->p[ 1 ] );\r
2147                 VectorCopy( ds->verts[ ds->indexes[ i + 2 ] ].xyz, w->p[ 2 ] );\r
2148                 refs += FilterWindingIntoTree_r( w, ds, tree->headnode );\r
2149         }\r
2150         \r
2151         /* use point filtering as well */\r
2152         for( i = 0; i < ds->numVerts; i++ )\r
2153                 refs += FilterPointIntoTree_r( ds->verts[ i ].xyz, ds, tree->headnode );\r
2154 \r
2155         return refs;\r
2156 }\r
2157 \r
2158 \r
2159 \r
2160 /*\r
2161 FilterFoliageIntoTree()\r
2162 filters a foliage surface (wolf et/splash damage)\r
2163 */\r
2164 \r
2165 static int FilterFoliageIntoTree( mapDrawSurface_t *ds, tree_t *tree )\r
2166 {\r
2167         int                             f, i, refs;\r
2168         bspDrawVert_t   *instance;\r
2169         vec3_t                  xyz;\r
2170         winding_t               *w;\r
2171         \r
2172         \r
2173         /* walk origin list */\r
2174         refs = 0;\r
2175         for( f = 0; f < ds->numFoliageInstances; f++ )\r
2176         {\r
2177                 /* get instance */\r
2178                 instance = ds->verts + ds->patchHeight + f;\r
2179                 \r
2180                 /* walk triangle list */\r
2181                 for( i = 0; i < ds->numIndexes; i += 3 )\r
2182                 {\r
2183                         /* error check */\r
2184                         if( ds->indexes[ i ] >= ds->numVerts ||\r
2185                                 ds->indexes[ i + 1 ] >= ds->numVerts ||\r
2186                                 ds->indexes[ i + 2 ] >= ds->numVerts )\r
2187                                 Error( "Index %d greater than vertex count %d", ds->indexes[ i ], ds->numVerts );\r
2188                         \r
2189                         /* make a triangle winding and filter it into the tree */\r
2190                         w = AllocWinding( 3 );\r
2191                         w->numpoints = 3;\r
2192                         VectorAdd( instance->xyz, ds->verts[ ds->indexes[ i ] ].xyz, w->p[ 0 ] );\r
2193                         VectorAdd( instance->xyz, ds->verts[ ds->indexes[ i + 1 ] ].xyz, w->p[ 1 ] );\r
2194                         VectorAdd( instance->xyz, ds->verts[ ds->indexes[ i + 2 ] ].xyz, w->p[ 2 ] );\r
2195                         refs += FilterWindingIntoTree_r( w, ds, tree->headnode );\r
2196                 }\r
2197                 \r
2198                 /* use point filtering as well */\r
2199                 for( i = 0; i < (ds->numVerts - ds->numFoliageInstances); i++ )\r
2200                 {\r
2201                         VectorAdd( instance->xyz, ds->verts[ i ].xyz, xyz );\r
2202                         refs += FilterPointIntoTree_r( xyz, ds, tree->headnode );\r
2203                 }\r
2204         }\r
2205         \r
2206         return refs;\r
2207 }\r
2208 \r
2209 \r
2210 \r
2211 /*\r
2212 FilterFlareIntoTree()\r
2213 simple point filtering for flare surfaces\r
2214 */\r
2215 static int FilterFlareSurfIntoTree( mapDrawSurface_t *ds, tree_t *tree )\r
2216 {\r
2217         return FilterPointIntoTree_r( ds->lightmapOrigin, ds, tree->headnode );\r
2218 }\r
2219 \r
2220 \r
2221 \r
2222 /*\r
2223 EmitDrawVerts() - ydnar\r
2224 emits bsp drawverts from a map drawsurface\r
2225 */\r
2226 \r
2227 void EmitDrawVerts( mapDrawSurface_t *ds, bspDrawSurface_t *out )\r
2228 {\r
2229         int                             i, k;\r
2230         bspDrawVert_t   *dv;\r
2231         shaderInfo_t    *si;\r
2232         float                   offset;\r
2233         \r
2234         \r
2235         /* get stuff */\r
2236         si = ds->shaderInfo;\r
2237         offset = si->offset;\r
2238         \r
2239         /* copy the verts */\r
2240         out->firstVert = numBSPDrawVerts;\r
2241         out->numVerts = ds->numVerts;\r
2242         for( i = 0; i < ds->numVerts; i++ )\r
2243         {\r
2244                 /* allocate a new vert */\r
2245                 if( numBSPDrawVerts == MAX_MAP_DRAW_VERTS )\r
2246                         Error( "MAX_MAP_DRAW_VERTS" );\r
2247                 IncDrawVerts();\r
2248                 dv = &bspDrawVerts[ numBSPDrawVerts - 1 ];\r
2249                 \r
2250                 /* copy it */\r
2251                 memcpy( dv, &ds->verts[ i ], sizeof( *dv ) );\r
2252                 \r
2253                 /* offset? */\r
2254                 if( offset != 0.0f )\r
2255                         VectorMA( dv->xyz, offset, dv->normal, dv->xyz );\r
2256                 \r
2257                 /* expand model bounds\r
2258                    necessary because of misc_model surfaces on entities\r
2259                    note: does not happen on worldspawn as its bounds is only used for determining lightgrid bounds */\r
2260                 if( numBSPModels > 0 )\r
2261                         AddPointToBounds( dv->xyz, bspModels[ numBSPModels ].mins, bspModels[ numBSPModels ].maxs );\r
2262                 \r
2263                 /* debug color? */\r
2264                 if( debugSurfaces )\r
2265                 {\r
2266                         for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
2267                                 VectorCopy( debugColors[ (ds - mapDrawSurfs) % 12 ], dv->color[ k ] );\r
2268                 }\r
2269         }\r
2270 }\r
2271 \r
2272 \r
2273 \r
2274 /*\r
2275 FindDrawIndexes() - ydnar\r
2276 this attempts to find a run of indexes in the bsp that match the given indexes\r
2277 this tends to reduce the size of the bsp index pool by 1/3 or more\r
2278 returns numIndexes + 1 if the search failed\r
2279 */\r
2280 \r
2281 int FindDrawIndexes( int numIndexes, int *indexes )\r
2282 {\r
2283         int             i, j, numTestIndexes;\r
2284         \r
2285         \r
2286         /* dummy check */\r
2287         if( numIndexes < 3 || numBSPDrawIndexes < numIndexes || indexes == NULL )\r
2288                 return numBSPDrawIndexes;\r
2289         \r
2290         /* set limit */\r
2291         numTestIndexes = 1 + numBSPDrawIndexes - numIndexes;\r
2292         \r
2293         /* handle 3 indexes as a special case for performance */\r
2294         if( numIndexes == 3 )\r
2295         {\r
2296                 /* run through all indexes */\r
2297                 for( i = 0; i < numTestIndexes; i++ )\r
2298                 {\r
2299                         /* test 3 indexes */\r
2300                         if( indexes[ 0 ] == bspDrawIndexes[ i ] &&\r
2301                                 indexes[ 1 ] == bspDrawIndexes[ i + 1 ] &&\r
2302                                 indexes[ 2 ] == bspDrawIndexes[ i + 2 ] )\r
2303                         {\r
2304                                 numRedundantIndexes += numIndexes;\r
2305                                 return i;\r
2306                         }\r
2307                 }\r
2308                 \r
2309                 /* failed */\r
2310                 return numBSPDrawIndexes;\r
2311         }\r
2312         \r
2313         /* handle 4 or more indexes */\r
2314         for( i = 0; i < numTestIndexes; i++ )\r
2315         {\r
2316                 /* test first 4 indexes */\r
2317                 if( indexes[ 0 ] == bspDrawIndexes[ i ] &&\r
2318                         indexes[ 1 ] == bspDrawIndexes[ i + 1 ] &&\r
2319                         indexes[ 2 ] == bspDrawIndexes[ i + 2 ] &&\r
2320                         indexes[ 3 ] == bspDrawIndexes[ i + 3 ] )\r
2321                 {\r
2322                         /* handle 4 indexes */\r
2323                         if( numIndexes == 4 )\r
2324                                 return i;\r
2325                         \r
2326                         /* test the remainder */\r
2327                         for( j = 4; j < numIndexes; j++ )\r
2328                         {\r
2329                                 if( indexes[ j ] != bspDrawIndexes[ i + j ] )\r
2330                                         break;\r
2331                                 else if( j == (numIndexes - 1) )\r
2332                                 {\r
2333                                         numRedundantIndexes += numIndexes;\r
2334                                         return i;\r
2335                                 }\r
2336                         }\r
2337                 }\r
2338         }\r
2339         \r
2340         /* failed */\r
2341         return numBSPDrawIndexes;\r
2342 }\r
2343 \r
2344 \r
2345 \r
2346 /*\r
2347 EmitDrawIndexes() - ydnar\r
2348 attempts to find an existing run of drawindexes before adding new ones\r
2349 */\r
2350 \r
2351 void EmitDrawIndexes( mapDrawSurface_t *ds, bspDrawSurface_t *out )\r
2352 {\r
2353         int                     i;\r
2354         \r
2355         \r
2356         /* attempt to use redundant indexing */\r
2357         out->firstIndex = FindDrawIndexes( ds->numIndexes, ds->indexes );\r
2358         out->numIndexes = ds->numIndexes;\r
2359         if( out->firstIndex == numBSPDrawIndexes )\r
2360         {\r
2361                 /* copy new unique indexes */\r
2362                 for( i = 0; i < ds->numIndexes; i++ )\r
2363                 {\r
2364                         if( numBSPDrawIndexes == MAX_MAP_DRAW_INDEXES )\r
2365                                 Error( "MAX_MAP_DRAW_INDEXES" );\r
2366                         bspDrawIndexes[ numBSPDrawIndexes ] = ds->indexes[ i ];\r
2367 \r
2368                         /* validate the index */\r
2369                         if( ds->type != SURFACE_PATCH )\r
2370                         {\r
2371                                 if( bspDrawIndexes[ numBSPDrawIndexes ] < 0 || bspDrawIndexes[ numBSPDrawIndexes ] >= ds->numVerts )\r
2372                                 {\r
2373                                         Sys_Printf( "WARNING: %d %s has invalid index %d (%d)\n",\r
2374                                                 numBSPDrawSurfaces,\r
2375                                                 ds->shaderInfo->shader,\r
2376                                                 bspDrawIndexes[ numBSPDrawIndexes ],\r
2377                                                 i );\r
2378                                         bspDrawIndexes[ numBSPDrawIndexes ] = 0;\r
2379                                 }\r
2380                         }\r
2381                         \r
2382                         /* increment index count */\r
2383                         numBSPDrawIndexes++;\r
2384                 }\r
2385         }\r
2386 }\r
2387 \r
2388 \r
2389 \r
2390 \r
2391 /*\r
2392 EmitFlareSurface()\r
2393 emits a bsp flare drawsurface\r
2394 */\r
2395 \r
2396 void EmitFlareSurface( mapDrawSurface_t *ds )\r
2397 {\r
2398         int                                             i;\r
2399         bspDrawSurface_t                *out;\r
2400         \r
2401         \r
2402         /* ydnar: nuking useless flare drawsurfaces */\r
2403         if( emitFlares == qfalse && ds->type != SURFACE_SHADER )\r
2404                 return;\r
2405         \r
2406         /* limit check */\r
2407         if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )\r
2408                 Error( "MAX_MAP_DRAW_SURFS" );\r
2409         \r
2410         /* allocate a new surface */\r
2411         if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )\r
2412                 Error( "MAX_MAP_DRAW_SURFS" );\r
2413         out = &bspDrawSurfaces[ numBSPDrawSurfaces ];\r
2414         ds->outputNum = numBSPDrawSurfaces;\r
2415         numBSPDrawSurfaces++;\r
2416         memset( out, 0, sizeof( *out ) );\r
2417         \r
2418         /* set it up */\r
2419         out->surfaceType = MST_FLARE;\r
2420         out->shaderNum = EmitShader( ds->shaderInfo->shader, &ds->shaderInfo->contentFlags, &ds->shaderInfo->surfaceFlags );\r
2421         out->fogNum = ds->fogNum;\r
2422         \r
2423         /* RBSP */\r
2424         for( i = 0; i < MAX_LIGHTMAPS; i++ )\r
2425         {\r
2426                 out->lightmapNum[ i ] = -3;\r
2427                 out->lightmapStyles[ i ] = LS_NONE;\r
2428                 out->vertexStyles[ i ] = LS_NONE;\r
2429         }\r
2430         out->lightmapStyles[ 0 ] = ds->lightStyle;\r
2431         out->vertexStyles[ 0 ] = ds->lightStyle;\r
2432         \r
2433         VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );                  /* origin */\r
2434         VectorCopy( ds->lightmapVecs[ 0 ], out->lightmapVecs[ 0 ] );    /* color */\r
2435         VectorCopy( ds->lightmapVecs[ 1 ], out->lightmapVecs[ 1 ] );\r
2436         VectorCopy( ds->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );    /* normal */\r
2437         \r
2438         /* add to count */\r
2439         numSurfacesByType[ ds->type ]++;\r
2440 }\r
2441 \r
2442 \r
2443 \r
2444 /*\r
2445 EmitPatchSurface()\r
2446 emits a bsp patch drawsurface\r
2447 */\r
2448 \r
2449 void EmitPatchSurface( mapDrawSurface_t *ds )\r
2450 {\r
2451         int                                     i, j;\r
2452         bspDrawSurface_t        *out;\r
2453         int                                     surfaceFlags, contentFlags;\r
2454         \r
2455         \r
2456         /* invert the surface if necessary */\r
2457         if( ds->shaderInfo->invert )\r
2458         {\r
2459                 bspDrawVert_t   *dv1, *dv2, temp;\r
2460                 \r
2461 \r
2462                 /* walk the verts, flip the normal */\r
2463                 for( i = 0; i < ds->numVerts; i++ )\r
2464                         VectorScale( ds->verts[ i ].normal, -1.0f, ds->verts[ i ].normal );\r
2465                 \r
2466                 /* walk the verts again, but this time reverse their order */\r
2467                 for( j = 0; j < ds->patchHeight; j++ )\r
2468                 {\r
2469                         for( i = 0; i < (ds->patchWidth / 2); i++ )\r
2470                         {\r
2471                                 dv1 = &ds->verts[ j * ds->patchWidth + i ];\r
2472                                 dv2 = &ds->verts[ j * ds->patchWidth + (ds->patchWidth - i - 1) ];\r
2473                                 memcpy( &temp, dv1, sizeof( bspDrawVert_t ) );\r
2474                                 memcpy( dv1, dv2, sizeof( bspDrawVert_t ) );\r
2475                                 memcpy( dv2, &temp, sizeof( bspDrawVert_t ) );\r
2476                         }\r
2477                 }\r
2478                 \r
2479                 /* invert facing */\r
2480                 VectorScale( ds->lightmapVecs[ 2 ], -1.0f, ds->lightmapVecs[ 2 ] );\r
2481         }\r
2482         \r
2483         /* allocate a new surface */\r
2484         if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )\r
2485                 Error( "MAX_MAP_DRAW_SURFS" );\r
2486         out = &bspDrawSurfaces[ numBSPDrawSurfaces ];\r
2487         ds->outputNum = numBSPDrawSurfaces;\r
2488         numBSPDrawSurfaces++;\r
2489         memset( out, 0, sizeof( *out ) );\r
2490         \r
2491         /* set it up */\r
2492         out->surfaceType = MST_PATCH;\r
2493         if( debugSurfaces )\r
2494                 out->shaderNum = EmitShader( "debugsurfaces", NULL, NULL );\r
2495         else if( patchMeta )\r
2496         {\r
2497                 /* patch meta requires that we have nodraw patches for collision */\r
2498                 surfaceFlags = ds->shaderInfo->surfaceFlags;\r
2499                 contentFlags = ds->shaderInfo->contentFlags;\r
2500                 ApplySurfaceParm( "nodraw", &contentFlags, &surfaceFlags, NULL );\r
2501                 ApplySurfaceParm( "pointlight", &contentFlags, &surfaceFlags, NULL );\r
2502                 \r
2503                 /* we don't want this patch getting lightmapped */\r
2504                 VectorClear( ds->lightmapVecs[ 2 ] );\r
2505                 VectorClear( ds->lightmapAxis );\r
2506                 ds->sampleSize = 0;\r
2507 \r
2508                 /* emit the new fake shader */\r
2509                 out->shaderNum = EmitShader( ds->shaderInfo->shader, &contentFlags, &surfaceFlags );\r
2510         }\r
2511         else\r
2512                 out->shaderNum = EmitShader( ds->shaderInfo->shader, &ds->shaderInfo->contentFlags, &ds->shaderInfo->surfaceFlags );\r
2513         out->patchWidth = ds->patchWidth;\r
2514         out->patchHeight = ds->patchHeight;\r
2515         out->fogNum = ds->fogNum;\r
2516         \r
2517         /* RBSP */\r
2518         for( i = 0; i < MAX_LIGHTMAPS; i++ )\r
2519         {\r
2520                 out->lightmapNum[ i ] = -3;\r
2521                 out->lightmapStyles[ i ] = LS_NONE;\r
2522                 out->vertexStyles[ i ] = LS_NONE;\r
2523         }\r
2524         out->lightmapStyles[ 0 ] = LS_NORMAL;\r
2525         out->vertexStyles[ 0 ] = LS_NORMAL;\r
2526         \r
2527         /* ydnar: gs mods: previously, the lod bounds were stored in lightmapVecs[ 0 ] and [ 1 ], moved to bounds[ 0 ] and [ 1 ] */\r
2528         VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );\r
2529         VectorCopy( ds->bounds[ 0 ], out->lightmapVecs[ 0 ] );\r
2530         VectorCopy( ds->bounds[ 1 ], out->lightmapVecs[ 1 ] );\r
2531         VectorCopy( ds->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );\r
2532         \r
2533         /* ydnar: gs mods: clear out the plane normal */\r
2534         if( ds->planar == qfalse )\r
2535                 VectorClear( out->lightmapVecs[ 2 ] );\r
2536         \r
2537         /* emit the verts and indexes */\r
2538         EmitDrawVerts( ds, out );\r
2539         EmitDrawIndexes( ds, out );\r
2540         \r
2541         /* add to count */\r
2542         numSurfacesByType[ ds->type ]++;\r
2543 }\r
2544 \r
2545 \r
2546 \r
2547 /*\r
2548 OptimizeTriangleSurface() - ydnar\r
2549 optimizes the vertex/index data in a triangle surface\r
2550 */\r
2551 \r
2552 #define VERTEX_CACHE_SIZE       16\r
2553 \r
2554 static void OptimizeTriangleSurface( mapDrawSurface_t *ds )\r
2555 {\r
2556         int             i, j, k, temp, first, best, bestScore, score;\r
2557         int             vertexCache[ VERTEX_CACHE_SIZE + 1 ];   /* one more for optimizing insert */\r
2558         int             *indexes;\r
2559         \r
2560         \r
2561         /* certain surfaces don't get optimized */\r
2562         if( ds->numIndexes <= VERTEX_CACHE_SIZE ||\r
2563                 ds->shaderInfo->autosprite )\r
2564                 return;\r
2565         \r
2566         /* create index scratch pad */\r
2567         indexes = safe_malloc( ds->numIndexes * sizeof( *indexes ) );\r
2568         memcpy( indexes, ds->indexes, ds->numIndexes * sizeof( *indexes ) );\r
2569         \r
2570         /* setup */\r
2571         for( i = 0; i <= VERTEX_CACHE_SIZE && i < ds->numIndexes; i++ )\r
2572                 vertexCache[ i ] = indexes[ i ];\r
2573         \r
2574         /* add triangles in a vertex cache-aware order */\r
2575         for( i = 0; i < ds->numIndexes; i += 3 )\r
2576         {\r
2577                 /* find best triangle given the current vertex cache */\r
2578                 first = -1;\r
2579                 best = -1;\r
2580                 bestScore = -1;\r
2581                 for( j = 0; j < ds->numIndexes; j += 3 )\r
2582                 {\r
2583                         /* valid triangle? */\r
2584                         if( indexes[ j ] != -1 )\r
2585                         {\r
2586                                 /* set first if necessary */\r
2587                                 if( first < 0 )\r
2588                                         first = j;\r
2589                                 \r
2590                                 /* score the triangle */\r
2591                                 score = 0;\r
2592                                 for( k = 0; k < VERTEX_CACHE_SIZE; k++ )\r
2593                                 {\r
2594                                         if( indexes[ j ] == vertexCache[ k ] || indexes[ j + 1 ] == vertexCache[ k ] || indexes[ j + 2 ] == vertexCache[ k ] )\r
2595                                                 score++;\r
2596                                 }\r
2597                                 \r
2598                                 /* better triangle? */\r
2599                                 if( score > bestScore )\r
2600                                 {\r
2601                                         bestScore = score;\r
2602                                         best = j;\r
2603                                 }\r
2604                                 \r
2605                                 /* a perfect score of 3 means this triangle's verts are already present in the vertex cache */\r
2606                                 if( score == 3 )\r
2607                                         break;\r
2608                         }\r
2609                 }\r
2610                 \r
2611                 /* check if no decent triangle was found, and use first available */\r
2612                 if( best < 0 )\r
2613                         best = first;\r
2614                 \r
2615                 /* valid triangle? */\r
2616                 if( best >= 0 )\r
2617                 {\r
2618                         /* add triangle to vertex cache */\r
2619                         for( j = 0; j < 3; j++ )\r
2620                         {\r
2621                                 for( k = 0; k < VERTEX_CACHE_SIZE; k++ )\r
2622                                 {\r
2623                                         if( indexes[ best + j ] == vertexCache[ k ] )\r
2624                                                 break;\r
2625                                 }\r
2626                                 \r
2627                                 if( k >= VERTEX_CACHE_SIZE )\r
2628                                 {\r
2629                                         /* pop off top of vertex cache */\r
2630                                         for( k = VERTEX_CACHE_SIZE; k > 0; k-- )\r
2631                                                 vertexCache[ k ] = vertexCache[ k - 1 ];\r
2632                                         \r
2633                                         /* add vertex */\r
2634                                         vertexCache[ 0 ] = indexes[ best + j ];\r
2635                                 }\r
2636                         }\r
2637                         \r
2638                         /* add triangle to surface */\r
2639                         ds->indexes[ i ] = indexes[ best ];\r
2640                         ds->indexes[ i + 1 ] = indexes[ best + 1 ];\r
2641                         ds->indexes[ i + 2 ] = indexes[ best + 2 ];\r
2642                         \r
2643                         /* clear from input pool */\r
2644                         indexes[ best ] = -1;\r
2645                         indexes[ best + 1 ] = -1;\r
2646                         indexes[ best + 2 ] = -1;\r
2647                         \r
2648                         /* sort triangle windings (312 -> 123) */\r
2649                         while( ds->indexes[ i ] > ds->indexes[ i + 1 ] || ds->indexes[ i ] > ds->indexes[ i + 2 ] )\r
2650                         {\r
2651                                 temp = ds->indexes[ i ];\r
2652                                 ds->indexes[ i ] = ds->indexes[ i + 1 ];\r
2653                                 ds->indexes[ i + 1 ] = ds->indexes[ i + 2 ];\r
2654                                 ds->indexes[ i + 2 ] = temp;\r
2655                         }\r
2656                 }\r
2657         }\r
2658         \r
2659         /* clean up */\r
2660         free( indexes );\r
2661 }\r
2662 \r
2663 \r
2664 \r
2665 /*\r
2666 EmitTriangleSurface()\r
2667 creates a bsp drawsurface from arbitrary triangle surfaces\r
2668 */\r
2669 \r
2670 static void EmitTriangleSurface( mapDrawSurface_t *ds )\r
2671 {\r
2672         int                                             i, temp;\r
2673         bspDrawSurface_t                *out;\r
2674         \r
2675         \r
2676         /* invert the surface if necessary */\r
2677         if( ds->shaderInfo->invert )\r
2678         {\r
2679                 /* walk the indexes, reverse the triangle order */\r
2680                 for( i = 0; i < ds->numIndexes; i += 3 )\r
2681                 {\r
2682                         temp = ds->indexes[ i ];\r
2683                         ds->indexes[ i ] = ds->indexes[ i + 1 ];\r
2684                         ds->indexes[ i + 1 ] = temp;\r
2685                 }\r
2686                 \r
2687                 /* walk the verts, flip the normal */\r
2688                 for( i = 0; i < ds->numVerts; i++ )\r
2689                         VectorScale( ds->verts[ i ].normal, -1.0f, ds->verts[ i ].normal );\r
2690                 \r
2691                 /* invert facing */\r
2692                 VectorScale( ds->lightmapVecs[ 2 ], -1.0f, ds->lightmapVecs[ 2 ] );\r
2693         }\r
2694         \r
2695         /* allocate a new surface */\r
2696         if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )\r
2697                 Error( "MAX_MAP_DRAW_SURFS" );\r
2698         out = &bspDrawSurfaces[ numBSPDrawSurfaces ];\r
2699         ds->outputNum = numBSPDrawSurfaces;\r
2700         numBSPDrawSurfaces++;\r
2701         memset( out, 0, sizeof( *out ) );\r
2702         \r
2703         /* ydnar/sd: handle wolf et foliage surfaces */\r
2704         if( ds->type == SURFACE_FOLIAGE )\r
2705                 out->surfaceType = MST_FOLIAGE;\r
2706         \r
2707         /* ydnar: gs mods: handle lightmapped terrain (force to planar type) */\r
2708         //%     else if( VectorLength( ds->lightmapAxis ) <= 0.0f || ds->type == SURFACE_TRIANGLES || ds->type == SURFACE_FOGHULL || debugSurfaces )\r
2709         else if( (VectorLength( ds->lightmapAxis ) <= 0.0f && ds->planar == qfalse) || ds->type == SURFACE_TRIANGLES || ds->type == SURFACE_FOGHULL || debugSurfaces )\r
2710                 out->surfaceType = MST_TRIANGLE_SOUP;\r
2711         \r
2712         /* set to a planar face */\r
2713         else\r
2714                 out->surfaceType = MST_PLANAR;\r
2715         \r
2716         /* set it up */\r
2717         if( debugSurfaces )\r
2718                 out->shaderNum = EmitShader( "debugsurfaces", NULL, NULL );\r
2719         else\r
2720                 out->shaderNum = EmitShader( ds->shaderInfo->shader, &ds->shaderInfo->contentFlags, &ds->shaderInfo->surfaceFlags );\r
2721         out->patchWidth = ds->patchWidth;\r
2722         out->patchHeight = ds->patchHeight;\r
2723         out->fogNum = ds->fogNum;\r
2724         \r
2725         /* debug inset (push each triangle vertex towards the center of each triangle it is on */\r
2726         if( debugInset )\r
2727         {\r
2728                 bspDrawVert_t   *a, *b, *c;\r
2729                 vec3_t                  cent, dir;\r
2730 \r
2731                 \r
2732                 /* walk triangle list */\r
2733                 for( i = 0; i < ds->numIndexes; i += 3 )\r
2734                 {\r
2735                         /* get verts */\r
2736                         a = &ds->verts[ ds->indexes[ i ] ];\r
2737                         b = &ds->verts[ ds->indexes[ i + 1 ] ];\r
2738                         c = &ds->verts[ ds->indexes[ i + 2 ] ];\r
2739                         \r
2740                         /* calculate centroid */\r
2741                         VectorCopy( a->xyz, cent );\r
2742                         VectorAdd( cent, b->xyz, cent );\r
2743                         VectorAdd( cent, c->xyz, cent );\r
2744                         VectorScale( cent, 1.0f / 3.0f, cent );\r
2745                         \r
2746                         /* offset each vertex */\r
2747                         VectorSubtract( cent, a->xyz, dir );\r
2748                         VectorNormalize( dir, dir );\r
2749                         VectorAdd( a->xyz, dir, a->xyz );\r
2750                         VectorSubtract( cent, b->xyz, dir );\r
2751                         VectorNormalize( dir, dir );\r
2752                         VectorAdd( b->xyz, dir, b->xyz );\r
2753                         VectorSubtract( cent, c->xyz, dir );\r
2754                         VectorNormalize( dir, dir );\r
2755                         VectorAdd( c->xyz, dir, c->xyz );\r
2756                 }\r
2757         }\r
2758         \r
2759         /* RBSP */\r
2760         for( i = 0; i < MAX_LIGHTMAPS; i++ )\r
2761         {\r
2762                 out->lightmapNum[ i ] = -3;\r
2763                 out->lightmapStyles[ i ] = LS_NONE;\r
2764                 out->vertexStyles[ i ] = LS_NONE;\r
2765         }\r
2766         out->lightmapStyles[ 0 ] = LS_NORMAL;\r
2767         out->vertexStyles[ 0 ] = LS_NORMAL;\r
2768         \r
2769         /* lightmap vectors (lod bounds for patches */\r
2770         VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );\r
2771         VectorCopy( ds->lightmapVecs[ 0 ], out->lightmapVecs[ 0 ] );\r
2772         VectorCopy( ds->lightmapVecs[ 1 ], out->lightmapVecs[ 1 ] );\r
2773         VectorCopy( ds->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );\r
2774         \r
2775         /* ydnar: gs mods: clear out the plane normal */\r
2776         if( ds->planar == qfalse )\r
2777                 VectorClear( out->lightmapVecs[ 2 ] );\r
2778         \r
2779         /* optimize the surface's triangles */\r
2780         OptimizeTriangleSurface( ds );\r
2781         \r
2782         /* emit the verts and indexes */\r
2783         EmitDrawVerts( ds, out );\r
2784         EmitDrawIndexes( ds, out );\r
2785         \r
2786         /* add to count */\r
2787         numSurfacesByType[ ds->type ]++;\r
2788 }\r
2789 \r
2790 \r
2791 \r
2792 /*\r
2793 EmitFaceSurface()\r
2794 emits a bsp planar winding (brush face) drawsurface\r
2795 */\r
2796 \r
2797 static void EmitFaceSurface( mapDrawSurface_t *ds )\r
2798 {\r
2799         /* strip/fan finding was moved elsewhere */\r
2800         StripFaceSurface( ds );\r
2801         EmitTriangleSurface( ds );\r
2802 }\r
2803 \r
2804 \r
2805 \r
2806 /*\r
2807 MakeDebugPortalSurfs_r() - ydnar\r
2808 generates drawsurfaces for passable portals in the bsp\r
2809 */\r
2810 \r
2811 static void MakeDebugPortalSurfs_r( node_t *node, shaderInfo_t *si )\r
2812 {\r
2813         int                                     i, k, c, s;     \r
2814         portal_t                        *p;\r
2815         winding_t                       *w;\r
2816         mapDrawSurface_t        *ds;\r
2817         bspDrawVert_t           *dv;\r
2818         \r
2819         \r
2820         /* recurse if decision node */\r
2821         if( node->planenum != PLANENUM_LEAF)\r
2822         {\r
2823                 MakeDebugPortalSurfs_r( node->children[ 0 ], si );\r
2824                 MakeDebugPortalSurfs_r( node->children[ 1 ], si );\r
2825                 return;\r
2826         }\r
2827         \r
2828         /* don't bother with opaque leaves */\r
2829         if( node->opaque )\r
2830                 return;\r
2831         \r
2832         /* walk the list of portals */\r
2833         for( c = 0, p = node->portals; p != NULL; c++, p = p->next[ s ] )\r
2834         {\r
2835                 /* get winding and side even/odd */\r
2836                 w = p->winding;\r
2837                 s = (p->nodes[ 1 ] == node);\r
2838                 \r
2839                 /* is this a valid portal for this leaf? */\r
2840                 if( w && p->nodes[ 0 ] == node )\r
2841                 {\r
2842                         /* is this portal passable? */\r
2843                         if( PortalPassable( p ) == qfalse )\r
2844                                 continue;\r
2845                         \r
2846                         /* check max points */\r
2847                         if( w->numpoints > 64 )\r
2848                                 Error( "MakePortalSurfs_r: w->numpoints = %d", w->numpoints );\r
2849                         \r
2850                         /* allocate a drawsurface */\r
2851                         ds = AllocDrawSurface( SURFACE_FACE );\r
2852                         ds->shaderInfo = si;\r
2853                         ds->planar = qtrue;\r
2854                         ds->sideRef = AllocSideRef( p->side, NULL );\r
2855                         ds->planeNum = FindFloatPlane( p->plane.normal, p->plane.dist, 0, NULL );\r
2856                         VectorCopy( p->plane.normal, ds->lightmapVecs[ 2 ] );\r
2857                         ds->fogNum = -1;\r
2858                         ds->numVerts = w->numpoints;\r
2859                         ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );\r
2860                         memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );\r
2861                         \r
2862                         /* walk the winding */\r
2863                         for( i = 0; i < ds->numVerts; i++ )\r
2864                         {\r
2865                                 /* get vert */\r
2866                                 dv = ds->verts + i;\r
2867                                 \r
2868                                 /* set it */\r
2869                                 VectorCopy( w->p[ i ], dv->xyz );\r
2870                                 VectorCopy( p->plane.normal, dv->normal );\r
2871                                 dv->st[ 0 ] = 0;\r
2872                                 dv->st[ 1 ] = 0;\r
2873                                 for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
2874                                 {\r
2875                                         VectorCopy( debugColors[ c % 12 ], dv->color[ k ] );\r
2876                                         dv->color[ k ][ 3 ] = 32;\r
2877                                 }\r
2878                         }\r
2879                 }\r
2880         }\r
2881 }\r
2882 \r
2883 \r
2884 \r
2885 /*\r
2886 MakeDebugPortalSurfs() - ydnar\r
2887 generates drawsurfaces for passable portals in the bsp\r
2888 */\r
2889 \r
2890 void MakeDebugPortalSurfs( tree_t *tree )\r
2891 {\r
2892         shaderInfo_t    *si;\r
2893         \r
2894         \r
2895         /* note it */\r
2896         Sys_FPrintf( SYS_VRB, "--- MakeDebugPortalSurfs ---\n" );\r
2897         \r
2898         /* get portal debug shader */\r
2899         si = ShaderInfoForShader( "debugportals" );\r
2900         \r
2901         /* walk the tree */\r
2902         MakeDebugPortalSurfs_r( tree->headnode, si );\r
2903 }\r
2904 \r
2905 \r
2906 \r
2907 /*\r
2908 MakeFogHullSurfs()\r
2909 generates drawsurfaces for a foghull (this MUST use a sky shader)\r
2910 */\r
2911 \r
2912 void MakeFogHullSurfs( entity_t *e, tree_t *tree, char *shader )\r
2913 {\r
2914         shaderInfo_t            *si;\r
2915         mapDrawSurface_t        *ds;\r
2916         vec3_t                          fogMins, fogMaxs;\r
2917         int                                     i, indexes[] =\r
2918                                                 {\r
2919                                                         0, 1, 2, 0, 2, 3,\r
2920                                                         4, 7, 5, 5, 7, 6,\r
2921                                                         1, 5, 6, 1, 6, 2,\r
2922                                                         0, 4, 5, 0, 5, 1,\r
2923                                                         2, 6, 7, 2, 7, 3,\r
2924                                                         3, 7, 4, 3, 4, 0\r
2925                                                 };\r
2926 \r
2927         \r
2928         /* dummy check */\r
2929         if( shader == NULL || shader[ 0 ] == '\0' )\r
2930                 return;\r
2931         \r
2932         /* note it */\r
2933         Sys_FPrintf( SYS_VRB, "--- MakeFogHullSurfs ---\n" );\r
2934         \r
2935         /* get hull bounds */\r
2936         VectorCopy( mapMins, fogMins );\r
2937         VectorCopy( mapMaxs, fogMaxs );\r
2938         for( i = 0; i < 3; i++ )\r
2939         {\r
2940                 fogMins[ i ] -= 128;\r
2941                 fogMaxs[ i ] += 128;\r
2942         }\r
2943         \r
2944         /* get foghull shader */\r
2945         si = ShaderInfoForShader( shader );\r
2946         \r
2947         /* allocate a drawsurface */\r
2948         ds = AllocDrawSurface( SURFACE_FOGHULL );\r
2949         ds->shaderInfo = si;\r
2950         ds->fogNum = -1;\r
2951         ds->numVerts = 8;\r
2952         ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );\r
2953         memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );\r
2954         ds->numIndexes = 36;\r
2955         ds->indexes = safe_malloc( ds->numIndexes * sizeof( *ds->indexes ) );\r
2956         memset( ds->indexes, 0, ds->numIndexes * sizeof( *ds->indexes ) );\r
2957         \r
2958         /* set verts */\r
2959         VectorSet( ds->verts[ 0 ].xyz, fogMins[ 0 ], fogMins[ 1 ], fogMins[ 2 ] );\r
2960         VectorSet( ds->verts[ 1 ].xyz, fogMins[ 0 ], fogMaxs[ 1 ], fogMins[ 2 ] );\r
2961         VectorSet( ds->verts[ 2 ].xyz, fogMaxs[ 0 ], fogMaxs[ 1 ], fogMins[ 2 ] );\r
2962         VectorSet( ds->verts[ 3 ].xyz, fogMaxs[ 0 ], fogMins[ 1 ], fogMins[ 2 ] );\r
2963         \r
2964         VectorSet( ds->verts[ 4 ].xyz, fogMins[ 0 ], fogMins[ 1 ], fogMaxs[ 2 ] );\r
2965         VectorSet( ds->verts[ 5 ].xyz, fogMins[ 0 ], fogMaxs[ 1 ], fogMaxs[ 2 ] );\r
2966         VectorSet( ds->verts[ 6 ].xyz, fogMaxs[ 0 ], fogMaxs[ 1 ], fogMaxs[ 2 ] );\r
2967         VectorSet( ds->verts[ 7 ].xyz, fogMaxs[ 0 ], fogMins[ 1 ], fogMaxs[ 2 ] );\r
2968         \r
2969         /* set indexes */\r
2970         memcpy( ds->indexes, indexes, ds->numIndexes * sizeof( *ds->indexes ) );\r
2971 }\r
2972 \r
2973 \r
2974 \r
2975 /*\r
2976 BiasSurfaceTextures()\r
2977 biases a surface's texcoords as close to 0 as possible\r
2978 */\r
2979 \r
2980 void BiasSurfaceTextures( mapDrawSurface_t *ds )\r
2981 {\r
2982         int             i;\r
2983         \r
2984         \r
2985         /* calculate the surface texture bias */\r
2986         CalcSurfaceTextureRange( ds );\r
2987         \r
2988         /* don't bias globaltextured shaders */\r
2989         if( ds->shaderInfo->globalTexture )\r
2990                 return;\r
2991         \r
2992         /* bias the texture coordinates */\r
2993         for( i = 0; i < ds->numVerts; i++ )\r
2994         {\r
2995                 ds->verts[ i ].st[ 0 ] += ds->bias[ 0 ];\r
2996                 ds->verts[ i ].st[ 1 ] += ds->bias[ 1 ];\r
2997         }\r
2998 }\r
2999 \r
3000 \r
3001 \r
3002 /*\r
3003 AddSurfaceModelsToTriangle_r()\r
3004 adds models to a specified triangle, returns the number of models added\r
3005 */\r
3006 \r
3007 int AddSurfaceModelsToTriangle_r( mapDrawSurface_t *ds, surfaceModel_t *model, bspDrawVert_t **tri )\r
3008 {\r
3009         bspDrawVert_t   mid, *tri2[ 3 ];\r
3010         int                             max, n, localNumSurfaceModels;\r
3011         \r
3012         \r
3013         /* init */\r
3014         localNumSurfaceModels = 0;\r
3015         \r
3016         /* subdivide calc */\r
3017         {\r
3018                 int                     i;\r
3019                 float           *a, *b, dx, dy, dz, dist, maxDist;\r
3020                 \r
3021                 \r
3022                 /* find the longest edge and split it */\r
3023                 max = -1;\r
3024                 maxDist = 0.0f;\r
3025                 for( i = 0; i < 3; i++ )\r
3026                 {\r
3027                         /* get verts */\r
3028                         a = tri[ i ]->xyz;\r
3029                         b = tri[ (i + 1) % 3 ]->xyz;\r
3030                         \r
3031                         /* get dists */\r
3032                         dx = a[ 0 ] - b[ 0 ];\r
3033                         dy = a[ 1 ] - b[ 1 ];\r
3034                         dz = a[ 2 ] - b[ 2 ];\r
3035                         dist = (dx * dx) + (dy * dy) + (dz * dz);\r
3036                         \r
3037                         /* longer? */\r
3038                         if( dist > maxDist )\r
3039                         {\r
3040                                 maxDist = dist;\r
3041                                 max = i;\r
3042                         }\r
3043                 }\r
3044                 \r
3045                 /* is the triangle small enough? */\r
3046                 if( max < 0 || maxDist <= (model->density * model->density) )\r
3047                 {\r
3048                         float   odds, r, angle;\r
3049                         vec3_t  origin, normal, scale, axis[ 3 ], angles;\r
3050                         m4x4_t  transform, temp;\r
3051 \r
3052                         \r
3053                         /* roll the dice (model's odds scaled by vertex alpha) */\r
3054                         odds = model->odds * (tri[ 0 ]->color[ 0 ][ 3 ] + tri[ 0 ]->color[ 0 ][ 3 ] + tri[ 0 ]->color[ 0 ][ 3 ]) / 765.0f;\r
3055                         r = Random();\r
3056                         if( r > model->odds )\r
3057                                 return 0;\r
3058                         \r
3059                         /* calculate scale */\r
3060                         r = model->minScale + Random() * (model->maxScale - model->minScale);\r
3061                         VectorSet( scale, r, r, r );\r
3062                         \r
3063                         /* calculate angle */\r
3064                         angle = model->minAngle + Random() * (model->maxAngle - model->minAngle);\r
3065                         \r
3066                         /* calculate average origin */\r
3067                         VectorCopy( tri[ 0 ]->xyz, origin );\r
3068                         VectorAdd( origin, tri[ 1 ]->xyz, origin );\r
3069                         VectorAdd( origin, tri[ 2 ]->xyz, origin );\r
3070                         VectorScale( origin, (1.0f / 3.0f), origin );\r
3071                         \r
3072                         /* clear transform matrix */\r
3073                         m4x4_identity( transform );\r
3074 \r
3075                         /* handle oriented models */\r
3076                         if( model->oriented )\r
3077                         {\r
3078                                 /* set angles */\r
3079                                 VectorSet( angles, 0.0f, 0.0f, angle );\r
3080                                 \r
3081                                 /* calculate average normal */\r
3082                                 VectorCopy( tri[ 0 ]->normal, normal );\r
3083                                 VectorAdd( normal, tri[ 1 ]->normal, normal );\r
3084                                 VectorAdd( normal, tri[ 2 ]->normal, normal );\r
3085                                 if( VectorNormalize( normal, axis[ 2 ] ) == 0.0f )\r
3086                                         VectorCopy( tri[ 0 ]->normal, axis[ 2 ] );\r
3087                                 \r
3088                                 /* make perpendicular vectors */\r
3089                                 MakeNormalVectors( axis[ 2 ], axis[ 1 ], axis[ 0 ] );\r
3090                                 \r
3091                                 /* copy to matrix */\r
3092                                 m4x4_identity( temp );\r
3093                                 temp[ 0 ] = axis[ 0 ][ 0 ];     temp[ 1 ] = axis[ 0 ][ 1 ];     temp[ 2 ] = axis[ 0 ][ 2 ];\r
3094                                 temp[ 4 ] = axis[ 1 ][ 0 ];     temp[ 5 ] = axis[ 1 ][ 1 ];     temp[ 6 ] = axis[ 1 ][ 2 ];\r
3095                                 temp[ 8 ] = axis[ 2 ][ 0 ];     temp[ 9 ] = axis[ 2 ][ 1 ];     temp[ 10 ] = axis[ 2 ][ 2 ];\r
3096                                 \r
3097                                 /* scale */\r
3098                                 m4x4_scale_by_vec3( temp, scale );\r
3099                                 \r
3100                                 /* rotate around z axis */\r
3101                                 m4x4_rotate_by_vec3( temp, angles, eXYZ );\r
3102                                 \r
3103                                 /* translate */\r
3104                                 m4x4_translate_by_vec3( transform, origin );\r
3105                                 \r
3106                                 /* tranform into axis space */\r
3107                                 m4x4_multiply_by_m4x4( transform, temp );\r
3108                         }\r
3109                         \r
3110                         /* handle z-up models */\r
3111                         else\r
3112                         {\r
3113                                 /* set angles */\r
3114                                 VectorSet( angles, 0.0f, 0.0f, angle );\r
3115                                 \r
3116                                 /* set matrix */\r
3117                                 m4x4_pivoted_transform_by_vec3( transform, origin, angles, eXYZ, scale, vec3_origin );\r
3118                         }\r
3119                         \r
3120                         /* insert the model */\r
3121                         InsertModel( (char *) model->model, 0, transform, NULL, ds->celShader, ds->entityNum, ds->castShadows, ds->recvShadows, 0, ds->lightmapScale );\r
3122                         \r
3123                         /* return to sender */\r
3124                         return 1;\r
3125                 }\r
3126         }\r
3127         \r
3128         /* split the longest edge and map it */\r
3129         LerpDrawVert( tri[ max ], tri[ (max + 1) % 3 ], &mid );\r
3130         \r
3131         /* recurse to first triangle */\r
3132         VectorCopy( tri, tri2 );\r
3133         tri2[ max ] = &mid;\r
3134         n = AddSurfaceModelsToTriangle_r( ds, model, tri2 );\r
3135         if( n < 0 )\r
3136                 return n;\r
3137         localNumSurfaceModels += n;\r
3138         \r
3139         /* recurse to second triangle */\r
3140         VectorCopy( tri, tri2 );\r
3141         tri2[ (max + 1) % 3 ] = &mid;\r
3142         n = AddSurfaceModelsToTriangle_r( ds, model, tri2 );\r
3143         if( n < 0 )\r
3144                 return n;\r
3145         localNumSurfaceModels += n;\r
3146         \r
3147         /* return count */\r
3148         return localNumSurfaceModels;\r
3149 }\r
3150 \r
3151 \r
3152 \r
3153 /*\r
3154 AddSurfaceModels()\r
3155 adds a surface's shader models to the surface\r
3156 */\r
3157 \r
3158 int AddSurfaceModels( mapDrawSurface_t *ds )\r
3159 {\r
3160         surfaceModel_t  *model;\r
3161         int                             i, x, y, n, pw[ 5 ], r, localNumSurfaceModels, iterations;\r
3162         mesh_t                  src, *mesh, *subdivided;\r
3163         bspDrawVert_t   centroid, *tri[ 3 ];\r
3164         float                   alpha;\r
3165         \r
3166         \r
3167         /* dummy check */\r
3168         if( ds == NULL || ds->shaderInfo == NULL || ds->shaderInfo->surfaceModel == NULL )\r
3169                 return 0;\r
3170         \r
3171         /* init */\r
3172         localNumSurfaceModels = 0;\r
3173         \r
3174         /* walk the model list */\r
3175         for( model = ds->shaderInfo->surfaceModel; model != NULL; model = model->next )\r
3176         {\r
3177                 /* switch on type */\r
3178                 switch( ds->type )\r
3179                 {\r
3180                         /* handle brush faces and decals */\r
3181                         case SURFACE_FACE:\r
3182                         case SURFACE_DECAL:\r
3183                                 /* calculate centroid */\r
3184                                 memset( &centroid, 0, sizeof( centroid ) );\r
3185                                 alpha = 0.0f;\r
3186                                 \r
3187                                 /* walk verts */\r
3188                                 for( i = 0; i < ds->numVerts; i++ )\r
3189                                 {\r
3190                                         VectorAdd( centroid.xyz, ds->verts[ i ].xyz, centroid.xyz );\r
3191                                         VectorAdd( centroid.normal, ds->verts[ i ].normal, centroid.normal );\r
3192                                         centroid.st[ 0 ] += ds->verts[ i ].st[ 0 ];\r
3193                                         centroid.st[ 1 ] += ds->verts[ i ].st[ 1 ];\r
3194                                         alpha += ds->verts[ i ].color[ 0 ][ 3 ];\r
3195                                 }\r
3196                                 \r
3197                                 /* average */\r
3198                                 centroid.xyz[ 0 ] /= ds->numVerts;\r
3199                                 centroid.xyz[ 1 ] /= ds->numVerts;\r
3200                                 centroid.xyz[ 2 ] /= ds->numVerts;\r
3201                                 if( VectorNormalize( centroid.normal, centroid.normal ) == 0.0f )\r
3202                                         VectorCopy( ds->verts[ 0 ].normal, centroid.normal );\r
3203                                 centroid.st[ 0 ]  /= ds->numVerts;\r
3204                                 centroid.st[ 1 ]  /= ds->numVerts;\r
3205                                 alpha /= ds->numVerts;\r
3206                                 centroid.color[ 0 ][ 0 ] = 0xFF;\r
3207                                 centroid.color[ 0 ][ 1 ] = 0xFF;\r
3208                                 centroid.color[ 0 ][ 2 ] = 0xFF;\r
3209                                 centroid.color[ 0 ][ 2 ] = (alpha > 255.0f ? 0xFF : alpha);\r
3210                                 \r
3211                                 /* head vert is centroid */\r
3212                                 tri[ 0 ] = &centroid;\r
3213                                 \r
3214                                 /* walk fanned triangles */\r
3215                                 for( i = 0; i < ds->numVerts; i++ )\r
3216                                 {\r
3217                                         /* set triangle */\r
3218                                         tri[ 1 ] = &ds->verts[ i ];\r
3219                                         tri[ 2 ] = &ds->verts[ (i + 1) % ds->numVerts ];\r
3220                                         \r
3221                                         /* create models */\r
3222                                         n = AddSurfaceModelsToTriangle_r( ds, model, tri );\r
3223                                         if( n < 0 )\r
3224                                                 return n;\r
3225                                         localNumSurfaceModels += n;\r
3226                                 }\r
3227                                 break;\r
3228                         \r
3229                         /* handle patches */\r
3230                         case SURFACE_PATCH:\r
3231                                 /* subdivide the surface */\r
3232                                 src.width = ds->patchWidth;\r
3233                                 src.height = ds->patchHeight;\r
3234                                 src.verts = ds->verts;\r
3235                                 //%     subdivided = SubdivideMesh( src, 8.0f, 512 );\r
3236                                 iterations = IterationsForCurve( ds->longestCurve, patchSubdivisions );\r
3237                                 subdivided = SubdivideMesh2( src, iterations );\r
3238                                 \r
3239                                 /* fit it to the curve and remove colinear verts on rows/columns */\r
3240                                 PutMeshOnCurve( *subdivided );\r
3241                                 mesh = RemoveLinearMeshColumnsRows( subdivided );\r
3242                                 FreeMesh( subdivided );\r
3243                                 \r
3244                                 /* subdivide each quad to place the models */\r
3245                                 for( y = 0; y < (mesh->height - 1); y++ )\r
3246                                 {\r
3247                                         for( x = 0; x < (mesh->width - 1); x++ )\r
3248                                         {\r
3249                                                 /* set indexes */\r
3250                                                 pw[ 0 ] = x + (y * mesh->width);\r
3251                                                 pw[ 1 ] = x + ((y + 1) * mesh->width);\r
3252                                                 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);\r
3253                                                 pw[ 3 ] = x + 1 + (y * mesh->width);\r
3254                                                 pw[ 4 ] = x + (y * mesh->width);        /* same as pw[ 0 ] */\r
3255                                                 \r
3256                                                 /* set radix */\r
3257                                                 r = (x + y) & 1;\r
3258                                                 \r
3259                                                 /* triangle 1 */\r
3260                                                 tri[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];\r
3261                                                 tri[ 1 ] = &mesh->verts[ pw[ r + 1 ] ];\r
3262                                                 tri[ 2 ] = &mesh->verts[ pw[ r + 2 ] ];\r
3263                                                 n = AddSurfaceModelsToTriangle_r( ds, model, tri );\r
3264                                                 if( n < 0 )\r
3265                                                         return n;\r
3266                                                 localNumSurfaceModels += n;\r
3267                                                 \r
3268                                                 /* triangle 2 */\r
3269                                                 tri[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];\r
3270                                                 tri[ 1 ] = &mesh->verts[ pw[ r + 2 ] ];\r
3271                                                 tri[ 2 ] = &mesh->verts[ pw[ r + 3 ] ];\r
3272                                                 n = AddSurfaceModelsToTriangle_r( ds, model, tri );\r
3273                                                 if( n < 0 )\r
3274                                                         return n;\r
3275                                                 localNumSurfaceModels += n;\r
3276                                         }\r
3277                                 }\r
3278                                 \r
3279                                 /* free the subdivided mesh */\r
3280                                 FreeMesh( mesh );\r
3281                                 break;\r
3282                         \r
3283                         /* handle triangle surfaces */\r
3284                         case SURFACE_TRIANGLES:\r
3285                         case SURFACE_FORCED_META:\r
3286                         case SURFACE_META:\r
3287                                 /* walk the triangle list */\r
3288                                 for( i = 0; i < ds->numIndexes; i += 3 )\r
3289                                 {\r
3290                                         tri[ 0 ] = &ds->verts[ ds->indexes[ i ] ];\r
3291                                         tri[ 1 ] = &ds->verts[ ds->indexes[ i + 1 ] ];\r
3292                                         tri[ 2 ] = &ds->verts[ ds->indexes[ i + 2 ] ];\r
3293                                         n = AddSurfaceModelsToTriangle_r( ds, model, tri );\r
3294                                         if( n < 0 )\r
3295                                                 return n;\r
3296                                         localNumSurfaceModels += n;\r
3297                                 }\r
3298                                 break;\r
3299                         \r
3300                         /* no support for flares, foghull, etc */\r
3301                         default:\r
3302                                 break;\r
3303                 }\r
3304         }\r
3305         \r
3306         /* return count */\r
3307         return localNumSurfaceModels;\r
3308 }\r
3309 \r
3310 \r
3311 \r
3312 /*\r
3313 AddEntitySurfaceModels() - ydnar\r
3314 adds surfacemodels to an entity's surfaces\r
3315 */\r
3316 \r
3317 void AddEntitySurfaceModels( entity_t *e )\r
3318 {\r
3319         int             i;\r
3320         \r
3321         \r
3322         /* note it */\r
3323         Sys_FPrintf( SYS_VRB, "--- AddEntitySurfaceModels ---\n" );\r
3324         \r
3325         /* walk the surface list */\r
3326         for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )\r
3327                 numSurfaceModels += AddSurfaceModels( &mapDrawSurfs[ i ] );\r
3328 }\r
3329 \r
3330 \r
3331 \r
3332 /*\r
3333 FilterDrawsurfsIntoTree()\r
3334 upon completion, all drawsurfs that actually generate a reference\r
3335 will have been emited to the bspfile arrays, and the references\r
3336 will have valid final indexes\r
3337 */\r
3338 \r
3339 void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree )\r
3340 {\r
3341         int                                     i, j;\r
3342         mapDrawSurface_t        *ds;\r
3343         shaderInfo_t            *si;\r
3344         vec3_t                          origin, mins, maxs;\r
3345         int                                     refs;\r
3346         int                                     numSurfs, numRefs, numSkyboxSurfaces;\r
3347         \r
3348         \r
3349         /* note it */\r
3350         Sys_FPrintf( SYS_VRB, "--- FilterDrawsurfsIntoTree ---\n" );\r
3351         \r
3352         /* filter surfaces into the tree */\r
3353         numSurfs = 0;\r
3354         numRefs = 0;\r
3355         numSkyboxSurfaces = 0;\r
3356         for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )\r
3357         {\r
3358                 /* get surface and try to early out */\r
3359                 ds = &mapDrawSurfs[ i ];\r
3360                 if( ds->numVerts == 0 && ds->type != SURFACE_FLARE && ds->type != SURFACE_SHADER )\r
3361                         continue;\r
3362                 \r
3363                 /* get shader */\r
3364                 si = ds->shaderInfo;\r
3365                 \r
3366                 /* ydnar: skybox surfaces are special */\r
3367                 if( ds->skybox )\r
3368                 {\r
3369                         refs = AddReferenceToTree_r( ds, tree->headnode, qtrue );\r
3370                         ds->skybox = qfalse;\r
3371                 }\r
3372                 else\r
3373                 {\r
3374                         /* refs initially zero */\r
3375                         refs = 0;\r
3376                         \r
3377                         /* ydnar: apply alphamod */\r
3378                         AlphaMod( ds->shaderInfo->alphaMod, ds->numVerts, ds->verts );\r
3379                         \r
3380                         /* apply texture coordinate mods */\r
3381                         for( j = 0; j < ds->numVerts; j++ )\r
3382                                 TcMod( si->mod, ds->verts[ j ].st );\r
3383                         \r
3384                         /* ydnar: make fur surfaces */\r
3385                         if( si->furNumLayers > 0 )\r
3386                                 Fur( ds );\r
3387                         \r
3388                         /* ydnar/sd: make foliage surfaces */\r
3389                         if( si->foliage != NULL )\r
3390                                 Foliage( ds );\r
3391                         \r
3392                         /* create a flare surface if necessary */\r
3393                         if( si->flareShader[ 0 ] )\r
3394                                 AddSurfaceFlare( ds, e->origin );\r
3395                         \r
3396                         /* ydnar: don't emit nodraw surfaces (like nodraw fog) */\r
3397                         if( si != NULL && (si->compileFlags & C_NODRAW) && ds->type != SURFACE_PATCH )\r
3398                                 continue;\r
3399                         \r
3400                         /* ydnar: bias the surface textures */\r
3401                         BiasSurfaceTextures( ds );\r
3402                         \r
3403                         /* ydnar: globalizing of fog volume handling (eek a hack) */\r
3404                         if( e != entities && si->noFog == qfalse )\r
3405                         {\r
3406                                 /* find surface origin and offset by entity origin */\r
3407                                 VectorAdd( ds->mins, ds->maxs, origin );\r
3408                                 VectorScale( origin, 0.5f, origin );\r
3409                                 VectorAdd( origin, e->origin, origin );\r
3410                                 \r
3411                                 VectorAdd( ds->mins, e->origin, mins );\r
3412                                 VectorAdd( ds->maxs, e->origin, maxs );\r
3413                                 \r
3414                                 /* set the fog number for this surface */\r
3415                                 ds->fogNum = FogForBounds( mins, maxs, 1.0f );  //%     FogForPoint( origin, 0.0f );\r
3416                         }\r
3417                 }\r
3418                 \r
3419                 /* ydnar: gs mods: handle the various types of surfaces */\r
3420                 switch( ds->type )\r
3421                 {\r
3422                         /* handle brush faces */\r
3423                         case SURFACE_FACE:\r
3424                         case SURFACE_DECAL:\r
3425                                 if( refs == 0 )\r
3426                                         refs = FilterFaceIntoTree( ds, tree );\r
3427                                 if( refs > 0 )\r
3428                                         EmitFaceSurface( ds );\r
3429                                 break;\r
3430                         \r
3431                         /* handle patches */\r
3432                         case SURFACE_PATCH:\r
3433                                 if( refs == 0 )\r
3434                                         refs = FilterPatchIntoTree( ds, tree );\r
3435                                 if( refs > 0 )\r
3436                                         EmitPatchSurface( ds );\r
3437                                 break;\r
3438                         \r
3439                         /* handle triangle surfaces */\r
3440                         case SURFACE_TRIANGLES:\r
3441                         case SURFACE_FORCED_META:\r
3442                         case SURFACE_META:\r
3443                                 //%     Sys_FPrintf( SYS_VRB, "Surface %4d: [%1d] %4d verts %s\n", numSurfs, ds->planar, ds->numVerts, si->shader );\r
3444                                 if( refs == 0 )\r
3445                                         refs = FilterTrianglesIntoTree( ds, tree );\r
3446                                 if( refs > 0 )\r
3447                                         EmitTriangleSurface( ds );\r
3448                                 break;\r
3449                         \r
3450                         /* handle foliage surfaces (splash damage/wolf et) */\r
3451                         case SURFACE_FOLIAGE:\r
3452                                 //%     Sys_FPrintf( SYS_VRB, "Surface %4d: [%d] %4d verts %s\n", numSurfs, ds->numFoliageInstances, ds->numVerts, si->shader );\r
3453                                 if( refs == 0 )\r
3454                                         refs = FilterFoliageIntoTree( ds, tree );\r
3455                                 if( refs > 0 )\r
3456                                         EmitTriangleSurface( ds );\r
3457                                 break;\r
3458                         \r
3459                         /* handle foghull surfaces */\r
3460                         case SURFACE_FOGHULL:\r
3461                                 if( refs == 0 )\r
3462                                         refs = AddReferenceToTree_r( ds, tree->headnode, qfalse );\r
3463                                 if( refs > 0 )\r
3464                                         EmitTriangleSurface( ds );\r
3465                                 break;\r
3466                         \r
3467                         /* handle flares */\r
3468                         case SURFACE_FLARE:\r
3469                                 if( refs == 0 )\r
3470                                         refs = FilterFlareSurfIntoTree( ds, tree );\r
3471                                 if( refs > 0 )\r
3472                                         EmitFlareSurface( ds );\r
3473                                 break;\r
3474                         \r
3475                         /* handle shader-only surfaces */\r
3476                         case SURFACE_SHADER:\r
3477                                 refs = 1;\r
3478                                 EmitFlareSurface( ds );\r
3479                                 break;\r
3480                         \r
3481                         /* no references */\r
3482                         default:\r
3483                                 refs = 0;\r
3484                                 break;\r
3485                 }\r
3486                 \r
3487                 /* tot up the references */\r
3488                 if( refs > 0 )\r
3489                 {\r
3490                         /* tot up counts */\r
3491                         numSurfs++;\r
3492                         numRefs += refs;\r
3493                         \r
3494                         /* emit extra surface data */\r
3495                         SetSurfaceExtra( ds, numBSPDrawSurfaces - 1 );\r
3496                         //%     Sys_FPrintf( SYS_VRB, "%d verts %d indexes\n", ds->numVerts, ds->numIndexes );\r
3497                         \r
3498                         /* one last sanity check */\r
3499                         {\r
3500                                 bspDrawSurface_t        *out;\r
3501                                 out = &bspDrawSurfaces[ numBSPDrawSurfaces - 1 ];\r
3502                                 if( out->numVerts == 3 && out->numIndexes > 3 )\r
3503                                 {\r
3504                                         Sys_Printf( "\nWARNING: Potentially bad %s surface (%d: %d, %d)\n     %s\n",\r
3505                                                 surfaceTypes[ ds->type ],\r
3506                                                 numBSPDrawSurfaces - 1, out->numVerts, out->numIndexes, si->shader );\r
3507                                 }\r
3508                         }\r
3509                         \r
3510                         /* ydnar: handle skybox surfaces */\r
3511                         if( ds->skybox )\r
3512                         {\r
3513                                 MakeSkyboxSurface( ds );\r
3514                                 numSkyboxSurfaces++;\r
3515                         }\r
3516                 }\r
3517         }\r
3518         \r
3519         /* emit some statistics */\r
3520         Sys_FPrintf( SYS_VRB, "%9d references\n", numRefs );\r
3521         Sys_FPrintf( SYS_VRB, "%9d (%d) emitted drawsurfs\n", numSurfs, numBSPDrawSurfaces );\r
3522         Sys_FPrintf( SYS_VRB, "%9d stripped face surfaces\n", numStripSurfaces );\r
3523         Sys_FPrintf( SYS_VRB, "%9d fanned face surfaces\n", numFanSurfaces );\r
3524         Sys_FPrintf( SYS_VRB, "%9d surface models generated\n", numSurfaceModels );\r
3525         Sys_FPrintf( SYS_VRB, "%9d skybox surfaces generated\n", numSkyboxSurfaces );\r
3526         for( i = 0; i < NUM_SURFACE_TYPES; i++ )\r
3527                 Sys_FPrintf( SYS_VRB, "%9d %s surfaces\n", numSurfacesByType[ i ], surfaceTypes[ i ] );\r
3528         \r
3529         Sys_FPrintf( SYS_VRB, "%9d redundant indexes supressed, saving %d Kbytes\n", numRedundantIndexes, (numRedundantIndexes * 4 / 1024) );\r
3530 }\r
3531 \r
3532 \r
3533 \r