2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\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
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
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
21 ----------------------------------------------------------------------------------
\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
26 ------------------------------------------------------------------------------- */
\r
42 this section handles drawsurface allocation and creation
\r
48 ydnar: gs mods: changed to force an explicit type when allocating
\r
51 mapDrawSurface_t *AllocDrawSurface( surfaceType_t type )
\r
53 mapDrawSurface_t *ds;
\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
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
66 /* ydnar: do initial surface setup */
\r
67 memset( ds, 0, sizeof( mapDrawSurface_t ) );
\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
81 ydnar: general surface finish pass
\r
84 void FinishSurface( mapDrawSurface_t *ds )
\r
87 if( ds == NULL || ds->shaderInfo == NULL )
\r
90 /* ydnar: rocking tek-fu celshading */
\r
91 if( ds->celShader != NULL )
\r
92 MakeCelSurface( ds, ds->celShader );
\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
103 clones a map drawsurface, using the specified shader
\r
106 mapDrawSurface_t *CloneSurface( mapDrawSurface_t *src, shaderInfo_t *si )
\r
108 mapDrawSurface_t *ds;
\r
112 if( src == NULL || si == NULL )
\r
115 /* allocate a new surface */
\r
116 ds = AllocDrawSurface( src->type );
\r
121 memcpy( ds, src, sizeof( *ds ) );
\r
123 /* destroy side reference */
\r
124 ds->sideRef = NULL;
\r
127 ds->shaderInfo = si;
\r
130 if( ds->numVerts > 0 )
\r
132 ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );
\r
133 memcpy( ds->verts, src->verts, ds->numVerts * sizeof( *ds->verts ) );
\r
137 if( ds->numIndexes <= 0 )
\r
139 ds->indexes = safe_malloc( ds->numIndexes * sizeof( *ds->indexes ) );
\r
140 memcpy( ds->indexes, src->indexes, ds->numIndexes * sizeof( *ds->indexes ) );
\r
142 /* return the surface */
\r
149 MakeCelSurface() - ydnar
\r
150 makes a copy of a surface, but specific to cel shading
\r
153 mapDrawSurface_t *MakeCelSurface( mapDrawSurface_t *src, shaderInfo_t *si )
\r
155 mapDrawSurface_t *ds;
\r
159 if( src == NULL || si == NULL )
\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
168 ds = CloneSurface( src, si );
\r
172 /* do some fixups for celshading */
\r
173 ds->planar = qfalse;
\r
176 /* return the surface */
\r
183 MakeSkyboxSurface() - ydnar
\r
184 generates a skybox surface, viewable from everywhere there is sky
\r
187 mapDrawSurface_t *MakeSkyboxSurface( mapDrawSurface_t *src )
\r
190 mapDrawSurface_t *ds;
\r
198 ds = CloneSurface( src, src->shaderInfo );
\r
205 /* scale the surface vertexes */
\r
206 for( i = 0; i < ds->numVerts; i++ )
\r
208 m4x4_transform_point( skyboxTransform, ds->verts[ i ].xyz );
\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
215 /* so backface culling creep doesn't bork the surface */
\r
216 VectorClear( ds->lightmapVecs[ 2 ] );
\r
218 /* return the surface */
\r
225 IsTriangleDegenerate
\r
226 returns qtrue if all three points are colinear, backwards, or the triangle is just plain bogus
\r
229 #define TINY_AREA 1.0f
\r
231 qboolean IsTriangleDegenerate( bspDrawVert_t *points, int a, int b, int c )
\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
243 /* assume all very small or backwards triangles will cause problems */
\r
244 if( d < TINY_AREA )
\r
247 /* must be a good triangle */
\r
254 ClearSurface() - ydnar
\r
255 clears a surface and frees any allocated memory
\r
258 void ClearSurface( mapDrawSurface_t *ds )
\r
260 ds->type = SURFACE_BAD;
\r
261 ds->planar = qfalse;
\r
264 if( 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
277 TidyEntitySurfaces() - ydnar
\r
278 deletes all empty or bad surfaces from the surface list
\r
281 void TidyEntitySurfaces( entity_t *e )
\r
284 mapDrawSurface_t *out, *in;
\r
288 Sys_FPrintf( SYS_VRB, "--- TidyEntitySurfaces ---\n" );
\r
290 /* walk the surface list */
\r
292 for( i = e->firstDrawSurf, j = e->firstDrawSurf; j < numMapDrawSurfs; i++, j++ )
\r
294 /* get out surface */
\r
295 out = &mapDrawSurfs[ i ];
\r
297 /* walk the surface list again until a proper surface is found */
\r
298 for( j; j < numMapDrawSurfs; j++ )
\r
300 /* get in surface */
\r
301 in = &mapDrawSurfs[ j ];
\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
309 ClearSurface( in );
\r
313 /* copy if necessary */
\r
315 memcpy( out, in, sizeof( mapDrawSurface_t ) );
\r
318 /* set the new number of drawsurfs */
\r
319 numMapDrawSurfs = i;
\r
321 /* emit some stats */
\r
322 Sys_FPrintf( SYS_VRB, "%9d empty or malformed surfaces deleted\n", deleted );
\r
328 CalcSurfaceTextureRange() - ydnar
\r
329 calculates the clamped texture range for a given surface, returns qtrue if it's within [-texRange,texRange]
\r
332 qboolean CalcSurfaceTextureRange( mapDrawSurface_t *ds )
\r
334 int i, j, v, size[ 2 ];
\r
335 float mins[ 2 ], maxs[ 2 ];
\r
338 /* try to early out */
\r
339 if( ds->numVerts <= 0 )
\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
349 for( j = 0; j < 2; j++ )
\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
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
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
371 for( j = 0; j < 2; j++ )
\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
382 for( j = 0; j < 2; j++ )
\r
383 ds->texRange[ j ] = (ds->texMaxs[ j ] - ds->texMins[ j ]);
\r
385 /* if range is zero, then assume unlimited precision */
\r
386 if( texRange == 0 )
\r
389 /* within range? */
\r
390 for( j = 0; j < 2; j++ )
\r
392 if( ds->texMins[ j ] < -texRange || ds->texMaxs[ j ] > texRange )
\r
403 CalcLightmapAxis() - ydnar
\r
404 gives closed lightmap axis for a plane normal
\r
407 qboolean CalcLightmapAxis( vec3_t normal, vec3_t axis )
\r
413 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && normal[ 2 ] == 0.0f )
\r
415 VectorClear( axis );
\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
425 if( absolute[ 2 ] > absolute[ 0 ] - 0.0001f && absolute[ 2 ] > absolute[ 1 ] - 0.0001f )
\r
427 if( normal[ 2 ] > 0.0f )
\r
428 VectorSet( axis, 0.0f, 0.0f, 1.0f );
\r
430 VectorSet( axis, 0.0f, 0.0f, -1.0f );
\r
432 else if( absolute[ 0 ] > absolute[ 1 ] - 0.0001f && absolute[ 0 ] > absolute[ 2 ] - 0.0001f )
\r
434 if( normal[ 0 ] > 0.0f )
\r
435 VectorSet( axis, 1.0f, 0.0f, 0.0f );
\r
437 VectorSet( axis, -1.0f, 0.0f, 0.0f );
\r
441 if( normal[ 1 ] > 0.0f )
\r
442 VectorSet( axis, 0.0f, 1.0f, 0.0f );
\r
444 VectorSet( axis, 0.0f, -1.0f, 0.0f );
\r
454 ClassifySurfaces() - ydnar
\r
455 fills out a bunch of info in the surfaces, including planar status, lightmap projection, and bounding box
\r
458 #define PLANAR_EPSILON 0.5f //% 0.126f 0.25f
\r
460 void ClassifySurfaces( int numSurfs, mapDrawSurface_t *ds )
\r
466 static vec3_t axii[ 6 ] =
\r
477 /* walk the list of surfaces */
\r
478 for( numSurfs; numSurfs > 0; numSurfs--, ds++ )
\r
480 /* ignore bogus (or flare) surfaces */
\r
481 if( ds->type == SURFACE_BAD || ds->numVerts <= 0 )
\r
485 si = ds->shaderInfo;
\r
487 /* -----------------------------------------------------------------
\r
488 force meta if vertex count is too high or shader requires it
\r
489 ----------------------------------------------------------------- */
\r
491 if( ds->type != SURFACE_PATCH && ds->type != SURFACE_FACE )
\r
493 if( ds->numVerts > SHADER_MAX_VERTEXES )
\r
494 ds->type = SURFACE_FORCED_META;
\r
497 /* -----------------------------------------------------------------
\r
498 plane and bounding box classification
\r
499 ----------------------------------------------------------------- */
\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
506 /* try to get an existing plane */
\r
507 if( ds->planeNum >= 0 )
\r
509 VectorCopy( mapplanes[ ds->planeNum ].normal, plane );
\r
510 plane[ 3 ] = mapplanes[ ds->planeNum ].dist;
\r
513 /* construct one from the first vert with a valid normal */
\r
516 VectorClear( plane );
\r
518 for( i = 0; i < ds->numVerts; i++ )
\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
522 VectorCopy( ds->verts[ i ].normal, plane );
\r
523 plane[ 3 ] = DotProduct( ds->verts[ i ].xyz, plane );
\r
529 /* test for bogus plane */
\r
530 if( VectorLength( plane ) <= 0.0f )
\r
532 ds->planar = qfalse;
\r
537 /* determine if surface is planar */
\r
538 ds->planar = qtrue;
\r
540 /* test each vert */
\r
541 for( i = 0; i < ds->numVerts; i++ )
\r
543 /* point-plane test */
\r
544 dist = DotProduct( ds->verts[ i ].xyz, plane ) - plane[ 3 ];
\r
545 if( fabs( dist ) > PLANAR_EPSILON )
\r
547 //% if( ds->planeNum >= 0 )
\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
552 ds->planar = qfalse;
\r
558 /* find map plane if necessary */
\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
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
573 /* -----------------------------------------------------------------
\r
574 lightmap bounds and axis projection
\r
575 ----------------------------------------------------------------- */
\r
577 /* vertex lit surfaces don't need this information */
\r
578 if( si->compileFlags & C_VERTEXLIT || ds->type == SURFACE_TRIANGLES )
\r
580 VectorClear( ds->lightmapAxis );
\r
581 //% VectorClear( ds->lightmapVecs[ 2 ] );
\r
582 ds->sampleSize = 0;
\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
595 /* find best lightmap axis */
\r
596 for( bestAxis = 0; bestAxis < 6; bestAxis++ )
\r
598 for( i = 0; i < ds->numVerts && bestAxis < 6; i++ )
\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
607 if( i == ds->numVerts )
\r
611 /* set axis if possible */
\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
620 //% if( ds->type == SURFACE_PATCH )
\r
621 //% Sys_Printf( "Failed to map axis %d onto patch\n", bestAxis );
\r
624 /* get lightmap sample size */
\r
625 if( ds->sampleSize <= 0 )
\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
643 ClassifyEntitySurfaces() - ydnar
\r
644 classifies all surfaces in an entity
\r
647 void ClassifyEntitySurfaces( entity_t *e )
\r
653 Sys_FPrintf( SYS_VRB, "--- ClassifyEntitySurfaces ---\n" );
\r
655 /* walk the surface list */
\r
656 for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
\r
657 ClassifySurfaces( 1, &mapDrawSurfs[ i ] );
\r
659 /* tidy things up */
\r
660 TidyEntitySurfaces( e );
\r
666 GetShaderIndexForPoint() - ydnar
\r
667 for shader-indexed surfaces (terrain), find a matching index from the indexmap
\r
670 byte GetShaderIndexForPoint( indexMap_t *im, vec3_t eMins, vec3_t eMaxs, vec3_t point )
\r
674 vec3_t mins, maxs, size;
\r
677 /* early out if no indexmap */
\r
681 /* this code is really broken */
\r
683 /* legacy precision fudges for terrain */
\r
684 for( i = 0; i < 3; i++ )
\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
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
696 else if( s > 1.0f )
\r
700 else if( t > 1.0f )
\r
704 x = (im->w - 1) * s;
\r
705 y = (im->h - 1) * t;
\r
708 for( i = 0; i < 3; i++ )
\r
710 mins[ i ] = eMins[ i ];
\r
711 maxs[ i ] = eMaxs[ i ];
\r
712 size[ i ] = maxs[ i ] - mins[ i ];
\r
716 s = (point[ 0 ] - mins[ 0 ]) / size[ 0 ];
\r
717 t = (maxs[ 1 ] - point[ 1 ]) / size[ 1 ];
\r
724 else if( x > (im->w - 1) )
\r
728 else if( y > (im->h - 1) )
\r
733 return im->pixels[ y * im->w + x ];
\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
744 shaderInfo_t *GetIndexedShader( shaderInfo_t *parent, indexMap_t *im, int numPoints, byte *shaderIndexes )
\r
747 byte minShaderIndex, maxShaderIndex;
\r
748 char shader[ MAX_QPATH ];
\r
752 /* early out if bad data */
\r
753 if( im == NULL || numPoints <= 0 || shaderIndexes == NULL )
\r
754 return ShaderInfoForShader( "default" );
\r
756 /* determine min/max index */
\r
757 minShaderIndex = 255;
\r
758 maxShaderIndex = 0;
\r
759 for( i = 0; i < numPoints; i++ )
\r
761 if( shaderIndexes[ i ] < minShaderIndex )
\r
762 minShaderIndex = shaderIndexes[ i ];
\r
763 if( shaderIndexes[ i ] > maxShaderIndex )
\r
764 maxShaderIndex = shaderIndexes[ i ];
\r
767 /* set alpha inline */
\r
768 for( i = 0; i < numPoints; i++ )
\r
770 /* straight rip from terrain.c */
\r
771 if( shaderIndexes[ i ] < maxShaderIndex )
\r
772 shaderIndexes[ i ] = 0;
\r
774 shaderIndexes[ i ] = 255;
\r
777 /* make a shader name */
\r
778 if( minShaderIndex == maxShaderIndex )
\r
779 sprintf( shader, "textures/%s_%d", im->shader, maxShaderIndex );
\r
781 sprintf( shader, "textures/%s_%dto%d", im->shader, minShaderIndex, maxShaderIndex );
\r
783 /* get the shader */
\r
784 si = ShaderInfoForShader( shader );
\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
797 /* set xy texture projection */
\r
799 VectorCopy( parent->vecs[ 0 ], si->vecs[ 0 ] );
\r
800 VectorCopy( parent->vecs[ 1 ], si->vecs[ 1 ] );
\r
802 if( VectorLength( parent->lightmapAxis ) > 0.0f && VectorLength( si->lightmapAxis ) <= 0.0f )
\r
804 /* set lightmap projection axis */
\r
805 VectorCopy( parent->lightmapAxis, si->lightmapAxis );
\r
808 /* return the shader */
\r
816 DrawSurfaceForSide()
\r
817 creates a SURF_FACE drawsurface from a given brush side and winding
\r
820 #define SNAP_FLOAT_TO_INT 8
\r
821 #define SNAP_INT_TO_FLOAT (1.0 / SNAP_FLOAT_TO_INT)
\r
823 mapDrawSurface_t *DrawSurfaceForSide( entity_t *e, brush_t *b, side_t *s, winding_t *w )
\r
826 mapDrawSurface_t *ds;
\r
827 shaderInfo_t *si, *parent;
\r
831 vec3_t vTranslated;
\r
833 byte shaderIndexes[ 256 ];
\r
834 float offsets[ 256 ];
\r
835 char tempShader[ MAX_QPATH ];
\r
838 /* ydnar: don't make a drawsurf for culled sides */
\r
843 if( w->numpoints > MAX_POINTS_ON_WINDING )
\r
844 Error( "DrawSurfaceForSide: w->numpoints = %d (> %d)", w->numpoints, MAX_POINTS_ON_WINDING );
\r
847 si = s->shaderInfo;
\r
849 /* ydnar: gs mods: check for indexed shader */
\r
850 if( si->indexed && b->im != NULL )
\r
855 /* get shader indexes for each point */
\r
856 for( i = 0; i < w->numpoints; i++ )
\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
863 /* get matching shader and set alpha */
\r
865 si = GetIndexedShader( parent, b->im, w->numpoints, shaderIndexes );
\r
870 /* ydnar: sky hack/fix for GL_CLAMP borders on ati cards */
\r
871 if( skyFixHack && si->skyParmsImageBase[ 0 ] != '\0' )
\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
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
894 ds->planar = qtrue;
\r
895 ds->planeNum = s->planenum;
\r
896 VectorCopy( mapplanes[ s->planenum ].normal, ds->lightmapVecs[ 2 ] );
\r
898 ds->shaderInfo = si;
\r
900 ds->sideRef = AllocSideRef( s, NULL );
\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
907 /* compute s/t coordinates from brush primitive texture matrix (compute axis base) */
\r
908 ComputeAxisBase( mapplanes[ s->planenum ].normal, texX, texY );
\r
910 /* create the vertexes */
\r
911 for( j = 0; j < w->numpoints; j++ )
\r
913 /* get the drawvert */
\r
914 dv = ds->verts + j;
\r
916 /* copy xyz and do potential z offset */
\r
917 VectorCopy( w->p[ j ], dv->xyz );
\r
919 dv->xyz[ 2 ] += offsets[ j ];
\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
926 /* ydnar: tek-fu celshading support for flat shaded shit */
\r
929 dv->st[ 0 ] = si->stFlat[ 0 ];
\r
930 dv->st[ 1 ] = si->stFlat[ 1 ];
\r
933 /* ydnar: gs mods: added support for explicit shader texcoord generation */
\r
934 else if( si->tcGen )
\r
936 dv->st[ 0 ] = DotProduct( si->vecs[ 0 ], vTranslated );
\r
937 dv->st[ 1 ] = DotProduct( si->vecs[ 1 ], vTranslated );
\r
940 /* old quake-style texturing */
\r
941 else if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
\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
950 /* brush primitive texturing */
\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
961 VectorCopy( mapplanes[ s->planenum ].normal, dv->normal );
\r
963 /* ydnar: set color */
\r
964 for( k = 0; k < MAX_LIGHTMAPS; k++ )
\r
966 dv->color[ k ][ 0 ] = 255;
\r
967 dv->color[ k ][ 1 ] = 255;
\r
968 dv->color[ k ][ 2 ] = 255;
\r
970 /* ydnar: gs mods: handle indexed shader blending */
\r
971 dv->color[ k ][ 3 ] = (indexed ? shaderIndexes[ j ] : 255);
\r
975 /* set cel shader */
\r
976 ds->celShader = b->celShader;
\r
978 /* finish surface */
\r
979 FinishSurface( ds );
\r
981 /* ydnar: gs mods: moved st biasing elsewhere */
\r
988 DrawSurfaceForMesh()
\r
989 moved here from patch.c
\r
992 #define YDNAR_NORMAL_EPSILON 0.50f
\r
994 qboolean VectorCompareExt( vec3_t n1, vec3_t n2, float epsilon )
\r
1000 for( i= 0; i < 3; i++ )
\r
1001 if( fabs( n1[ i ] - n2[ i ]) > epsilon )
\r
1006 mapDrawSurface_t *DrawSurfaceForMesh( entity_t *e, parseMesh_t *p, mesh_t *mesh )
\r
1008 int i, k, numVerts;
\r
1012 mapDrawSurface_t *ds;
\r
1013 shaderInfo_t *si, *parent;
\r
1014 bspDrawVert_t *dv;
\r
1015 vec3_t vTranslated;
\r
1018 byte shaderIndexes[ MAX_EXPANDED_AXIS * MAX_EXPANDED_AXIS ];
\r
1019 float offsets[ MAX_EXPANDED_AXIS * MAX_EXPANDED_AXIS ];
\r
1022 /* get mesh and shader shader */
\r
1023 if( mesh == NULL )
\r
1025 si = p->shaderInfo;
\r
1026 if( mesh == NULL || si == NULL )
\r
1029 /* get vertex count */
\r
1030 numVerts = mesh->width * mesh->height;
\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
1036 /* create a copy of the mesh */
\r
1037 copy = CopyMesh( mesh );
\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
1044 /* put the mesh on the curve */
\r
1045 PutMeshOnCurve( *copy );
\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
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
1056 /* free the old mesh */
\r
1059 /* ydnar: gs mods: check for indexed shader */
\r
1060 if( si->indexed && p->im != NULL )
\r
1065 /* get shader indexes for each point */
\r
1066 for( i = 0; i < numVerts; i++ )
\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
1072 /* get matching shader and set alpha */
\r
1074 si = GetIndexedShader( parent, p->im, numVerts, shaderIndexes );
\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
1086 ds->shaderInfo = si;
\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
1096 ds->planeNum = -1;
\r
1098 ds->longestCurve = p->longestCurve;
\r
1099 ds->maxIterations = p->maxIterations;
\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
1106 /* spew forth errors */
\r
1107 if( VectorLength( plane ) < 0.001f )
\r
1108 Sys_Printf( "BOGUS " );
\r
1110 /* test each vert */
\r
1111 for( i = 1; i < ds->numVerts && planar; i++ )
\r
1114 if( VectorCompare( plane, mesh->verts[ i ].normal ) == qfalse )
\r
1117 /* point-plane test */
\r
1118 dist = DotProduct( mesh->verts[ i ].xyz, plane ) - plane[ 3 ];
\r
1119 if( fabs( dist ) > EQUAL_EPSILON )
\r
1123 /* add a map plane */
\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
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
1135 /* walk the verts to do special stuff */
\r
1136 for( i = 0; i < ds->numVerts; i++ )
\r
1138 /* get the drawvert */
\r
1139 dv = &ds->verts[ i ];
\r
1141 /* ydnar: tek-fu celshading support for flat shaded shit */
\r
1144 dv->st[ 0 ] = si->stFlat[ 0 ];
\r
1145 dv->st[ 1 ] = si->stFlat[ 1 ];
\r
1148 /* ydnar: gs mods: added support for explicit shader texcoord generation */
\r
1149 else if( si->tcGen )
\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
1157 /* ydnar: set color */
\r
1158 for( k = 0; k < MAX_LIGHTMAPS; k++ )
\r
1160 dv->color[ k ][ 0 ] = 255;
\r
1161 dv->color[ k ][ 1 ] = 255;
\r
1162 dv->color[ k ][ 2 ] = 255;
\r
1164 /* ydnar: gs mods: handle indexed shader blending */
\r
1165 dv->color[ k ][ 3 ] = (indexed ? shaderIndexes[ i ] : 255);
\r
1168 /* ydnar: offset */
\r
1170 dv->xyz[ 2 ] += offsets[ i ];
\r
1173 /* set cel shader */
\r
1174 ds->celShader = p->celShader;
\r
1176 /* finish surface */
\r
1177 FinishSurface( ds );
\r
1179 /* return the drawsurface */
\r
1186 DrawSurfaceForFlare() - ydnar
\r
1187 creates a flare draw surface
\r
1190 mapDrawSurface_t *DrawSurfaceForFlare( int entNum, vec3_t origin, vec3_t normal, vec3_t color, char *flareShader, int lightStyle )
\r
1192 mapDrawSurface_t *ds;
\r
1195 /* emit flares? */
\r
1196 if( emitFlares == qfalse )
\r
1199 /* allocate drawsurface */
\r
1200 ds = AllocDrawSurface( SURFACE_FLARE );
\r
1201 ds->entityNum = entNum;
\r
1204 if( flareShader != NULL && flareShader[ 0 ] != '\0' )
\r
1205 ds->shaderInfo = ShaderInfoForShader( flareShader );
\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
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
1222 /* return to sender */
\r
1229 DrawSurfaceForShader() - ydnar
\r
1230 creates a bogus surface to forcing the game to load a shader
\r
1233 mapDrawSurface_t *DrawSurfaceForShader( char *shader )
\r
1237 mapDrawSurface_t *ds;
\r
1241 si = ShaderInfoForShader( shader );
\r
1243 /* find existing surface */
\r
1244 for( i = 0; i < numMapDrawSurfs; i++ )
\r
1247 ds = &mapDrawSurfs[ i ];
\r
1250 if( ds->shaderInfo == si )
\r
1254 /* create a new surface */
\r
1255 ds = AllocDrawSurface( SURFACE_SHADER );
\r
1256 ds->entityNum = 0;
\r
1257 ds->shaderInfo = ShaderInfoForShader( shader );
\r
1259 /* return to sender */
\r
1266 AddSurfaceFlare() - ydnar
\r
1267 creates flares (coronas) centered on surfaces
\r
1270 static void AddSurfaceFlare( mapDrawSurface_t *ds, vec3_t entityOrigin )
\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
1284 /* push origin off surface a bit */
\r
1285 VectorMA( origin, 2.0f, ds->lightmapVecs[ 2 ], origin );
\r
1287 /* create the drawsurface */
\r
1288 DrawSurfaceForFlare( ds->entityNum, origin, ds->lightmapVecs[ 2 ], ds->shaderInfo->color, ds->shaderInfo->flareShader, ds->shaderInfo->lightStyle );
\r
1295 subdivides a face surface until it is smaller than the specified size (subdivisions)
\r
1298 static void SubdivideFace( entity_t *e, brush_t *brush, side_t *side, winding_t *w, int fogNum, float subdivisions )
\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
1312 if( w->numpoints < 3 )
\r
1313 Error( "SubdivideFaceSurface: Bad w->numpoints" );
\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
1320 /* split the face */
\r
1321 for( axis = 0; axis < 3; axis++ )
\r
1323 vec3_t planePoint = { 0, 0, 0 };
\r
1324 vec3_t planeNormal = { 0, 0, 0 };
\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
1335 /* subdivide if necessary */
\r
1336 if( (subCeil - subFloor) > subdivisions )
\r
1338 /* clip the winding */
\r
1339 ClipWindingEpsilon( w, planeNormal, d, epsilon, &frontWinding, &backWinding );
\r
1341 /* the clip may not produce two polygons if it was epsilon close */
\r
1342 if( frontWinding == NULL )
\r
1344 else if( backWinding == NULL )
\r
1348 SubdivideFace( e, brush, side, frontWinding, fogNum, subdivisions );
\r
1349 SubdivideFace( e, brush, side, backWinding, fogNum, subdivisions );
\r
1355 /* create a face surface */
\r
1356 ds = DrawSurfaceForSide( e, brush, side, w );
\r
1358 /* set correct fog num */
\r
1359 ds->fogNum = fogNum;
\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
1370 void SubdivideFaceSurfaces( entity_t *e, tree_t *tree )
\r
1372 int i, j, numBaseDrawSurfs, fogNum;
\r
1373 mapDrawSurface_t *ds;
\r
1378 float range, size, subdivisions, s2;
\r
1382 Sys_FPrintf( SYS_VRB, "--- SubdivideFaceSurfaces ---\n" );
\r
1384 /* walk the list of surfaces */
\r
1385 numBaseDrawSurfs = numMapDrawSurfs;
\r
1386 for( i = e->firstDrawSurf; i < numBaseDrawSurfs; i++ )
\r
1389 ds = &mapDrawSurfs[ i ];
\r
1391 /* only subdivide brush sides */
\r
1392 if( ds->type != SURFACE_FACE || ds->mapBrush == NULL || ds->sideRef == NULL || ds->sideRef->side == NULL )
\r
1396 brush = ds->mapBrush;
\r
1397 side = ds->sideRef->side;
\r
1399 /* check subdivision for shader */
\r
1400 si = side->shaderInfo;
\r
1404 /* ydnar: don't subdivide sky surfaces */
\r
1405 if( si->compileFlags & C_SKY )
\r
1408 /* do texture coordinate range check */
\r
1409 ClassifySurfaces( 1, ds );
\r
1410 if( CalcSurfaceTextureRange( ds ) == qfalse )
\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
1422 s2 = ceil( (float) texRange / j );
\r
1423 if( fabs( subdivisions - s2 ) <= 4.0 )
\r
1425 subdivisions = s2;
\r
1431 subdivisions = si->subdivisions;
\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
1439 /* preserve fog num */
\r
1440 fogNum = ds->fogNum;
\r
1442 /* make a winding and free the surface */
\r
1443 w = WindingFromDrawSurf( ds );
\r
1444 ClearSurface( ds );
\r
1446 /* subdivide it */
\r
1447 SubdivideFace( e, brush, side, w, fogNum, subdivisions );
\r
1454 ====================
\r
1455 ClipSideIntoTree_r
\r
1457 Adds non-opaque leaf fragments to the convex hull
\r
1458 ====================
\r
1461 void ClipSideIntoTree_r( winding_t *w, side_t *side, node_t *node )
\r
1464 winding_t *front, *back;
\r
1470 if ( node->planenum != PLANENUM_LEAF ) {
\r
1471 if ( side->planenum == node->planenum ) {
\r
1472 ClipSideIntoTree_r( w, side, node->children[0] );
\r
1475 if ( side->planenum == ( node->planenum ^ 1) ) {
\r
1476 ClipSideIntoTree_r( w, side, node->children[1] );
\r
1480 plane = &mapplanes[ node->planenum ];
\r
1481 ClipWindingEpsilon ( w, plane->normal, plane->dist,
\r
1482 ON_EPSILON, &front, &back );
\r
1485 ClipSideIntoTree_r( front, side, node->children[0] );
\r
1486 ClipSideIntoTree_r( back, side, node->children[1] );
\r
1491 // if opaque leaf, don't add
\r
1492 if ( !node->opaque ) {
\r
1493 AddWindingToConvexHull( w, &side->visibleHull, mapplanes[ side->planenum ].normal );
\r
1504 static int g_numHiddenFaces, g_numCoinFaces;
\r
1509 CullVectorCompare() - ydnar
\r
1510 compares two vectors with an epsilon
\r
1513 #define CULL_EPSILON 0.1f
\r
1515 qboolean CullVectorCompare( const vec3_t v1, const vec3_t v2 )
\r
1520 for( i = 0; i < 3; i++ )
\r
1521 if( fabs( v1[ i ] - v2[ i ] ) > CULL_EPSILON )
\r
1529 SideInBrush() - ydnar
\r
1530 determines if a brushside lies inside another brush
\r
1533 qboolean SideInBrush( side_t *side, brush_t *b )
\r
1539 /* ignore sides w/o windings or shaders */
\r
1540 if( side->winding == NULL || side->shaderInfo == NULL )
\r
1543 /* ignore culled sides and translucent brushes */
\r
1544 if( side->culled == qtrue || (b->compileFlags & C_TRANSLUCENT) )
\r
1547 /* side iterator */
\r
1548 for( i = 0; i < b->numsides; i++ )
\r
1550 /* fail if any sides are caulk */
\r
1551 if( b->sides[ i ].compileFlags & C_NODRAW )
\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
1561 /* don't cull autosprite or polygonoffset surfaces */
\r
1562 if( side->shaderInfo )
\r
1564 if( side->shaderInfo->autosprite || side->shaderInfo->polygonOffset )
\r
1569 side->culled = qtrue;
\r
1570 g_numHiddenFaces++;
\r
1576 CullSides() - ydnar
\r
1577 culls obscured or buried brushsides from the map
\r
1580 void CullSides( entity_t *e )
\r
1583 int i, j, k, l, first, second, dir;
\r
1584 winding_t *w1, *w2;
\r
1586 side_t *side1, *side2;
\r
1590 Sys_FPrintf( SYS_VRB, "--- CullSides ---\n" );
\r
1592 g_numHiddenFaces = 0;
\r
1593 g_numCoinFaces = 0;
\r
1595 /* brush interator 1 */
\r
1596 for( b1 = e->brushes; b1; b1 = b1->next )
\r
1599 if( b1->numsides < 1 )
\r
1602 /* brush iterator 2 */
\r
1603 for( b2 = b1->next; b2; b2 = b2->next )
\r
1606 if( b2->numsides < 1 )
\r
1609 /* original check */
\r
1610 if( b1->original == b2->original && b1->original != NULL )
\r
1615 for( i = 0; i < 3; i++ )
\r
1616 if( b1->mins[ i ] > b2->maxs[ i ] || b1->maxs[ i ] < b2->mins[ i ] )
\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
1627 /* side iterator 1 */
\r
1628 for( i = 0; i < b1->numsides; i++ )
\r
1630 /* winding check */
\r
1631 side1 = &b1->sides[ i ];
\r
1632 w1 = side1->winding;
\r
1635 numPoints = w1->numpoints;
\r
1636 if( side1->shaderInfo == NULL )
\r
1639 /* side iterator 2 */
\r
1640 for( j = 0; j < b2->numsides; j++ )
\r
1642 /* winding check */
\r
1643 side2 = &b2->sides[ j ];
\r
1644 w2 = side2->winding;
\r
1647 if( side2->shaderInfo == NULL )
\r
1649 if( w1->numpoints != w2->numpoints )
\r
1651 if( side1->culled == qtrue && side2->culled == qtrue )
\r
1654 /* compare planes */
\r
1655 if( (side1->planenum & ~0x00000001) != (side2->planenum & ~0x00000001) )
\r
1658 /* get autosprite and polygonoffset status */
\r
1659 if( side1->shaderInfo &&
\r
1660 (side1->shaderInfo->autosprite || side1->shaderInfo->polygonOffset) )
\r
1662 if( side2->shaderInfo &&
\r
1663 (side2->shaderInfo->autosprite || side2->shaderInfo->polygonOffset) )
\r
1666 /* find first common point */
\r
1668 for( k = 0; k < numPoints; k++ )
\r
1670 if( VectorCompare( w1->p[ 0 ], w2->p[ k ] ) )
\r
1679 /* find second common point (regardless of winding order) */
\r
1682 if( (first + 1) < numPoints )
\r
1683 second = first + 1;
\r
1686 if( CullVectorCompare( w1->p[ 1 ], w2->p[ second ] ) )
\r
1691 second = first - 1;
\r
1693 second = numPoints - 1;
\r
1694 if( CullVectorCompare( w1->p[ 1 ], w2->p[ second ] ) )
\r
1700 /* compare the rest of the points */
\r
1702 for( k = 0; k < numPoints; k++ )
\r
1704 if( !CullVectorCompare( w1->p[ k ], w2->p[ l ] ) )
\r
1709 l = numPoints - 1;
\r
1710 else if( l >= numPoints )
\r
1717 if( !side2->culled && !(side2->compileFlags & C_TRANSLUCENT) && !(side2->compileFlags & C_NODRAW) )
\r
1719 side1->culled = qtrue;
\r
1723 if( side1->planenum == side2->planenum && side1->culled == qtrue )
\r
1727 if( !side1->culled && !(side1->compileFlags & C_TRANSLUCENT) && !(side1->compileFlags & C_NODRAW) )
\r
1729 side2->culled = qtrue;
\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
1746 ClipSidesIntoTree()
\r
1748 creates side->visibleHull for all visible sides
\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
1755 void ClipSidesIntoTree( entity_t *e, tree_t *tree )
\r
1760 side_t *side, *newSide;
\r
1764 /* ydnar: cull brush sides */
\r
1768 Sys_FPrintf( SYS_VRB, "--- ClipSidesIntoTree ---\n" );
\r
1770 /* walk the brush list */
\r
1771 for( b = e->brushes; b; b = b->next )
\r
1773 /* walk the brush sides */
\r
1774 for( i = 0; i < b->numsides; i++ )
\r
1777 side = &b->sides[ i ];
\r
1778 if( side->winding == NULL )
\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
1786 /* anything left? */
\r
1787 w = side->visibleHull;
\r
1792 si = side->shaderInfo;
\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
1801 /* always use the original winding for autosprites and noclip faces */
\r
1802 if( si->autosprite || si->noClip )
\r
1803 w = side->winding;
\r
1805 /* save this winding as a visible surface */
\r
1806 DrawSurfaceForSide( e, b, side, w );
\r
1808 /* make a back side for fog */
\r
1809 if( !(si->compileFlags & C_FOG) )
\r
1812 /* duplicate the up-facing side */
\r
1813 w = ReverseWinding( w );
\r
1814 newSide = safe_malloc( sizeof( *side ) );
\r
1816 newSide->visibleHull = w;
\r
1817 newSide->planenum ^= 1;
\r
1819 /* save this winding as a visible surface */
\r
1820 DrawSurfaceForSide( e, b, newSide, w );
\r
1829 this section deals with filtering drawsurfaces into the bsp tree,
\r
1830 adding references to each leaf a surface touches
\r
1835 AddReferenceToLeaf() - ydnar
\r
1836 adds a reference to surface ds in the bsp leaf node
\r
1839 int AddReferenceToLeaf( mapDrawSurface_t *ds, node_t *node )
\r
1841 drawSurfRef_t *dsr;
\r
1845 if( node->planenum != PLANENUM_LEAF || node->opaque )
\r
1848 /* try to find an existing reference */
\r
1849 for( dsr = node->drawSurfReferences; dsr; dsr = dsr->nextRef )
\r
1851 if( dsr->outputNum == numBSPDrawSurfaces )
\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
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
1874 AddReferenceToTree_r() - ydnar
\r
1875 adds a reference to the specified drawsurface to every leaf in the tree
\r
1878 int AddReferenceToTree_r( mapDrawSurface_t *ds, node_t *node, qboolean skybox )
\r
1884 if( node == NULL )
\r
1887 /* is this a decision node? */
\r
1888 if( node->planenum != PLANENUM_LEAF )
\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
1899 /* skybox surfaces only get added to sky leaves */
\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
1908 /* add a reference */
\r
1909 return AddReferenceToLeaf( ds, node );
\r
1915 FilterPointIntoTree_r() - ydnar
\r
1916 filters a single point from a surface into the tree
\r
1919 int FilterPointIntoTree_r( vec3_t point, mapDrawSurface_t *ds, node_t *node )
\r
1926 /* is this a decision node? */
\r
1927 if( node->planenum != PLANENUM_LEAF )
\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
1933 /* filter by this plane */
\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
1944 /* add a reference */
\r
1945 return AddReferenceToLeaf( ds, node );
\r
1951 FilterWindingIntoTree_r() - ydnar
\r
1952 filters a winding from a drawsurface into the tree
\r
1955 int FilterWindingIntoTree_r( winding_t *w, mapDrawSurface_t *ds, node_t *node )
\r
1959 vec4_t plane1, plane2, reverse;
\r
1960 winding_t *fat, *front, *back;
\r
1964 /* get shaderinfo */
\r
1965 si = ds->shaderInfo;
\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
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
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
1988 /* is this a decision node? */
\r
1989 if( node->planenum != PLANENUM_LEAF )
\r
1991 /* get node plane */
\r
1992 p1 = &mapplanes[ node->planenum ];
\r
1993 VectorCopy( p1->normal, plane1 );
\r
1994 plane1[ 3 ] = p1->dist;
\r
1996 /* check if surface is planar */
\r
1997 if( ds->planeNum >= 0 )
\r
1999 /* get surface plane */
\r
2000 p2 = &mapplanes[ ds->planeNum ];
\r
2001 VectorCopy( p2->normal, plane2 );
\r
2002 plane2[ 3 ] = p2->dist;
\r
2005 /* invert surface plane */
\r
2006 VectorSubtract( vec3_origin, plane2, reverse );
\r
2007 reverse[ 3 ] = -plane2[ 3 ];
\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
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
2023 /* clip the winding by this plane */
\r
2024 ClipWindingEpsilon( w, plane1, plane1[ 3 ], ON_EPSILON, &front, &back );
\r
2026 /* filter by this plane */
\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
2038 /* add a reference */
\r
2039 return AddReferenceToLeaf( ds, node );
\r
2045 FilterFaceIntoTree()
\r
2046 filters a planar winding face drawsurface into the bsp tree
\r
2049 int FilterFaceIntoTree( mapDrawSurface_t *ds, tree_t *tree )
\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
2066 FilterPatchIntoTree()
\r
2067 subdivides a patch into an approximate curve and filters it into the tree
\r
2070 #define FILTER_SUBDIVISION 8
\r
2072 static int FilterPatchIntoTree( mapDrawSurface_t *ds, tree_t *tree )
\r
2074 int i, x, y, refs;
\r
2075 mesh_t src, *mesh;
\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
2086 /* filter each quad into the tree (fixme: use new patch x-triangulation code?) */
\r
2088 for( y = 0; y < (mesh->height - 1); y++ )
\r
2090 for( x = 0; x < (mesh->width - 1); x++ )
\r
2093 w = AllocWinding( 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
2101 w = AllocWinding( 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
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
2114 /* free the subdivided mesh and return */
\r
2122 FilterTrianglesIntoTree()
\r
2123 filters a triangle surface (meta, model) into the bsp
\r
2126 static int FilterTrianglesIntoTree( mapDrawSurface_t *ds, tree_t *tree )
\r
2132 /* ydnar: gs mods: this was creating bogus triangles before */
\r
2134 for( i = 0; i < ds->numIndexes; i += 3 )
\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
2142 /* make a triangle winding and filter it into the tree */
\r
2143 w = AllocWinding( 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
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
2161 FilterFoliageIntoTree()
\r
2162 filters a foliage surface (wolf et/splash damage)
\r
2165 static int FilterFoliageIntoTree( mapDrawSurface_t *ds, tree_t *tree )
\r
2168 bspDrawVert_t *instance;
\r
2173 /* walk origin list */
\r
2175 for( f = 0; f < ds->numFoliageInstances; f++ )
\r
2177 /* get instance */
\r
2178 instance = ds->verts + ds->patchHeight + f;
\r
2180 /* walk triangle list */
\r
2181 for( i = 0; i < ds->numIndexes; i += 3 )
\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
2189 /* make a triangle winding and filter it into the tree */
\r
2190 w = AllocWinding( 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
2198 /* use point filtering as well */
\r
2199 for( i = 0; i < (ds->numVerts - ds->numFoliageInstances); i++ )
\r
2201 VectorAdd( instance->xyz, ds->verts[ i ].xyz, xyz );
\r
2202 refs += FilterPointIntoTree_r( xyz, ds, tree->headnode );
\r
2212 FilterFlareIntoTree()
\r
2213 simple point filtering for flare surfaces
\r
2215 static int FilterFlareSurfIntoTree( mapDrawSurface_t *ds, tree_t *tree )
\r
2217 return FilterPointIntoTree_r( ds->lightmapOrigin, ds, tree->headnode );
\r
2223 EmitDrawVerts() - ydnar
\r
2224 emits bsp drawverts from a map drawsurface
\r
2227 void EmitDrawVerts( mapDrawSurface_t *ds, bspDrawSurface_t *out )
\r
2230 bspDrawVert_t *dv;
\r
2236 si = ds->shaderInfo;
\r
2237 offset = si->offset;
\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
2244 /* allocate a new vert */
\r
2245 if( numBSPDrawVerts == MAX_MAP_DRAW_VERTS )
\r
2246 Error( "MAX_MAP_DRAW_VERTS" );
\r
2248 dv = &bspDrawVerts[ numBSPDrawVerts - 1 ];
\r
2251 memcpy( dv, &ds->verts[ i ], sizeof( *dv ) );
\r
2254 if( offset != 0.0f )
\r
2255 VectorMA( dv->xyz, offset, dv->normal, dv->xyz );
\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
2263 /* debug color? */
\r
2264 if( debugSurfaces )
\r
2266 for( k = 0; k < MAX_LIGHTMAPS; k++ )
\r
2267 VectorCopy( debugColors[ (ds - mapDrawSurfs) % 12 ], dv->color[ k ] );
\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
2281 int FindDrawIndexes( int numIndexes, int *indexes )
\r
2283 int i, j, numTestIndexes;
\r
2287 if( numIndexes < 3 || numBSPDrawIndexes < numIndexes || indexes == NULL )
\r
2288 return numBSPDrawIndexes;
\r
2291 numTestIndexes = 1 + numBSPDrawIndexes - numIndexes;
\r
2293 /* handle 3 indexes as a special case for performance */
\r
2294 if( numIndexes == 3 )
\r
2296 /* run through all indexes */
\r
2297 for( i = 0; i < numTestIndexes; i++ )
\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
2304 numRedundantIndexes += numIndexes;
\r
2310 return numBSPDrawIndexes;
\r
2313 /* handle 4 or more indexes */
\r
2314 for( i = 0; i < numTestIndexes; i++ )
\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
2322 /* handle 4 indexes */
\r
2323 if( numIndexes == 4 )
\r
2326 /* test the remainder */
\r
2327 for( j = 4; j < numIndexes; j++ )
\r
2329 if( indexes[ j ] != bspDrawIndexes[ i + j ] )
\r
2331 else if( j == (numIndexes - 1) )
\r
2333 numRedundantIndexes += numIndexes;
\r
2341 return numBSPDrawIndexes;
\r
2347 EmitDrawIndexes() - ydnar
\r
2348 attempts to find an existing run of drawindexes before adding new ones
\r
2351 void EmitDrawIndexes( mapDrawSurface_t *ds, bspDrawSurface_t *out )
\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
2361 /* copy new unique indexes */
\r
2362 for( i = 0; i < ds->numIndexes; i++ )
\r
2364 if( numBSPDrawIndexes == MAX_MAP_DRAW_INDEXES )
\r
2365 Error( "MAX_MAP_DRAW_INDEXES" );
\r
2366 bspDrawIndexes[ numBSPDrawIndexes ] = ds->indexes[ i ];
\r
2368 /* validate the index */
\r
2369 if( ds->type != SURFACE_PATCH )
\r
2371 if( bspDrawIndexes[ numBSPDrawIndexes ] < 0 || bspDrawIndexes[ numBSPDrawIndexes ] >= ds->numVerts )
\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
2378 bspDrawIndexes[ numBSPDrawIndexes ] = 0;
\r
2382 /* increment index count */
\r
2383 numBSPDrawIndexes++;
\r
2392 EmitFlareSurface()
\r
2393 emits a bsp flare drawsurface
\r
2396 void EmitFlareSurface( mapDrawSurface_t *ds )
\r
2399 bspDrawSurface_t *out;
\r
2402 /* ydnar: nuking useless flare drawsurfaces */
\r
2403 if( emitFlares == qfalse && ds->type != SURFACE_SHADER )
\r
2407 if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )
\r
2408 Error( "MAX_MAP_DRAW_SURFS" );
\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
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
2424 for( i = 0; i < MAX_LIGHTMAPS; i++ )
\r
2426 out->lightmapNum[ i ] = -3;
\r
2427 out->lightmapStyles[ i ] = LS_NONE;
\r
2428 out->vertexStyles[ i ] = LS_NONE;
\r
2430 out->lightmapStyles[ 0 ] = ds->lightStyle;
\r
2431 out->vertexStyles[ 0 ] = ds->lightStyle;
\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
2438 /* add to count */
\r
2439 numSurfacesByType[ ds->type ]++;
\r
2445 EmitPatchSurface()
\r
2446 emits a bsp patch drawsurface
\r
2449 void EmitPatchSurface( mapDrawSurface_t *ds )
\r
2452 bspDrawSurface_t *out;
\r
2453 int surfaceFlags, contentFlags;
\r
2456 /* invert the surface if necessary */
\r
2457 if( ds->shaderInfo->invert )
\r
2459 bspDrawVert_t *dv1, *dv2, temp;
\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
2466 /* walk the verts again, but this time reverse their order */
\r
2467 for( j = 0; j < ds->patchHeight; j++ )
\r
2469 for( i = 0; i < (ds->patchWidth / 2); i++ )
\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
2479 /* invert facing */
\r
2480 VectorScale( ds->lightmapVecs[ 2 ], -1.0f, ds->lightmapVecs[ 2 ] );
\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
2492 out->surfaceType = MST_PATCH;
\r
2493 if( debugSurfaces )
\r
2494 out->shaderNum = EmitShader( "debugsurfaces", NULL, NULL );
\r
2495 else if( patchMeta )
\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
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
2508 /* emit the new fake shader */
\r
2509 out->shaderNum = EmitShader( ds->shaderInfo->shader, &contentFlags, &surfaceFlags );
\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
2518 for( i = 0; i < MAX_LIGHTMAPS; i++ )
\r
2520 out->lightmapNum[ i ] = -3;
\r
2521 out->lightmapStyles[ i ] = LS_NONE;
\r
2522 out->vertexStyles[ i ] = LS_NONE;
\r
2524 out->lightmapStyles[ 0 ] = LS_NORMAL;
\r
2525 out->vertexStyles[ 0 ] = LS_NORMAL;
\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
2533 /* ydnar: gs mods: clear out the plane normal */
\r
2534 if( ds->planar == qfalse )
\r
2535 VectorClear( out->lightmapVecs[ 2 ] );
\r
2537 /* emit the verts and indexes */
\r
2538 EmitDrawVerts( ds, out );
\r
2539 EmitDrawIndexes( ds, out );
\r
2541 /* add to count */
\r
2542 numSurfacesByType[ ds->type ]++;
\r
2548 OptimizeTriangleSurface() - ydnar
\r
2549 optimizes the vertex/index data in a triangle surface
\r
2552 #define VERTEX_CACHE_SIZE 16
\r
2554 static void OptimizeTriangleSurface( mapDrawSurface_t *ds )
\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
2561 /* certain surfaces don't get optimized */
\r
2562 if( ds->numIndexes <= VERTEX_CACHE_SIZE ||
\r
2563 ds->shaderInfo->autosprite )
\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
2571 for( i = 0; i <= VERTEX_CACHE_SIZE && i < ds->numIndexes; i++ )
\r
2572 vertexCache[ i ] = indexes[ i ];
\r
2574 /* add triangles in a vertex cache-aware order */
\r
2575 for( i = 0; i < ds->numIndexes; i += 3 )
\r
2577 /* find best triangle given the current vertex cache */
\r
2581 for( j = 0; j < ds->numIndexes; j += 3 )
\r
2583 /* valid triangle? */
\r
2584 if( indexes[ j ] != -1 )
\r
2586 /* set first if necessary */
\r
2590 /* score the triangle */
\r
2592 for( k = 0; k < VERTEX_CACHE_SIZE; k++ )
\r
2594 if( indexes[ j ] == vertexCache[ k ] || indexes[ j + 1 ] == vertexCache[ k ] || indexes[ j + 2 ] == vertexCache[ k ] )
\r
2598 /* better triangle? */
\r
2599 if( score > bestScore )
\r
2601 bestScore = score;
\r
2605 /* a perfect score of 3 means this triangle's verts are already present in the vertex cache */
\r
2611 /* check if no decent triangle was found, and use first available */
\r
2615 /* valid triangle? */
\r
2618 /* add triangle to vertex cache */
\r
2619 for( j = 0; j < 3; j++ )
\r
2621 for( k = 0; k < VERTEX_CACHE_SIZE; k++ )
\r
2623 if( indexes[ best + j ] == vertexCache[ k ] )
\r
2627 if( k >= VERTEX_CACHE_SIZE )
\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
2634 vertexCache[ 0 ] = indexes[ best + j ];
\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
2643 /* clear from input pool */
\r
2644 indexes[ best ] = -1;
\r
2645 indexes[ best + 1 ] = -1;
\r
2646 indexes[ best + 2 ] = -1;
\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
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
2666 EmitTriangleSurface()
\r
2667 creates a bsp drawsurface from arbitrary triangle surfaces
\r
2670 static void EmitTriangleSurface( mapDrawSurface_t *ds )
\r
2673 bspDrawSurface_t *out;
\r
2676 /* invert the surface if necessary */
\r
2677 if( ds->shaderInfo->invert )
\r
2679 /* walk the indexes, reverse the triangle order */
\r
2680 for( i = 0; i < ds->numIndexes; i += 3 )
\r
2682 temp = ds->indexes[ i ];
\r
2683 ds->indexes[ i ] = ds->indexes[ i + 1 ];
\r
2684 ds->indexes[ i + 1 ] = temp;
\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
2691 /* invert facing */
\r
2692 VectorScale( ds->lightmapVecs[ 2 ], -1.0f, ds->lightmapVecs[ 2 ] );
\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
2703 /* ydnar/sd: handle wolf et foliage surfaces */
\r
2704 if( ds->type == SURFACE_FOLIAGE )
\r
2705 out->surfaceType = MST_FOLIAGE;
\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
2712 /* set to a planar face */
\r
2714 out->surfaceType = MST_PLANAR;
\r
2717 if( debugSurfaces )
\r
2718 out->shaderNum = EmitShader( "debugsurfaces", NULL, NULL );
\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
2725 /* debug inset (push each triangle vertex towards the center of each triangle it is on */
\r
2728 bspDrawVert_t *a, *b, *c;
\r
2732 /* walk triangle list */
\r
2733 for( i = 0; i < ds->numIndexes; i += 3 )
\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
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
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
2760 for( i = 0; i < MAX_LIGHTMAPS; i++ )
\r
2762 out->lightmapNum[ i ] = -3;
\r
2763 out->lightmapStyles[ i ] = LS_NONE;
\r
2764 out->vertexStyles[ i ] = LS_NONE;
\r
2766 out->lightmapStyles[ 0 ] = LS_NORMAL;
\r
2767 out->vertexStyles[ 0 ] = LS_NORMAL;
\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
2775 /* ydnar: gs mods: clear out the plane normal */
\r
2776 if( ds->planar == qfalse )
\r
2777 VectorClear( out->lightmapVecs[ 2 ] );
\r
2779 /* optimize the surface's triangles */
\r
2780 OptimizeTriangleSurface( ds );
\r
2782 /* emit the verts and indexes */
\r
2783 EmitDrawVerts( ds, out );
\r
2784 EmitDrawIndexes( ds, out );
\r
2786 /* add to count */
\r
2787 numSurfacesByType[ ds->type ]++;
\r
2794 emits a bsp planar winding (brush face) drawsurface
\r
2797 static void EmitFaceSurface( mapDrawSurface_t *ds )
\r
2799 /* strip/fan finding was moved elsewhere */
\r
2800 StripFaceSurface( ds );
\r
2801 EmitTriangleSurface( ds );
\r
2807 MakeDebugPortalSurfs_r() - ydnar
\r
2808 generates drawsurfaces for passable portals in the bsp
\r
2811 static void MakeDebugPortalSurfs_r( node_t *node, shaderInfo_t *si )
\r
2816 mapDrawSurface_t *ds;
\r
2817 bspDrawVert_t *dv;
\r
2820 /* recurse if decision node */
\r
2821 if( node->planenum != PLANENUM_LEAF)
\r
2823 MakeDebugPortalSurfs_r( node->children[ 0 ], si );
\r
2824 MakeDebugPortalSurfs_r( node->children[ 1 ], si );
\r
2828 /* don't bother with opaque leaves */
\r
2829 if( node->opaque )
\r
2832 /* walk the list of portals */
\r
2833 for( c = 0, p = node->portals; p != NULL; c++, p = p->next[ s ] )
\r
2835 /* get winding and side even/odd */
\r
2837 s = (p->nodes[ 1 ] == node);
\r
2839 /* is this a valid portal for this leaf? */
\r
2840 if( w && p->nodes[ 0 ] == node )
\r
2842 /* is this portal passable? */
\r
2843 if( PortalPassable( p ) == qfalse )
\r
2846 /* check max points */
\r
2847 if( w->numpoints > 64 )
\r
2848 Error( "MakePortalSurfs_r: w->numpoints = %d", w->numpoints );
\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
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
2862 /* walk the winding */
\r
2863 for( i = 0; i < ds->numVerts; i++ )
\r
2866 dv = ds->verts + i;
\r
2869 VectorCopy( w->p[ i ], dv->xyz );
\r
2870 VectorCopy( p->plane.normal, dv->normal );
\r
2873 for( k = 0; k < MAX_LIGHTMAPS; k++ )
\r
2875 VectorCopy( debugColors[ c % 12 ], dv->color[ k ] );
\r
2876 dv->color[ k ][ 3 ] = 32;
\r
2886 MakeDebugPortalSurfs() - ydnar
\r
2887 generates drawsurfaces for passable portals in the bsp
\r
2890 void MakeDebugPortalSurfs( tree_t *tree )
\r
2896 Sys_FPrintf( SYS_VRB, "--- MakeDebugPortalSurfs ---\n" );
\r
2898 /* get portal debug shader */
\r
2899 si = ShaderInfoForShader( "debugportals" );
\r
2901 /* walk the tree */
\r
2902 MakeDebugPortalSurfs_r( tree->headnode, si );
\r
2908 MakeFogHullSurfs()
\r
2909 generates drawsurfaces for a foghull (this MUST use a sky shader)
\r
2912 void MakeFogHullSurfs( entity_t *e, tree_t *tree, char *shader )
\r
2915 mapDrawSurface_t *ds;
\r
2916 vec3_t fogMins, fogMaxs;
\r
2917 int i, indexes[] =
\r
2929 if( shader == NULL || shader[ 0 ] == '\0' )
\r
2933 Sys_FPrintf( SYS_VRB, "--- MakeFogHullSurfs ---\n" );
\r
2935 /* get hull bounds */
\r
2936 VectorCopy( mapMins, fogMins );
\r
2937 VectorCopy( mapMaxs, fogMaxs );
\r
2938 for( i = 0; i < 3; i++ )
\r
2940 fogMins[ i ] -= 128;
\r
2941 fogMaxs[ i ] += 128;
\r
2944 /* get foghull shader */
\r
2945 si = ShaderInfoForShader( shader );
\r
2947 /* allocate a drawsurface */
\r
2948 ds = AllocDrawSurface( SURFACE_FOGHULL );
\r
2949 ds->shaderInfo = si;
\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
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
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
2970 memcpy( ds->indexes, indexes, ds->numIndexes * sizeof( *ds->indexes ) );
\r
2976 BiasSurfaceTextures()
\r
2977 biases a surface's texcoords as close to 0 as possible
\r
2980 void BiasSurfaceTextures( mapDrawSurface_t *ds )
\r
2985 /* calculate the surface texture bias */
\r
2986 CalcSurfaceTextureRange( ds );
\r
2988 /* don't bias globaltextured shaders */
\r
2989 if( ds->shaderInfo->globalTexture )
\r
2992 /* bias the texture coordinates */
\r
2993 for( i = 0; i < ds->numVerts; i++ )
\r
2995 ds->verts[ i ].st[ 0 ] += ds->bias[ 0 ];
\r
2996 ds->verts[ i ].st[ 1 ] += ds->bias[ 1 ];
\r
3003 AddSurfaceModelsToTriangle_r()
\r
3004 adds models to a specified triangle, returns the number of models added
\r
3007 int AddSurfaceModelsToTriangle_r( mapDrawSurface_t *ds, surfaceModel_t *model, bspDrawVert_t **tri )
\r
3009 bspDrawVert_t mid, *tri2[ 3 ];
\r
3010 int max, n, localNumSurfaceModels;
\r
3014 localNumSurfaceModels = 0;
\r
3016 /* subdivide calc */
\r
3019 float *a, *b, dx, dy, dz, dist, maxDist;
\r
3022 /* find the longest edge and split it */
\r
3025 for( i = 0; i < 3; i++ )
\r
3028 a = tri[ i ]->xyz;
\r
3029 b = tri[ (i + 1) % 3 ]->xyz;
\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
3038 if( dist > maxDist )
\r
3045 /* is the triangle small enough? */
\r
3046 if( max < 0 || maxDist <= (model->density * model->density) )
\r
3048 float odds, r, angle;
\r
3049 vec3_t origin, normal, scale, axis[ 3 ], angles;
\r
3050 m4x4_t transform, temp;
\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
3056 if( r > model->odds )
\r
3059 /* calculate scale */
\r
3060 r = model->minScale + Random() * (model->maxScale - model->minScale);
\r
3061 VectorSet( scale, r, r, r );
\r
3063 /* calculate angle */
\r
3064 angle = model->minAngle + Random() * (model->maxAngle - model->minAngle);
\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
3072 /* clear transform matrix */
\r
3073 m4x4_identity( transform );
\r
3075 /* handle oriented models */
\r
3076 if( model->oriented )
\r
3079 VectorSet( angles, 0.0f, 0.0f, angle );
\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
3088 /* make perpendicular vectors */
\r
3089 MakeNormalVectors( axis[ 2 ], axis[ 1 ], axis[ 0 ] );
\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
3098 m4x4_scale_by_vec3( temp, scale );
\r
3100 /* rotate around z axis */
\r
3101 m4x4_rotate_by_vec3( temp, angles, eXYZ );
\r
3104 m4x4_translate_by_vec3( transform, origin );
\r
3106 /* tranform into axis space */
\r
3107 m4x4_multiply_by_m4x4( transform, temp );
\r
3110 /* handle z-up models */
\r
3114 VectorSet( angles, 0.0f, 0.0f, angle );
\r
3117 m4x4_pivoted_transform_by_vec3( transform, origin, angles, eXYZ, scale, vec3_origin );
\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
3123 /* return to sender */
\r
3128 /* split the longest edge and map it */
\r
3129 LerpDrawVert( tri[ max ], tri[ (max + 1) % 3 ], &mid );
\r
3131 /* recurse to first triangle */
\r
3132 VectorCopy( tri, tri2 );
\r
3133 tri2[ max ] = ∣
\r
3134 n = AddSurfaceModelsToTriangle_r( ds, model, tri2 );
\r
3137 localNumSurfaceModels += n;
\r
3139 /* recurse to second triangle */
\r
3140 VectorCopy( tri, tri2 );
\r
3141 tri2[ (max + 1) % 3 ] = ∣
\r
3142 n = AddSurfaceModelsToTriangle_r( ds, model, tri2 );
\r
3145 localNumSurfaceModels += n;
\r
3147 /* return count */
\r
3148 return localNumSurfaceModels;
\r
3154 AddSurfaceModels()
\r
3155 adds a surface's shader models to the surface
\r
3158 int AddSurfaceModels( mapDrawSurface_t *ds )
\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
3168 if( ds == NULL || ds->shaderInfo == NULL || ds->shaderInfo->surfaceModel == NULL )
\r
3172 localNumSurfaceModels = 0;
\r
3174 /* walk the model list */
\r
3175 for( model = ds->shaderInfo->surfaceModel; model != NULL; model = model->next )
\r
3177 /* switch on type */
\r
3178 switch( ds->type )
\r
3180 /* handle brush faces and decals */
\r
3181 case SURFACE_FACE:
\r
3182 case SURFACE_DECAL:
\r
3183 /* calculate centroid */
\r
3184 memset( ¢roid, 0, sizeof( centroid ) );
\r
3188 for( i = 0; i < ds->numVerts; i++ )
\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
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
3211 /* head vert is centroid */
\r
3212 tri[ 0 ] = ¢roid;
\r
3214 /* walk fanned triangles */
\r
3215 for( i = 0; i < ds->numVerts; i++ )
\r
3217 /* set triangle */
\r
3218 tri[ 1 ] = &ds->verts[ i ];
\r
3219 tri[ 2 ] = &ds->verts[ (i + 1) % ds->numVerts ];
\r
3221 /* create models */
\r
3222 n = AddSurfaceModelsToTriangle_r( ds, model, tri );
\r
3225 localNumSurfaceModels += n;
\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
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
3244 /* subdivide each quad to place the models */
\r
3245 for( y = 0; y < (mesh->height - 1); y++ )
\r
3247 for( x = 0; x < (mesh->width - 1); x++ )
\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
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
3266 localNumSurfaceModels += n;
\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
3275 localNumSurfaceModels += n;
\r
3279 /* free the subdivided mesh */
\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
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
3296 localNumSurfaceModels += n;
\r
3300 /* no support for flares, foghull, etc */
\r
3306 /* return count */
\r
3307 return localNumSurfaceModels;
\r
3313 AddEntitySurfaceModels() - ydnar
\r
3314 adds surfacemodels to an entity's surfaces
\r
3317 void AddEntitySurfaceModels( entity_t *e )
\r
3323 Sys_FPrintf( SYS_VRB, "--- AddEntitySurfaceModels ---\n" );
\r
3325 /* walk the surface list */
\r
3326 for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
\r
3327 numSurfaceModels += AddSurfaceModels( &mapDrawSurfs[ i ] );
\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
3339 void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree )
\r
3342 mapDrawSurface_t *ds;
\r
3344 vec3_t origin, mins, maxs;
\r
3346 int numSurfs, numRefs, numSkyboxSurfaces;
\r
3350 Sys_FPrintf( SYS_VRB, "--- FilterDrawsurfsIntoTree ---\n" );
\r
3352 /* filter surfaces into the tree */
\r
3355 numSkyboxSurfaces = 0;
\r
3356 for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
\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
3364 si = ds->shaderInfo;
\r
3366 /* ydnar: skybox surfaces are special */
\r
3369 refs = AddReferenceToTree_r( ds, tree->headnode, qtrue );
\r
3370 ds->skybox = qfalse;
\r
3374 /* refs initially zero */
\r
3377 /* ydnar: apply alphamod */
\r
3378 AlphaMod( ds->shaderInfo->alphaMod, ds->numVerts, ds->verts );
\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
3384 /* ydnar: make fur surfaces */
\r
3385 if( si->furNumLayers > 0 )
\r
3388 /* ydnar/sd: make foliage surfaces */
\r
3389 if( si->foliage != NULL )
\r
3392 /* create a flare surface if necessary */
\r
3393 if( si->flareShader[ 0 ] )
\r
3394 AddSurfaceFlare( ds, e->origin );
\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
3400 /* ydnar: bias the surface textures */
\r
3401 BiasSurfaceTextures( ds );
\r
3403 /* ydnar: globalizing of fog volume handling (eek a hack) */
\r
3404 if( e != entities && si->noFog == qfalse )
\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
3411 VectorAdd( ds->mins, e->origin, mins );
\r
3412 VectorAdd( ds->maxs, e->origin, maxs );
\r
3414 /* set the fog number for this surface */
\r
3415 ds->fogNum = FogForBounds( mins, maxs, 1.0f ); //% FogForPoint( origin, 0.0f );
\r
3419 /* ydnar: gs mods: handle the various types of surfaces */
\r
3420 switch( ds->type )
\r
3422 /* handle brush faces */
\r
3423 case SURFACE_FACE:
\r
3424 case SURFACE_DECAL:
\r
3426 refs = FilterFaceIntoTree( ds, tree );
\r
3428 EmitFaceSurface( ds );
\r
3431 /* handle patches */
\r
3432 case SURFACE_PATCH:
\r
3434 refs = FilterPatchIntoTree( ds, tree );
\r
3436 EmitPatchSurface( ds );
\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
3445 refs = FilterTrianglesIntoTree( ds, tree );
\r
3447 EmitTriangleSurface( ds );
\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
3454 refs = FilterFoliageIntoTree( ds, tree );
\r
3456 EmitTriangleSurface( ds );
\r
3459 /* handle foghull surfaces */
\r
3460 case SURFACE_FOGHULL:
\r
3462 refs = AddReferenceToTree_r( ds, tree->headnode, qfalse );
\r
3464 EmitTriangleSurface( ds );
\r
3467 /* handle flares */
\r
3468 case SURFACE_FLARE:
\r
3470 refs = FilterFlareSurfIntoTree( ds, tree );
\r
3472 EmitFlareSurface( ds );
\r
3475 /* handle shader-only surfaces */
\r
3476 case SURFACE_SHADER:
\r
3478 EmitFlareSurface( ds );
\r
3481 /* no references */
\r
3487 /* tot up the references */
\r
3490 /* tot up counts */
\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
3498 /* one last sanity check */
\r
3500 bspDrawSurface_t *out;
\r
3501 out = &bspDrawSurfaces[ numBSPDrawSurfaces - 1 ];
\r
3502 if( out->numVerts == 3 && out->numIndexes > 3 )
\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
3510 /* ydnar: handle skybox surfaces */
\r
3513 MakeSkyboxSurface( ds );
\r
3514 numSkyboxSurfaces++;
\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
3529 Sys_FPrintf( SYS_VRB, "%9d redundant indexes supressed, saving %d Kbytes\n", numRedundantIndexes, (numRedundantIndexes * 4 / 1024) );
\r