1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytesDeluxe( const float *color, byte *colorBytes, float scale, const float *deluxel, byte *deluxeBytes)
51 vec3_t sample, direction;
53 float colorMultiplier;
57 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
61 /* make a local copy */
62 VectorScale( color, scale, sample );
65 gamma = 1.0f / lightmapGamma;
66 for( i = 0; i < 3; i++ )
68 /* handle negative light */
69 if( sample[ i ] <= 0.0f )
76 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
81 if (lightmapExposure == 1)
83 /* clamp with color normalization */
85 if( sample[ 1 ] > max )
87 if( sample[ 2 ] > max )
89 if(max * colorMultiplier > 255.0f)
90 colorMultiplier *= 255.0 / max;
94 if (lightmapExposure==0)
97 inv = 1.f/lightmapExposure;
101 if( sample[ 1 ] > max )
103 if( sample[ 2 ] > max )
105 max *= colorMultiplier;
107 dif = (1- exp(-max * inv) ) * 255;
118 colorMultiplier *= dif;
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, (colorMultiplier / lightmapCompensate), sample );
125 colorBytes[ 0 ] = sample[ 0 ];
126 colorBytes[ 1 ] = sample[ 1 ];
127 colorBytes[ 2 ] = sample[ 2 ];
130 /* store direction */
133 if(normalizeDeluxemap)
135 if(!VectorNormalize(deluxel, direction))
136 VectorClear(direction);
142 VectorScale(deluxel, 1 / deluxel[3], direction);
144 // if the light was scaled down due to it being too bright...
145 // we need to reduce the directionality so ADDING light can't
146 // make stuff DARKER!
147 if(colorMultiplier < 1)
148 VectorScale(direction, colorMultiplier, direction);
149 // TODO find out the best renderer equation for this
152 VectorClear(direction);
155 /* normalize average light direction */
156 //if(direction[0] != 0 || direction[1] != 0 || direction[2] != 0)
158 /* encode [-1,1] in [0,255] */
159 for( i = 0; i < 3; i++ )
161 temp = (direction[ i ] + 1.0f) * 127.5f;
163 deluxeBytes[ i ] = 0;
164 else if( temp > 255 )
165 deluxeBytes[ i ] = 255;
167 deluxeBytes[ i ] = temp;
173 void ColorToBytes( const float *color, byte *colorBytes, float scale )
175 ColorToBytesDeluxe(color, colorBytes, scale, NULL, NULL);
180 /* -------------------------------------------------------------------------------
182 this section deals with phong shading (normal interpolation across brush faces)
184 ------------------------------------------------------------------------------- */
188 smooths together coincident vertex normals across the bsp
191 #define MAX_SAMPLES 256
192 #define THETA_EPSILON 0.000001
193 #define EQUAL_NORMAL_EPSILON 0.01
195 void SmoothNormals( void )
197 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
198 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
199 bspDrawSurface_t *ds;
203 vec3_t average, diff;
204 int indexes[ MAX_SAMPLES ];
205 vec3_t votes[ MAX_SAMPLES ];
208 /* allocate shade angle table */
209 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
210 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
212 /* allocate smoothed table */
213 cs = (numBSPDrawVerts / 8) + 1;
214 smoothed = safe_malloc( cs );
215 memset( smoothed, 0, cs );
217 /* set default shade angle */
218 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
221 /* run through every surface and flag verts belonging to non-lightmapped surfaces
222 and set per-vertex smoothing angle */
223 for( i = 0; i < numBSPDrawSurfaces; i++ )
226 ds = &bspDrawSurfaces[ i ];
228 /* get shader for shade angle */
229 si = surfaceInfos[ i ].si;
230 if( si->shadeAngleDegrees )
231 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
233 shadeAngle = defaultShadeAngle;
234 if( shadeAngle > maxShadeAngle )
235 maxShadeAngle = shadeAngle;
238 for( j = 0; j < ds->numVerts; j++ )
240 f = ds->firstVert + j;
241 shadeAngles[ f ] = shadeAngle;
242 if( ds->surfaceType == MST_TRIANGLE_SOUP )
243 smoothed[ f >> 3 ] |= (1 << (f & 7));
246 /* ydnar: optional force-to-trisoup */
247 if( trisoup && ds->surfaceType == MST_PLANAR )
249 ds->surfaceType = MST_TRIANGLE_SOUP;
250 ds->lightmapNum[ 0 ] = -3;
254 /* bail if no surfaces have a shade angle */
255 if( maxShadeAngle == 0 )
264 start = I_FloatTime();
266 /* go through the list of vertexes */
267 for( i = 0; i < numBSPDrawVerts; i++ )
270 f = 10 * i / numBSPDrawVerts;
274 Sys_Printf( "%i...", f );
277 /* already smoothed? */
278 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
282 VectorClear( average );
286 /* build a table of coincident vertexes */
287 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
289 /* already smoothed? */
290 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
294 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
297 /* use smallest shade angle */
298 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
300 /* check shade angle */
301 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
304 else if( dot < -1.0 )
306 testAngle = acos( dot ) + THETA_EPSILON;
307 if( testAngle >= shadeAngle )
309 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
312 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
314 /* add to the list */
315 indexes[ numVerts++ ] = j;
318 smoothed[ j >> 3 ] |= (1 << (j & 7));
320 /* see if this normal has already been voted */
321 for( k = 0; k < numVotes; k++ )
323 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
324 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
325 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
326 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
330 /* add a new vote? */
331 if( k == numVotes && numVotes < MAX_SAMPLES )
333 VectorAdd( average, bspDrawVerts[ j ].normal, average );
334 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
339 /* don't average for less than 2 verts */
344 if( VectorNormalize( average, average ) > 0 )
347 for( j = 0; j < numVerts; j++ )
348 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
352 /* free the tables */
357 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
362 /* -------------------------------------------------------------------------------
364 this section deals with phong shaded lightmap tracing
366 ------------------------------------------------------------------------------- */
368 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
372 calculates the st tangent vectors for normalmapping
375 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
382 /* calculate barycentric basis for the triangle */
383 bb = (dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]) - (dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]);
384 if( fabs( bb ) < 0.00000001f )
388 for( i = 0; i < numVerts; i++ )
390 /* calculate s tangent vector */
391 s = dv[ i ]->st[ 0 ] + 10.0f;
392 t = dv[ i ]->st[ 1 ];
393 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
394 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
395 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
397 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
398 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
399 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
401 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
402 VectorNormalize( stv[ i ], stv[ i ] );
404 /* calculate t tangent vector */
405 s = dv[ i ]->st[ 0 ];
406 t = dv[ i ]->st[ 1 ] + 10.0f;
407 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
408 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
409 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
411 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
412 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
413 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
415 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
416 VectorNormalize( ttv[ i ], ttv[ i ] );
419 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
420 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
423 /* return to caller */
432 perterbs the normal by the shader's normalmap in tangent space
435 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
442 VectorCopy( dv->normal, pNormal );
444 /* sample normalmap */
445 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
448 /* remap sampled normal from [0,255] to [-1,-1] */
449 for( i = 0; i < 3; i++ )
450 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
452 /* scale tangent vectors and add to original normal */
453 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
454 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
455 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
457 /* renormalize and return */
458 VectorNormalize( pNormal, pNormal );
465 maps a luxel for triangle bv at
469 #define BOGUS_NUDGE -99999.0f
471 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
473 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
474 float *luxel, *origin, *normal, d, lightmapSampleOffset;
481 vec4_t sideplane, hostplane;
486 static float nudges[][ 2 ] =
488 //%{ 0, 0 }, /* try center first */
489 { -NUDGE, 0 }, /* left */
490 { NUDGE, 0 }, /* right */
491 { 0, NUDGE }, /* up */
492 { 0, -NUDGE }, /* down */
493 { -NUDGE, NUDGE }, /* left/up */
494 { NUDGE, -NUDGE }, /* right/down */
495 { NUDGE, NUDGE }, /* right/up */
496 { -NUDGE, -NUDGE }, /* left/down */
497 { BOGUS_NUDGE, BOGUS_NUDGE }
501 /* find luxel xy coords (fixme: subtract 0.5?) */
502 x = dv->lightmap[ 0 ][ 0 ];
503 y = dv->lightmap[ 0 ][ 1 ];
506 else if( x >= lm->sw )
510 else if( y >= lm->sh )
513 /* set shader and cluster list */
517 numClusters = info->numSurfaceClusters;
518 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
527 /* get luxel, origin, cluster, and normal */
528 luxel = SUPER_LUXEL( 0, x, y );
529 origin = SUPER_ORIGIN( x, y );
530 normal = SUPER_NORMAL( x, y );
531 cluster = SUPER_CLUSTER( x, y );
533 /* don't attempt to remap occluded luxels for planar surfaces */
534 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
537 /* only average the normal for premapped luxels */
538 else if( (*cluster) >= 0 )
540 /* do bumpmap calculations */
542 PerturbNormal( dv, si, pNormal, stv, ttv );
544 VectorCopy( dv->normal, pNormal );
546 /* add the additional normal data */
547 VectorAdd( normal, pNormal, normal );
552 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
556 /* axial lightmap projection */
557 if( lm->vecs != NULL )
559 /* calculate an origin for the sample from the lightmap vectors */
560 VectorCopy( lm->origin, origin );
561 for( i = 0; i < 3; i++ )
563 /* add unless it's the axis, which is taken care of later */
564 if( i == lm->axisNum )
566 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
569 /* project the origin onto the plane */
570 d = DotProduct( origin, plane ) - plane[ 3 ];
571 d /= plane[ lm->axisNum ];
572 origin[ lm->axisNum ] -= d;
575 /* non axial lightmap projection (explicit xyz) */
577 VectorCopy( dv->xyz, origin );
579 //////////////////////
580 //27's test to make sure samples stay within the triangle boundaries
581 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
582 //2) if it does, nudge it onto the correct side.
584 if (worldverts!=NULL && lightmapTriangleCheck)
588 VectorCopy(worldverts[j],cverts[j]);
590 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
596 //build plane using 2 edges and a normal
599 VectorCopy(cverts[next],temp);
600 VectorAdd(temp,hostplane,temp);
601 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
603 //planetest sample point
604 e=DotProduct(origin,sideplane);
609 //VectorClear(origin);
610 //Move the sample point back inside triangle bounds
611 origin[0]-=sideplane[0]*(e+1);
612 origin[1]-=sideplane[1]*(e+1);
613 origin[2]-=sideplane[2]*(e+1);
622 ////////////////////////
624 /* planar surfaces have precalculated lightmap vectors for nudging */
625 if( lm->plane != NULL )
627 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
628 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
629 VectorCopy( lm->plane, vecs[ 2 ] );
632 /* non-planar surfaces must calculate them */
636 VectorCopy( plane, vecs[ 2 ] );
638 VectorCopy( dv->normal, vecs[ 2 ] );
639 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
642 /* push the origin off the surface a bit */
644 lightmapSampleOffset = si->lightmapSampleOffset;
646 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
647 if( lm->axisNum < 0 )
648 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
649 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
650 origin[ lm->axisNum ] -= lightmapSampleOffset;
652 origin[ lm->axisNum ] += lightmapSampleOffset;
654 VectorCopy(origin,origintwo);
655 if(lightmapExtraVisClusterNudge)
657 origintwo[0]+=vecs[2][0];
658 origintwo[1]+=vecs[2][1];
659 origintwo[2]+=vecs[2][2];
663 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
665 /* another retarded hack, storing nudge count in luxel[ 1 ] */
668 /* point in solid? (except in dark mode) */
669 if( pointCluster < 0 && dark == qfalse )
671 /* nudge the the location around */
673 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
675 /* nudge the vector around a bit */
676 for( i = 0; i < 3; i++ )
678 /* set nudged point*/
679 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
683 /* get pvs cluster */
684 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
685 if( pointCluster >= 0 )
686 VectorCopy( nudged, origin );
691 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
692 if( pointCluster < 0 && si != NULL && dark == qfalse )
694 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
695 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
696 if( pointCluster >= 0 )
697 VectorCopy( nudged, origin );
702 if( pointCluster < 0 )
704 (*cluster) = CLUSTER_OCCLUDED;
705 VectorClear( origin );
706 VectorClear( normal );
712 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
714 /* do bumpmap calculations */
716 PerturbNormal( dv, si, pNormal, stv, ttv );
718 VectorCopy( dv->normal, pNormal );
720 /* store the cluster and normal */
721 (*cluster) = pointCluster;
722 VectorCopy( pNormal, normal );
724 /* store explicit mapping pass and implicit mapping pass */
739 recursively subdivides a triangle until its edges are shorter
740 than the distance between two luxels (thanks jc :)
743 static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
745 bspDrawVert_t mid, *dv2[ 3 ];
749 /* map the vertexes */
751 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
752 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
753 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
759 float *a, *b, dx, dy, dist, maxDist;
762 /* find the longest edge and split it */
765 for( i = 0; i < 3; i++ )
768 a = dv[ i ]->lightmap[ 0 ];
769 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
772 dx = a[ 0 ] - b[ 0 ];
773 dy = a[ 1 ] - b[ 1 ];
774 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
784 /* try to early out */
785 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
789 /* split the longest edge and map it */
790 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
791 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
793 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
794 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
796 /* recurse to first triangle */
797 VectorCopy( dv, dv2 );
799 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
801 /* recurse to second triangle */
802 VectorCopy( dv, dv2 );
803 dv2[ (max + 1) % 3 ] = ∣
804 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
811 seed function for MapTriangle_r()
812 requires a cw ordered triangle
815 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
819 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
820 vec3_t worldverts[ 3 ];
823 /* get plane if possible */
824 if( lm->plane != NULL )
826 VectorCopy( lm->plane, plane );
827 plane[ 3 ] = lm->plane[ 3 ];
830 /* otherwise make one from the points */
831 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
834 /* check to see if we need to calculate texture->world tangent vectors */
835 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
846 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
847 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
848 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
850 /* map the vertexes */
851 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
852 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
853 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
855 /* 2002-11-20: prefer axial triangle edges */
858 /* subdivide the triangle */
859 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
863 for( i = 0; i < 3; i++ )
866 bspDrawVert_t *dv2[ 3 ];
870 a = dv[ i ]->lightmap[ 0 ];
871 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
873 /* make degenerate triangles for mapping edges */
874 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
877 dv2[ 1 ] = dv[ (i + 1) % 3 ];
878 dv2[ 2 ] = dv[ (i + 1) % 3 ];
880 /* map the degenerate triangle */
881 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
892 recursively subdivides a quad until its edges are shorter
893 than the distance between two luxels
896 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] )
898 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
905 float *a, *b, dx, dy, dist, maxDist;
908 /* find the longest edge and split it */
911 for( i = 0; i < 4; i++ )
914 a = dv[ i ]->lightmap[ 0 ];
915 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
918 dx = a[ 0 ] - b[ 0 ];
919 dy = a[ 1 ] - b[ 1 ];
920 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
930 /* try to early out */
931 if( max < 0 || maxDist <= subdivideThreshold )
935 /* we only care about even/odd edges */
938 /* split the longest edges */
939 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
940 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
942 /* map the vertexes */
943 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
944 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
949 /* recurse to first quad */
951 dv2[ 1 ] = &mid[ 0 ];
952 dv2[ 2 ] = &mid[ 1 ];
954 MapQuad_r( lm, info, dv2, plane, stv, ttv );
956 /* recurse to second quad */
957 dv2[ 0 ] = &mid[ 0 ];
960 dv2[ 3 ] = &mid[ 1 ];
961 MapQuad_r( lm, info, dv2, plane, stv, ttv );
967 /* recurse to first quad */
970 dv2[ 2 ] = &mid[ 0 ];
971 dv2[ 3 ] = &mid[ 1 ];
972 MapQuad_r( lm, info, dv2, plane, stv, ttv );
974 /* recurse to second quad */
975 dv2[ 0 ] = &mid[ 1 ];
976 dv2[ 1 ] = &mid[ 0 ];
979 MapQuad_r( lm, info, dv2, plane, stv, ttv );
987 seed function for MapQuad_r()
988 requires a cw ordered triangle quad
991 #define QUAD_PLANAR_EPSILON 0.5f
993 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
997 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
1000 /* get plane if possible */
1001 if( lm->plane != NULL )
1003 VectorCopy( lm->plane, plane );
1004 plane[ 3 ] = lm->plane[ 3 ];
1007 /* otherwise make one from the points */
1008 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
1011 /* 4th point must fall on the plane */
1012 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
1013 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
1016 /* check to see if we need to calculate texture->world tangent vectors */
1017 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
1028 /* map the vertexes */
1029 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
1030 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
1031 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
1032 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
1034 /* subdivide the quad */
1035 MapQuad_r( lm, info, dv, plane, stv, ttv );
1043 maps the locations, normals, and pvs clusters for a raw lightmap
1046 #define VectorDivide( in, d, out ) VectorScale( in, (1.0f / (d)), out ) //% (out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)
1048 void MapRawLightmap( int rawLightmapNum )
1050 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1051 float *luxel, *origin, *normal, samples, radius, pass;
1053 bspDrawSurface_t *ds;
1054 surfaceInfo_t *info;
1055 mesh_t src, *subdivided, *mesh;
1056 bspDrawVert_t *verts, *dv[ 4 ], fake;
1059 /* bail if this number exceeds the number of raw lightmaps */
1060 if( rawLightmapNum >= numRawLightmaps )
1064 lm = &rawLightmaps[ rawLightmapNum ];
1066 /* -----------------------------------------------------------------
1067 map referenced surfaces onto the raw lightmap
1068 ----------------------------------------------------------------- */
1070 /* walk the list of surfaces on this raw lightmap */
1071 for( n = 0; n < lm->numLightSurfaces; n++ )
1073 /* with > 1 surface per raw lightmap, clear occluded */
1076 for( y = 0; y < lm->sh; y++ )
1078 for( x = 0; x < lm->sw; x++ )
1081 cluster = SUPER_CLUSTER( x, y );
1083 *cluster = CLUSTER_UNMAPPED;
1089 num = lightSurfaces[ lm->firstLightSurface + n ];
1090 ds = &bspDrawSurfaces[ num ];
1091 info = &surfaceInfos[ num ];
1093 /* bail if no lightmap to calculate */
1094 if( info->lm != lm )
1100 /* map the surface onto the lightmap origin/cluster/normal buffers */
1101 switch( ds->surfaceType )
1105 verts = yDrawVerts + ds->firstVert;
1107 /* map the triangles */
1108 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1110 for( i = 0; i < ds->numIndexes; i += 3 )
1112 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1113 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1114 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1115 MapTriangle( lm, info, dv, mapNonAxial );
1121 /* make a mesh from the drawsurf */
1122 src.width = ds->patchWidth;
1123 src.height = ds->patchHeight;
1124 src.verts = &yDrawVerts[ ds->firstVert ];
1125 //% subdivided = SubdivideMesh( src, 8, 512 );
1126 subdivided = SubdivideMesh2( src, info->patchIterations );
1128 /* fit it to the curve and remove colinear verts on rows/columns */
1129 PutMeshOnCurve( *subdivided );
1130 mesh = RemoveLinearMeshColumnsRows( subdivided );
1131 FreeMesh( subdivided );
1134 verts = mesh->verts;
1140 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1141 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1142 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1143 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1147 /* map the mesh quads */
1150 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1152 for( y = 0; y < (mesh->height - 1); y++ )
1154 for( x = 0; x < (mesh->width - 1); x++ )
1157 pw[ 0 ] = x + (y * mesh->width);
1158 pw[ 1 ] = x + ((y + 1) * mesh->width);
1159 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1160 pw[ 3 ] = x + 1 + (y * mesh->width);
1161 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1166 /* get drawverts and map first triangle */
1167 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1168 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1169 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1170 MapTriangle( lm, info, dv, mapNonAxial );
1172 /* get drawverts and map second triangle */
1173 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1174 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1175 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1176 MapTriangle( lm, info, dv, mapNonAxial );
1183 for( y = 0; y < (mesh->height - 1); y++ )
1185 for( x = 0; x < (mesh->width - 1); x++ )
1188 pw[ 0 ] = x + (y * mesh->width);
1189 pw[ 1 ] = x + ((y + 1) * mesh->width);
1190 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1191 pw[ 3 ] = x + 1 + (y * mesh->width);
1197 /* attempt to map quad first */
1198 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1199 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1200 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1201 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1202 if( MapQuad( lm, info, dv ) )
1205 /* get drawverts and map first triangle */
1206 MapTriangle( lm, info, dv, mapNonAxial );
1208 /* get drawverts and map second triangle */
1209 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1210 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1211 MapTriangle( lm, info, dv, mapNonAxial );
1226 /* -----------------------------------------------------------------
1227 average and clean up luxel normals
1228 ----------------------------------------------------------------- */
1230 /* walk the luxels */
1231 for( y = 0; y < lm->sh; y++ )
1233 for( x = 0; x < lm->sw; x++ )
1236 luxel = SUPER_LUXEL( 0, x, y );
1237 normal = SUPER_NORMAL( x, y );
1238 cluster = SUPER_CLUSTER( x, y );
1240 /* only look at mapped luxels */
1244 /* the normal data could be the sum of multiple samples */
1245 if( luxel[ 3 ] > 1.0f )
1246 VectorNormalize( normal, normal );
1248 /* mark this luxel as having only one normal */
1253 /* non-planar surfaces stop here */
1254 if( lm->plane == NULL )
1257 /* -----------------------------------------------------------------
1258 map occluded or unuxed luxels
1259 ----------------------------------------------------------------- */
1261 /* walk the luxels */
1262 radius = floor( superSample / 2 );
1263 radius = radius > 0 ? radius : 1.0f;
1265 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1267 for( y = 0; y < lm->sh; y++ )
1269 for( x = 0; x < lm->sw; x++ )
1272 luxel = SUPER_LUXEL( 0, x, y );
1273 normal = SUPER_NORMAL( x, y );
1274 cluster = SUPER_CLUSTER( x, y );
1276 /* only look at unmapped luxels */
1277 if( *cluster != CLUSTER_UNMAPPED )
1280 /* divine a normal and origin from neighboring luxels */
1281 VectorClear( fake.xyz );
1282 VectorClear( fake.normal );
1283 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1284 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1286 for( sy = (y - 1); sy <= (y + 1); sy++ )
1288 if( sy < 0 || sy >= lm->sh )
1291 for( sx = (x - 1); sx <= (x + 1); sx++ )
1293 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1296 /* get neighboring luxel */
1297 luxel = SUPER_LUXEL( 0, sx, sy );
1298 origin = SUPER_ORIGIN( sx, sy );
1299 normal = SUPER_NORMAL( sx, sy );
1300 cluster = SUPER_CLUSTER( sx, sy );
1302 /* only consider luxels mapped in previous passes */
1303 if( *cluster < 0 || luxel[ 0 ] >= pass )
1306 /* add its distinctiveness to our own */
1307 VectorAdd( fake.xyz, origin, fake.xyz );
1308 VectorAdd( fake.normal, normal, fake.normal );
1309 samples += luxel[ 3 ];
1314 if( samples == 0.0f )
1318 VectorDivide( fake.xyz, samples, fake.xyz );
1319 //% VectorDivide( fake.normal, samples, fake.normal );
1320 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1323 /* map the fake vert */
1324 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1329 /* -----------------------------------------------------------------
1330 average and clean up luxel normals
1331 ----------------------------------------------------------------- */
1333 /* walk the luxels */
1334 for( y = 0; y < lm->sh; y++ )
1336 for( x = 0; x < lm->sw; x++ )
1339 luxel = SUPER_LUXEL( 0, x, y );
1340 normal = SUPER_NORMAL( x, y );
1341 cluster = SUPER_CLUSTER( x, y );
1343 /* only look at mapped luxels */
1347 /* the normal data could be the sum of multiple samples */
1348 if( luxel[ 3 ] > 1.0f )
1349 VectorNormalize( normal, normal );
1351 /* mark this luxel as having only one normal */
1359 for( y = 0; y < lm->sh; y++ )
1361 for( x = 0; x < lm->sw; x++ )
1366 cluster = SUPER_CLUSTER( x, y );
1367 origin = SUPER_ORIGIN( x, y );
1368 normal = SUPER_NORMAL( x, y );
1369 luxel = SUPER_LUXEL( x, y );
1374 /* check if within the bounding boxes of all surfaces referenced */
1375 ClearBounds( mins, maxs );
1376 for( n = 0; n < lm->numLightSurfaces; n++ )
1379 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1380 TOL = info->sampleSize + 2;
1381 AddPointToBounds( info->mins, mins, maxs );
1382 AddPointToBounds( info->maxs, mins, maxs );
1383 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1384 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1385 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1390 if( n < lm->numLightSurfaces )
1393 /* report bogus origin */
1394 Sys_Printf( "%6d [%2d,%2d] (%4d): XYZ(%+4.1f %+4.1f %+4.1f) LO(%+4.1f %+4.1f %+4.1f) HI(%+4.1f %+4.1f %+4.1f) <%3.0f>\n",
1395 rawLightmapNum, x, y, *cluster,
1396 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1397 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1398 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1409 sets up dirtmap (ambient occlusion)
1412 #define DIRT_CONE_ANGLE 88 /* degrees */
1413 #define DIRT_NUM_ANGLE_STEPS 16
1414 #define DIRT_NUM_ELEVATION_STEPS 3
1415 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1417 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1418 static int numDirtVectors = 0;
1420 void SetupDirt( void )
1423 float angle, elevation, angleStep, elevationStep;
1427 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1429 /* calculate angular steps */
1430 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1431 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1435 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1437 /* iterate elevation */
1438 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1440 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1441 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1442 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1447 /* emit some statistics */
1448 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1454 calculates dirt value for a given sample
1457 float DirtForSample( trace_t *trace )
1460 float gatherDirt, outDirt, angle, elevation, ooDepth;
1461 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1467 if( trace == NULL || trace->cluster < 0 )
1472 ooDepth = 1.0f / dirtDepth;
1473 VectorCopy( trace->normal, normal );
1475 /* check if the normal is aligned to the world-up */
1476 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f )
1478 if( normal[ 2 ] == 1.0f )
1480 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1481 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1483 else if( normal[ 2 ] == -1.0f )
1485 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1486 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1491 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1492 CrossProduct( normal, worldUp, myRt );
1493 VectorNormalize( myRt, myRt );
1494 CrossProduct( myRt, normal, myUp );
1495 VectorNormalize( myUp, myUp );
1498 /* 1 = random mode, 0 (well everything else) = non-random mode */
1502 for( i = 0; i < numDirtVectors; i++ )
1504 /* get random vector */
1505 angle = Random() * DEG2RAD( 360.0f );
1506 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1507 temp[ 0 ] = cos( angle ) * sin( elevation );
1508 temp[ 1 ] = sin( angle ) * sin( elevation );
1509 temp[ 2 ] = cos( elevation );
1511 /* transform into tangent space */
1512 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1513 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1514 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1517 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1518 SetupTrace( trace );
1524 VectorSubtract( trace->hit, trace->origin, displacement );
1525 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1531 /* iterate through ordered vectors */
1532 for( i = 0; i < numDirtVectors; i++ )
1534 /* transform vector into tangent space */
1535 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1536 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1537 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1540 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1541 SetupTrace( trace );
1547 VectorSubtract( trace->hit, trace->origin, displacement );
1548 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1554 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1555 SetupTrace( trace );
1561 VectorSubtract( trace->hit, trace->origin, displacement );
1562 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1566 if( gatherDirt <= 0.0f )
1569 /* apply gain (does this even do much? heh) */
1570 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1571 if( outDirt > 1.0f )
1575 outDirt *= dirtScale;
1576 if( outDirt > 1.0f )
1579 /* return to sender */
1580 return 1.0f - outDirt;
1587 calculates dirty fraction for each luxel
1590 void DirtyRawLightmap( int rawLightmapNum )
1592 int i, x, y, sx, sy, *cluster;
1593 float *origin, *normal, *dirt, *dirt2, average, samples;
1595 surfaceInfo_t *info;
1599 /* bail if this number exceeds the number of raw lightmaps */
1600 if( rawLightmapNum >= numRawLightmaps )
1604 lm = &rawLightmaps[ rawLightmapNum ];
1607 trace.testOcclusion = qtrue;
1608 trace.forceSunlight = qfalse;
1609 trace.recvShadows = lm->recvShadows;
1610 trace.numSurfaces = lm->numLightSurfaces;
1611 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1612 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1613 trace.testAll = qfalse;
1615 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1616 trace.twoSided = qfalse;
1617 for( i = 0; i < trace.numSurfaces; i++ )
1620 info = &surfaceInfos[ trace.surfaces[ i ] ];
1622 /* check twosidedness */
1623 if( info->si->twoSided )
1625 trace.twoSided = qtrue;
1631 for( y = 0; y < lm->sh; y++ )
1633 for( x = 0; x < lm->sw; x++ )
1636 cluster = SUPER_CLUSTER( x, y );
1637 origin = SUPER_ORIGIN( x, y );
1638 normal = SUPER_NORMAL( x, y );
1639 dirt = SUPER_DIRT( x, y );
1641 /* set default dirt */
1644 /* only look at mapped luxels */
1649 trace.cluster = *cluster;
1650 VectorCopy( origin, trace.origin );
1651 VectorCopy( normal, trace.normal );
1654 *dirt = DirtForSample( &trace );
1658 /* testing no filtering */
1662 for( y = 0; y < lm->sh; y++ )
1664 for( x = 0; x < lm->sw; x++ )
1667 cluster = SUPER_CLUSTER( x, y );
1668 dirt = SUPER_DIRT( x, y );
1670 /* filter dirt by adjacency to unmapped luxels */
1673 for( sy = (y - 1); sy <= (y + 1); sy++ )
1675 if( sy < 0 || sy >= lm->sh )
1678 for( sx = (x - 1); sx <= (x + 1); sx++ )
1680 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1683 /* get neighboring luxel */
1684 cluster = SUPER_CLUSTER( sx, sy );
1685 dirt2 = SUPER_DIRT( sx, sy );
1686 if( *cluster < 0 || *dirt2 <= 0.0f )
1695 if( samples <= 0.0f )
1700 if( samples <= 0.0f )
1704 *dirt = average / samples;
1713 calculates the pvs cluster, origin, normal of a sub-luxel
1716 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1718 int i, *cluster, *cluster2;
1719 float *origin, *origin2, *normal; //% , *normal2;
1720 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1723 /* calulate x vector */
1724 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1726 cluster = SUPER_CLUSTER( x, y );
1727 origin = SUPER_ORIGIN( x, y );
1728 //% normal = SUPER_NORMAL( x, y );
1729 cluster2 = SUPER_CLUSTER( x + 1, y );
1730 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1731 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1733 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1735 cluster = SUPER_CLUSTER( x - 1, y );
1736 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1737 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1738 cluster2 = SUPER_CLUSTER( x, y );
1739 origin2 = SUPER_ORIGIN( x, y );
1740 //% normal2 = SUPER_NORMAL( x, y );
1743 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1745 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1746 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1748 /* calulate y vector */
1749 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1751 cluster = SUPER_CLUSTER( x, y );
1752 origin = SUPER_ORIGIN( x, y );
1753 //% normal = SUPER_NORMAL( x, y );
1754 cluster2 = SUPER_CLUSTER( x, y + 1 );
1755 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1756 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1758 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1760 cluster = SUPER_CLUSTER( x, y - 1 );
1761 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1762 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1763 cluster2 = SUPER_CLUSTER( x, y );
1764 origin2 = SUPER_ORIGIN( x, y );
1765 //% normal2 = SUPER_NORMAL( x, y );
1768 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1770 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1771 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1773 /* calculate new origin */
1774 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1775 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1776 for( i = 0; i < 3; i++ )
1777 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1780 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1781 if( *sampleCluster < 0 )
1784 /* calculate new normal */
1785 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1786 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1787 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1789 normal = SUPER_NORMAL( x, y );
1790 VectorCopy( normal, sampleNormal );
1798 SubsampleRawLuxel_r()
1799 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1802 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )
1804 int b, samples, mapped, lighted;
1807 vec3_t origin[ 4 ], normal[ 4 ];
1808 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1809 vec3_t color, total;
1813 if( lightLuxel[ 3 ] >= lightSamples )
1817 VectorClear( total );
1821 /* make 2x2 subsample stamp */
1822 for( b = 0; b < 4; b++ )
1825 VectorCopy( sampleOrigin, origin[ b ] );
1827 /* calculate position */
1828 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1835 /* increment sample count */
1836 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1839 trace->cluster = *cluster;
1840 VectorCopy( origin[ b ], trace->origin );
1841 VectorCopy( normal[ b ], trace->normal );
1845 LightContributionToSample( trace );
1847 /* add to totals (fixme: make contrast function) */
1848 VectorCopy( trace->color, luxel[ b ] );
1849 VectorAdd( total, trace->color, total );
1850 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1854 /* subsample further? */
1855 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1856 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1857 lighted != 0 && lighted != mapped )
1859 for( b = 0; b < 4; b++ )
1861 if( cluster[ b ] < 0 )
1863 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.25f), luxel[ b ] );
1868 //% VectorClear( color );
1870 VectorCopy( lightLuxel, color );
1872 for( b = 0; b < 4; b++ )
1874 if( cluster[ b ] < 0 )
1876 VectorAdd( color, luxel[ b ], color );
1884 color[ 0 ] /= samples;
1885 color[ 1 ] /= samples;
1886 color[ 2 ] /= samples;
1889 VectorCopy( color, lightLuxel );
1890 lightLuxel[ 3 ] += 1.0f;
1897 IlluminateRawLightmap()
1898 illuminates the luxels
1901 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1902 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1904 void IlluminateRawLightmap( int rawLightmapNum )
1906 int i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
1907 int *cluster, *cluster2, mapped, lighted, totalLighted;
1909 surfaceInfo_t *info;
1910 qboolean filterColor, filterDir;
1912 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1913 float *lightLuxels, *lightLuxel, samples, filterRadius, weight;
1914 vec3_t color, averageColor, total, temp, temp2;
1915 float averageDir[4];
1916 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1918 float stackLightLuxels[ STACK_LL_SIZE ];
1923 /* bail if this number exceeds the number of raw lightmaps */
1924 if( rawLightmapNum >= numRawLightmaps )
1928 lm = &rawLightmaps[ rawLightmapNum ];
1931 trace.testOcclusion = !noTrace;
1932 trace.forceSunlight = qfalse;
1933 trace.recvShadows = lm->recvShadows;
1934 trace.numSurfaces = lm->numLightSurfaces;
1935 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1936 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1938 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1939 trace.twoSided = qfalse;
1940 for( i = 0; i < trace.numSurfaces; i++ )
1943 info = &surfaceInfos[ trace.surfaces[ i ] ];
1945 /* check twosidedness */
1946 if( info->si->twoSided )
1948 trace.twoSided = qtrue;
1953 /* create a culled light list for this raw lightmap */
1954 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
1956 /* -----------------------------------------------------------------
1958 ----------------------------------------------------------------- */
1961 numLuxelsIlluminated += (lm->sw * lm->sh);
1963 /* test debugging state */
1964 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
1966 /* debug fill the luxels */
1967 for( y = 0; y < lm->sh; y++ )
1969 for( x = 0; x < lm->sw; x++ )
1972 cluster = SUPER_CLUSTER( x, y );
1974 /* only fill mapped luxels */
1978 /* get particulars */
1979 luxel = SUPER_LUXEL( 0, x, y );
1980 origin = SUPER_ORIGIN( x, y );
1981 normal = SUPER_NORMAL( x, y );
1983 /* color the luxel with raw lightmap num? */
1985 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
1987 /* color the luxel with lightmap axis? */
1988 else if( debugAxis )
1990 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
1991 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
1992 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
1995 /* color the luxel with luxel cluster? */
1996 else if( debugCluster )
1997 VectorCopy( debugColors[ *cluster % 12 ], luxel );
1999 /* color the luxel with luxel origin? */
2000 else if( debugOrigin )
2002 VectorSubtract( lm->maxs, lm->mins, temp );
2003 VectorScale( temp, (1.0f / 255.0f), temp );
2004 VectorSubtract( origin, lm->mins, temp2 );
2005 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2006 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2007 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2010 /* color the luxel with the normal */
2011 else if( normalmap )
2013 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
2014 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
2015 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
2018 /* otherwise clear it */
2020 VectorClear( luxel );
2029 /* allocate temporary per-light luxel storage */
2030 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2031 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2032 lightLuxels = stackLightLuxels;
2034 lightLuxels = safe_malloc( llSize );
2037 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2039 /* set ambient color */
2040 for( y = 0; y < lm->sh; y++ )
2042 for( x = 0; x < lm->sw; x++ )
2045 cluster = SUPER_CLUSTER( x, y );
2046 luxel = SUPER_LUXEL( 0, x, y );
2047 normal = SUPER_NORMAL( x, y );
2048 deluxel = SUPER_DELUXEL( x, y );
2050 /* blacken unmapped clusters */
2052 VectorClear( luxel );
2057 VectorCopy( ambientColor, luxel );
2060 VectorScale( normal, 0.00390625f, deluxel );
2061 deluxel[3] = 0.00390625f;
2068 /* clear styled lightmaps */
2069 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2070 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2072 if( lm->superLuxels[ lightmapNum ] != NULL )
2073 memset( lm->superLuxels[ lightmapNum ], 0, size );
2076 /* debugging code */
2077 //% if( trace.numLights <= 0 )
2078 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2080 /* walk light list */
2081 for( i = 0; i < trace.numLights; i++ )
2084 trace.light = trace.lights[ i ];
2087 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2089 if( lm->styles[ lightmapNum ] == trace.light->style ||
2090 lm->styles[ lightmapNum ] == LS_NONE )
2094 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2095 if( lightmapNum >= MAX_LIGHTMAPS )
2097 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2102 memset( lightLuxels, 0, llSize );
2105 /* initial pass, one sample per luxel */
2106 for( y = 0; y < lm->sh; y++ )
2108 for( x = 0; x < lm->sw; x++ )
2111 cluster = SUPER_CLUSTER( x, y );
2115 /* get particulars */
2116 lightLuxel = LIGHT_LUXEL( x, y );
2117 deluxel = SUPER_DELUXEL( x, y );
2118 origin = SUPER_ORIGIN( x, y );
2119 normal = SUPER_NORMAL( x, y );
2122 ////////// 27's temp hack for testing edge clipping ////
2123 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2125 lightLuxel[ 1 ] = 255;
2126 lightLuxel[ 3 ] = 1.0f;
2132 /* set contribution count */
2133 lightLuxel[ 3 ] = 1.0f;
2136 trace.cluster = *cluster;
2137 VectorCopy( origin, trace.origin );
2138 VectorCopy( normal, trace.normal );
2140 /* get light for this sample */
2141 LightContributionToSample( &trace );
2142 VectorCopy( trace.color, lightLuxel );
2145 if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2149 /* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */
2152 /* color to grayscale (photoshop rgb weighting) */
2153 brightness = trace.color[ 0 ] * 0.3f + trace.color[ 1 ] * 0.59f + trace.color[ 2 ] * 0.11f;
2154 brightness *= (1.0 / 255.0);
2157 VectorScale( trace.direction, brightness, temp );
2158 VectorAdd( deluxel, temp, deluxel );
2159 deluxel[3] += brightness;
2165 /* don't even bother with everything else if nothing was lit */
2166 if( totalLighted == 0 )
2169 /* determine filter radius */
2170 filterRadius = lm->filterRadius > trace.light->filterRadius
2172 : trace.light->filterRadius;
2173 if( filterRadius < 0.0f )
2174 filterRadius = 0.0f;
2176 /* set luxel filter radius */
2177 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2178 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2179 luxelFilterRadius = 1;
2181 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2182 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2183 if( lightSamples > 1 && luxelFilterRadius == 0 )
2186 for( y = 0; y < (lm->sh - 1); y++ )
2188 for( x = 0; x < (lm->sw - 1); x++ )
2193 VectorClear( total );
2195 /* test 2x2 stamp */
2196 for( t = 0; t < 4; t++ )
2198 /* set sample coords */
2199 sx = x + tests[ t ][ 0 ];
2200 sy = y + tests[ t ][ 1 ];
2203 cluster = SUPER_CLUSTER( sx, sy );
2209 lightLuxel = LIGHT_LUXEL( sx, sy );
2210 VectorAdd( total, lightLuxel, total );
2211 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2215 /* if total color is under a certain amount, then don't bother subsampling */
2216 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2219 /* if all 4 pixels are either in shadow or light, then don't subsample */
2220 if( lighted != 0 && lighted != mapped )
2222 for( t = 0; t < 4; t++ )
2224 /* set sample coords */
2225 sx = x + tests[ t ][ 0 ];
2226 sy = y + tests[ t ][ 1 ];
2229 cluster = SUPER_CLUSTER( sx, sy );
2232 lightLuxel = LIGHT_LUXEL( sx, sy );
2233 origin = SUPER_ORIGIN( sx, sy );
2235 /* only subsample shadowed luxels */
2236 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2240 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f, lightLuxel );
2242 /* debug code to colorize subsampled areas to yellow */
2243 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2244 //% VectorSet( luxel, 255, 204, 0 );
2251 /* tertiary pass, apply dirt map (ambient occlusion) */
2255 for( y = 0; y < lm->sh; y++ )
2257 for( x = 0; x < lm->sw; x++ )
2260 cluster = SUPER_CLUSTER( x, y );
2264 /* get particulars */
2265 lightLuxel = LIGHT_LUXEL( x, y );
2266 dirt = SUPER_DIRT( x, y );
2268 /* scale light value */
2269 VectorScale( lightLuxel, *dirt, lightLuxel );
2274 /* allocate sampling lightmap storage */
2275 if( lm->superLuxels[ lightmapNum ] == NULL )
2277 /* allocate sampling lightmap storage */
2278 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2279 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2280 memset( lm->superLuxels[ lightmapNum ], 0, size );
2284 if( lightmapNum > 0 )
2286 lm->styles[ lightmapNum ] = trace.light->style;
2287 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2290 /* copy to permanent luxels */
2291 for( y = 0; y < lm->sh; y++ )
2293 for( x = 0; x < lm->sw; x++ )
2295 /* get cluster and origin */
2296 cluster = SUPER_CLUSTER( x, y );
2299 origin = SUPER_ORIGIN( x, y );
2302 if( luxelFilterRadius )
2305 VectorClear( averageColor );
2308 /* cheaper distance-based filtering */
2309 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2311 if( sy < 0 || sy >= lm->sh )
2314 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2316 if( sx < 0 || sx >= lm->sw )
2319 /* get particulars */
2320 cluster = SUPER_CLUSTER( sx, sy );
2323 lightLuxel = LIGHT_LUXEL( sx, sy );
2326 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2327 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2329 /* scale luxel by filter weight */
2330 VectorScale( lightLuxel, weight, color );
2331 VectorAdd( averageColor, color, averageColor );
2337 if( samples <= 0.0f )
2340 /* scale into luxel */
2341 luxel = SUPER_LUXEL( lightmapNum, x, y );
2344 /* handle negative light */
2345 if( trace.light->flags & LIGHT_NEGATIVE )
2347 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2348 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2349 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2352 /* handle normal light */
2355 luxel[ 0 ] += averageColor[ 0 ] / samples;
2356 luxel[ 1 ] += averageColor[ 1 ] / samples;
2357 luxel[ 2 ] += averageColor[ 2 ] / samples;
2364 /* get particulars */
2365 lightLuxel = LIGHT_LUXEL( x, y );
2366 luxel = SUPER_LUXEL( lightmapNum, x, y );
2368 /* handle negative light */
2369 if( trace.light->flags & LIGHT_NEGATIVE )
2370 VectorScale( averageColor, -1.0f, averageColor );
2375 /* handle negative light */
2376 if( trace.light->flags & LIGHT_NEGATIVE )
2377 VectorSubtract( luxel, lightLuxel, luxel );
2379 /* handle normal light */
2381 VectorAdd( luxel, lightLuxel, luxel );
2387 /* free temporary luxels */
2388 if( lightLuxels != stackLightLuxels )
2389 free( lightLuxels );
2392 /* free light list */
2393 FreeTraceLights( &trace );
2395 /* -----------------------------------------------------------------
2397 ----------------------------------------------------------------- */
2401 /* walk lightmaps */
2402 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2405 if( lm->superLuxels[ lightmapNum ] == NULL )
2408 /* apply floodlight to each luxel */
2409 for( y = 0; y < lm->sh; y++ )
2411 for( x = 0; x < lm->sw; x++ )
2414 cluster = SUPER_CLUSTER( x, y );
2418 /* get particulars */
2419 luxel = SUPER_LUXEL( lightmapNum, x, y );
2420 floodlight = SUPER_FLOODLIGHT( x, y );
2422 flood[0]=floodlightRGB[0]*floodlightIntensity;
2423 flood[1]=floodlightRGB[1]*floodlightIntensity;
2424 flood[2]=floodlightRGB[2]*floodlightIntensity;
2426 /* scale light value */
2427 VectorScale( flood, *floodlight, flood );
2432 if (luxel[3]==0) luxel[3]=1;
2440 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2443 if( lm->superLuxels[ lightmapNum ] == NULL )
2446 for( y = 0; y < lm->sh; y++ )
2448 for( x = 0; x < lm->sw; x++ )
2451 cluster = SUPER_CLUSTER( x, y );
2452 //% if( *cluster < 0 )
2455 /* get particulars */
2456 luxel = SUPER_LUXEL( lightmapNum, x, y );
2457 normal = SUPER_NORMAL ( x, y );
2459 luxel[0]=(normal[0]*127)+127;
2460 luxel[1]=(normal[1]*127)+127;
2461 luxel[2]=(normal[2]*127)+127;
2467 /* -----------------------------------------------------------------
2469 ----------------------------------------------------------------- */
2473 /* walk lightmaps */
2474 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2477 if( lm->superLuxels[ lightmapNum ] == NULL )
2480 /* apply dirt to each luxel */
2481 for( y = 0; y < lm->sh; y++ )
2483 for( x = 0; x < lm->sw; x++ )
2486 cluster = SUPER_CLUSTER( x, y );
2487 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2490 /* get particulars */
2491 luxel = SUPER_LUXEL( lightmapNum, x, y );
2492 dirt = SUPER_DIRT( x, y );
2495 VectorScale( luxel, *dirt, luxel );
2499 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2505 /* -----------------------------------------------------------------
2507 ----------------------------------------------------------------- */
2509 /* walk lightmaps */
2510 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2513 if( lm->superLuxels[ lightmapNum ] == NULL )
2516 /* average occluded luxels from neighbors */
2517 for( y = 0; y < lm->sh; y++ )
2519 for( x = 0; x < lm->sw; x++ )
2521 /* get particulars */
2522 cluster = SUPER_CLUSTER( x, y );
2523 luxel = SUPER_LUXEL( lightmapNum, x, y );
2524 deluxel = SUPER_DELUXEL( x, y );
2525 normal = SUPER_NORMAL( x, y );
2527 /* determine if filtering is necessary */
2528 filterColor = qfalse;
2531 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2532 filterColor = qtrue;
2533 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2536 if( !filterColor && !filterDir )
2539 /* choose seed amount */
2540 VectorClear( averageColor );
2541 VectorClear( averageDir );
2542 averageDir[3] = 0.0f;
2545 /* walk 3x3 matrix */
2546 for( sy = (y - 1); sy <= (y + 1); sy++ )
2548 if( sy < 0 || sy >= lm->sh )
2551 for( sx = (x - 1); sx <= (x + 1); sx++ )
2553 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2556 /* get neighbor's particulars */
2557 cluster2 = SUPER_CLUSTER( sx, sy );
2558 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2559 deluxel2 = SUPER_DELUXEL( sx, sy );
2561 /* ignore unmapped/unlit luxels */
2562 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2563 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2566 /* add its distinctiveness to our own */
2567 VectorAdd( averageColor, luxel2, averageColor );
2568 samples += luxel2[ 3 ];
2571 VectorAdd( averageDir, deluxel2, averageDir );
2572 averageDir[3] += deluxel2[3];
2578 if( samples <= 0.0f )
2581 /* dark lightmap seams */
2584 if( lightmapNum == 0 )
2585 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2592 VectorDivide( averageColor, samples, luxel );
2597 VectorDivide( averageDir, samples, deluxel );
2598 deluxel[3] = averageDir[3] / samples;
2601 /* set cluster to -3 */
2603 *cluster = CLUSTER_FLOODED;
2611 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2614 if( lm->superLuxels[ lightmapNum ] == NULL )
2616 for( y = 0; y < lm->sh; y++ )
2617 for( x = 0; x < lm->sw; x++ )
2620 cluster = SUPER_CLUSTER( x, y );
2621 luxel = SUPER_LUXEL( lightmapNum, x, y );
2622 deluxel = SUPER_DELUXEL( x, y );
2623 if(!luxel || !deluxel || !cluster)
2625 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2628 else if(*cluster < 0)
2631 // should have neither deluxemap nor lightmap
2633 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2638 // should have both deluxemap and lightmap
2640 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2650 IlluminateVertexes()
2651 light the surface vertexes
2654 #define VERTEX_NUDGE 4.0f
2656 void IlluminateVertexes( int num )
2658 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2659 int lightmapNum, numAvg;
2660 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2661 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2662 bspDrawSurface_t *ds;
2663 surfaceInfo_t *info;
2665 bspDrawVert_t *verts;
2669 /* get surface, info, and raw lightmap */
2670 ds = &bspDrawSurfaces[ num ];
2671 info = &surfaceInfos[ num ];
2674 /* -----------------------------------------------------------------
2675 illuminate the vertexes
2676 ----------------------------------------------------------------- */
2678 /* calculate vertex lighting for surfaces without lightmaps */
2679 if( lm == NULL || cpmaHack )
2682 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2683 trace.forceSunlight = info->si->forceSunlight;
2684 trace.recvShadows = info->recvShadows;
2685 trace.numSurfaces = 1;
2686 trace.surfaces = #
2687 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2689 /* twosided lighting */
2690 trace.twoSided = info->si->twoSided;
2692 /* make light list for this surface */
2693 CreateTraceLightsForSurface( num, &trace );
2696 verts = yDrawVerts + ds->firstVert;
2698 memset( avgColors, 0, sizeof( avgColors ) );
2700 /* walk the surface verts */
2701 for( i = 0; i < ds->numVerts; i++ )
2703 /* get vertex luxel */
2704 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2706 /* color the luxel with raw lightmap num? */
2708 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2710 /* color the luxel with luxel origin? */
2711 else if( debugOrigin )
2713 VectorSubtract( info->maxs, info->mins, temp );
2714 VectorScale( temp, (1.0f / 255.0f), temp );
2715 VectorSubtract( origin, lm->mins, temp2 );
2716 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2717 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2718 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2721 /* color the luxel with the normal */
2722 else if( normalmap )
2724 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2725 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2726 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2729 /* illuminate the vertex */
2732 /* clear vertex luxel */
2733 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2735 /* try at initial origin */
2736 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2737 if( trace.cluster >= 0 )
2740 VectorCopy( verts[ i ].xyz, trace.origin );
2741 VectorCopy( verts[ i ].normal, trace.normal );
2745 dirt = DirtForSample( &trace );
2750 LightingAtSample( &trace, ds->vertexStyles, colors );
2753 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2756 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2759 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2760 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2761 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2765 /* is this sample bright enough? */
2766 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2767 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2768 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2769 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2771 /* nudge the sample point around a bit */
2772 for( x = 0; x < 4; x++ )
2774 /* two's complement 0, 1, -1, 2, -2, etc */
2775 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2777 for( y = 0; y < 4; y++ )
2779 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2781 for( z = 0; z < 4; z++ )
2783 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2786 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2787 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2788 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2790 /* try at nudged origin */
2791 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2792 if( trace.cluster < 0 )
2796 LightingAtSample( &trace, ds->vertexStyles, colors );
2799 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2802 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2805 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2806 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2809 /* bright enough? */
2810 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2811 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2812 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2813 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2820 /* add to average? */
2821 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2822 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2823 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2824 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2827 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2829 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2830 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2835 /* another happy customer */
2836 numVertsIlluminated++;
2839 /* set average color */
2842 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2843 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2847 VectorCopy( ambientColor, avgColors[ 0 ] );
2850 /* clean up and store vertex color */
2851 for( i = 0; i < ds->numVerts; i++ )
2853 /* get vertex luxel */
2854 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2856 /* store average in occluded vertexes */
2857 if( radVertLuxel[ 0 ] < 0.0f )
2859 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2861 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2862 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2865 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2870 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2873 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2874 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2877 if( bouncing || bounce == 0 || !bounceOnly )
2878 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2879 if( !info->si->noVertexLight )
2880 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2884 /* free light list */
2885 FreeTraceLights( &trace );
2887 /* return to sender */
2891 /* -----------------------------------------------------------------
2892 reconstitute vertex lighting from the luxels
2893 ----------------------------------------------------------------- */
2895 /* set styles from lightmap */
2896 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2897 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2899 /* get max search radius */
2901 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
2903 /* walk the surface verts */
2904 verts = yDrawVerts + ds->firstVert;
2905 for( i = 0; i < ds->numVerts; i++ )
2907 /* do each lightmap */
2908 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2911 if( lm->superLuxels[ lightmapNum ] == NULL )
2914 /* get luxel coords */
2915 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
2916 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
2919 else if( x >= lm->sw )
2923 else if( y >= lm->sh )
2926 /* get vertex luxels */
2927 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2928 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2930 /* color the luxel with the normal? */
2933 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2934 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2935 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2938 /* color the luxel with surface num? */
2939 else if( debugSurfaces )
2940 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2942 /* divine color from the superluxels */
2945 /* increasing radius */
2946 VectorClear( radVertLuxel );
2948 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
2950 /* sample within radius */
2951 for( sy = (y - radius); sy <= (y + radius); sy++ )
2953 if( sy < 0 || sy >= lm->sh )
2956 for( sx = (x - radius); sx <= (x + radius); sx++ )
2958 if( sx < 0 || sx >= lm->sw )
2961 /* get luxel particulars */
2962 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2963 cluster = SUPER_CLUSTER( sx, sy );
2967 /* testing: must be brigher than ambient color */
2968 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
2971 /* add its distinctiveness to our own */
2972 VectorAdd( radVertLuxel, luxel, radVertLuxel );
2973 samples += luxel[ 3 ];
2979 if( samples > 0.0f )
2980 VectorDivide( radVertLuxel, samples, radVertLuxel );
2982 VectorCopy( ambientColor, radVertLuxel );
2985 /* store into floating point storage */
2986 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2987 numVertsIlluminated++;
2989 /* store into bytes (for vertex approximation) */
2990 if( !info->si->noVertexLight )
2991 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
2998 /* -------------------------------------------------------------------------------
3000 light optimization (-fast)
3002 creates a list of lights that will affect a surface and stores it in tw
3003 this is to optimize surface lighting by culling out as many of the
3004 lights in the world as possible from further calculation
3006 ------------------------------------------------------------------------------- */
3010 determines opaque brushes in the world and find sky shaders for sunlight calculations
3013 void SetupBrushes( void )
3015 int i, j, b, compileFlags;
3018 bspBrushSide_t *side;
3019 bspShader_t *shader;
3024 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3027 if( opaqueBrushes == NULL )
3028 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3031 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3032 numOpaqueBrushes = 0;
3034 /* walk the list of worldspawn brushes */
3035 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3038 b = bspModels[ 0 ].firstBSPBrush + i;
3039 brush = &bspBrushes[ b ];
3041 /* check all sides */
3044 for( j = 0; j < brush->numSides && inside; j++ )
3046 /* do bsp shader calculations */
3047 side = &bspBrushSides[ brush->firstSide + j ];
3048 shader = &bspShaders[ side->shaderNum ];
3050 /* get shader info */
3051 si = ShaderInfoForShader( shader->shader );
3055 /* or together compile flags */
3056 compileFlags |= si->compileFlags;
3059 /* determine if this brush is opaque to light */
3060 if( !(compileFlags & C_TRANSLUCENT) )
3062 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3068 /* emit some statistics */
3069 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3076 determines if two clusters are visible to each other using the PVS
3079 qboolean ClusterVisible( int a, int b )
3081 int portalClusters, leafBytes;
3086 if( a < 0 || b < 0 )
3094 if( numBSPVisBytes <=8 )
3098 portalClusters = ((int *) bspVisBytes)[ 0 ];
3099 leafBytes = ((int*) bspVisBytes)[ 1 ];
3100 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3103 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3112 borrowed from vlight.c
3115 int PointInLeafNum_r( vec3_t point, int nodenum )
3123 while( nodenum >= 0 )
3125 node = &bspNodes[ nodenum ];
3126 plane = &bspPlanes[ node->planeNum ];
3127 dist = DotProduct( point, plane->normal ) - plane->dist;
3129 nodenum = node->children[ 0 ];
3130 else if( dist < -0.1 )
3131 nodenum = node->children[ 1 ];
3134 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3135 if( bspLeafs[ leafnum ].cluster != -1 )
3137 nodenum = node->children[ 1 ];
3141 leafnum = -nodenum - 1;
3149 borrowed from vlight.c
3152 int PointInLeafNum( vec3_t point )
3154 return PointInLeafNum_r( point, 0 );
3160 ClusterVisibleToPoint() - ydnar
3161 returns qtrue if point can "see" cluster
3164 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3169 /* get leafNum for point */
3170 pointCluster = ClusterForPoint( point );
3171 if( pointCluster < 0 )
3175 return ClusterVisible( pointCluster, cluster );
3181 ClusterForPoint() - ydnar
3182 returns the pvs cluster for point
3185 int ClusterForPoint( vec3_t point )
3190 /* get leafNum for point */
3191 leafNum = PointInLeafNum( point );
3195 /* return the cluster */
3196 return bspLeafs[ leafNum ].cluster;
3202 ClusterForPointExt() - ydnar
3203 also takes brushes into account for occlusion testing
3206 int ClusterForPointExt( vec3_t point, float epsilon )
3208 int i, j, b, leafNum, cluster;
3211 int *brushes, numBSPBrushes;
3217 /* get leaf for point */
3218 leafNum = PointInLeafNum( point );
3221 leaf = &bspLeafs[ leafNum ];
3223 /* get the cluster */
3224 cluster = leaf->cluster;
3228 /* transparent leaf, so check point against all brushes in the leaf */
3229 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3230 numBSPBrushes = leaf->numBSPLeafBrushes;
3231 for( i = 0; i < numBSPBrushes; i++ )
3235 if( b > maxOpaqueBrush )
3237 brush = &bspBrushes[ b ];
3238 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3241 /* check point against all planes */
3243 for( j = 0; j < brush->numSides && inside; j++ )
3245 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3246 dot = DotProduct( point, plane->normal );
3252 /* if inside, return bogus cluster */
3257 /* if the point made it this far, it's not inside any opaque brushes */
3264 ClusterForPointExtFilter() - ydnar
3265 adds cluster checking against a list of known valid clusters
3268 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3273 /* get cluster for point */
3274 cluster = ClusterForPointExt( point, epsilon );
3276 /* check if filtering is necessary */
3277 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3281 for( i = 0; i < numClusters; i++ )
3283 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3294 ShaderForPointInLeaf() - ydnar
3295 checks a point against all brushes in a leaf, returning the shader of the brush
3296 also sets the cumulative surface and content flags for the brush hit
3299 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3304 int *brushes, numBSPBrushes;
3307 bspBrushSide_t *side;
3309 bspShader_t *shader;
3310 int allSurfaceFlags, allContentFlags;
3313 /* clear things out first */
3320 leaf = &bspLeafs[ leafNum ];
3322 /* transparent leaf, so check point against all brushes in the leaf */
3323 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3324 numBSPBrushes = leaf->numBSPLeafBrushes;
3325 for( i = 0; i < numBSPBrushes; i++ )
3328 brush = &bspBrushes[ brushes[ i ] ];
3330 /* check point against all planes */
3332 allSurfaceFlags = 0;
3333 allContentFlags = 0;
3334 for( j = 0; j < brush->numSides && inside; j++ )
3336 side = &bspBrushSides[ brush->firstSide + j ];
3337 plane = &bspPlanes[ side->planeNum ];
3338 dot = DotProduct( point, plane->normal );
3344 shader = &bspShaders[ side->shaderNum ];
3345 allSurfaceFlags |= shader->surfaceFlags;
3346 allContentFlags |= shader->contentFlags;
3350 /* handle if inside */
3353 /* if there are desired flags, check for same and continue if they aren't matched */
3354 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3356 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3359 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3360 *surfaceFlags = allSurfaceFlags;
3361 *contentFlags = allContentFlags;
3362 return brush->shaderNum;
3366 /* if the point made it this far, it's not inside any brushes */
3374 chops a bounding box by the plane defined by origin and normal
3375 returns qfalse if the bounds is entirely clipped away
3377 this is not exactly the fastest way to do this...
3380 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3382 /* FIXME: rewrite this so it doesn't use bloody brushes */
3390 calculates each light's effective envelope,
3391 taking into account brightness, type, and pvs.
3394 #define LIGHT_EPSILON 0.125f
3395 #define LIGHT_NUDGE 2.0f
3397 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3399 int i, x, y, z, x1, y1, z1;
3400 light_t *light, *light2, **owner;
3402 vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
3403 float radius, intensity;
3404 light_t *buckets[ 256 ];
3407 /* early out for weird cases where there are no lights */
3408 if( lights == NULL )
3412 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3416 numCulledLights = 0;
3418 while( *owner != NULL )
3423 /* handle negative lights */
3424 if( light->photons < 0.0f || light->add < 0.0f )
3426 light->photons *= -1.0f;
3427 light->add *= -1.0f;
3428 light->flags |= LIGHT_NEGATIVE;
3432 if( light->type == EMIT_SUN )
3436 light->envelope = MAX_WORLD_COORD * 8.0f;
3437 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3438 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3441 /* everything else */
3444 /* get pvs cluster for light */
3445 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3447 /* invalid cluster? */
3448 if( light->cluster < 0 )
3450 /* nudge the sample point around a bit */
3451 for( x = 0; x < 4; x++ )
3453 /* two's complement 0, 1, -1, 2, -2, etc */
3454 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3456 for( y = 0; y < 4; y++ )
3458 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3460 for( z = 0; z < 4; z++ )
3462 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3465 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3466 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3467 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3469 /* try at nudged origin */
3470 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3471 if( light->cluster < 0 )
3475 VectorCopy( origin, light->origin );
3481 /* only calculate for lights in pvs and outside of opaque brushes */
3482 if( light->cluster >= 0 )
3484 /* set light fast flag */
3486 light->flags |= LIGHT_FAST_TEMP;
3488 light->flags &= ~LIGHT_FAST_TEMP;
3489 if( light->si && light->si->noFast )
3490 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3492 /* clear light envelope */
3493 light->envelope = 0;
3495 /* handle area lights */
3496 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3498 /* ugly hack to calculate extent for area lights, but only done once */
3499 VectorScale( light->normal, -1.0f, dir );
3500 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3504 VectorMA( light->origin, radius, light->normal, origin );
3505 factor = PointToPolygonFormFactor( origin, dir, light->w );
3508 if( (factor * light->add) <= light->falloffTolerance )
3509 light->envelope = radius;
3512 /* check for fast mode */
3513 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3514 light->envelope = MAX_WORLD_COORD * 8.0f;
3519 intensity = light->photons;
3523 if( light->envelope <= 0.0f )
3525 /* solve distance for non-distance lights */
3526 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3527 light->envelope = MAX_WORLD_COORD * 8.0f;
3529 /* solve distance for linear lights */
3530 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3531 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3532 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3535 add = angle * light->photons * linearScale - (dist * light->fade);
3536 T = (light->photons * linearScale) - (dist * light->fade);
3537 T + (dist * light->fade) = (light->photons * linearScale);
3538 dist * light->fade = (light->photons * linearScale) - T;
3539 dist = ((light->photons * linearScale) - T) / light->fade;
3542 /* solve for inverse square falloff */
3544 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3547 add = light->photons / (dist * dist);
3548 T = light->photons / (dist * dist);
3549 T * (dist * dist) = light->photons;
3550 dist = sqrt( light->photons / T );
3554 /* chop radius against pvs */
3557 ClearBounds( mins, maxs );
3559 /* check all leaves */
3560 for( i = 0; i < numBSPLeafs; i++ )
3563 leaf = &bspLeafs[ i ];
3566 if( leaf->cluster < 0 )
3568 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3571 /* add this leafs bbox to the bounds */
3572 VectorCopy( leaf->mins, origin );
3573 AddPointToBounds( origin, mins, maxs );
3574 VectorCopy( leaf->maxs, origin );
3575 AddPointToBounds( origin, mins, maxs );
3578 /* test to see if bounds encompass light */
3579 for( i = 0; i < 3; i++ )
3581 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3583 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3584 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3585 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3586 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3587 AddPointToBounds( light->origin, mins, maxs );
3591 /* chop the bounds by a plane for area lights and spotlights */
3592 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3593 ChopBounds( mins, maxs, light->origin, light->normal );
3596 VectorCopy( mins, light->mins );
3597 VectorCopy( maxs, light->maxs );
3599 /* reflect bounds around light origin */
3600 //% VectorMA( light->origin, -1.0f, origin, origin );
3601 VectorScale( light->origin, 2, origin );
3602 VectorSubtract( origin, maxs, origin );
3603 AddPointToBounds( origin, mins, maxs );
3604 //% VectorMA( light->origin, -1.0f, mins, origin );
3605 VectorScale( light->origin, 2, origin );
3606 VectorSubtract( origin, mins, origin );
3607 AddPointToBounds( origin, mins, maxs );
3609 /* calculate spherical bounds */
3610 VectorSubtract( maxs, light->origin, dir );
3611 radius = (float) VectorLength( dir );
3613 /* if this radius is smaller than the envelope, then set the envelope to it */
3614 if( radius < light->envelope )
3616 light->envelope = radius;
3617 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3620 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3623 /* add grid/surface only check */
3626 if( !(light->flags & LIGHT_GRID) )
3627 light->envelope = 0.0f;
3631 if( !(light->flags & LIGHT_SURFACES) )
3632 light->envelope = 0.0f;
3637 if( light->cluster < 0 || light->envelope <= 0.0f )
3640 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3642 /* delete the light */
3644 *owner = light->next;
3645 if( light->w != NULL )
3652 /* square envelope */
3653 light->envelope2 = (light->envelope * light->envelope);
3655 /* increment light count */
3658 /* set next light */
3659 owner = &((**owner).next);
3662 /* bucket sort lights by style */
3663 memset( buckets, 0, sizeof( buckets ) );
3665 for( light = lights; light != NULL; light = light2 )
3667 /* get next light */
3668 light2 = light->next;
3670 /* filter into correct bucket */
3671 light->next = buckets[ light->style ];
3672 buckets[ light->style ] = light;
3674 /* if any styled light is present, automatically set nocollapse */
3675 if( light->style != LS_NORMAL )
3679 /* filter back into light list */
3681 for( i = 255; i >= 0; i-- )
3684 for( light = buckets[ i ]; light != NULL; light = light2 )
3686 light2 = light->next;
3687 light->next = lights;
3692 /* emit some statistics */
3693 Sys_Printf( "%9d total lights\n", numLights );
3694 Sys_Printf( "%9d culled lights\n", numCulledLights );
3700 CreateTraceLightsForBounds()
3701 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3704 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3708 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3709 float radius, dist, length;
3712 /* potential pre-setup */
3713 if( numLights == 0 )
3714 SetupEnvelopes( qfalse, fast );
3717 //% Sys_Printf( "CTWLFB: (%4.1f %4.1f %4.1f) (%4.1f %4.1f %4.1f)\n", mins[ 0 ], mins[ 1 ], mins[ 2 ], maxs[ 0 ], maxs[ 1 ], maxs[ 2 ] );
3719 /* allocate the light list */
3720 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3721 trace->numLights = 0;
3723 /* calculate spherical bounds */
3724 VectorAdd( mins, maxs, origin );
3725 VectorScale( origin, 0.5f, origin );
3726 VectorSubtract( maxs, origin, dir );
3727 radius = (float) VectorLength( dir );
3729 /* get length of normal vector */
3730 if( normal != NULL )
3731 length = VectorLength( normal );
3734 normal = nullVector;
3738 /* test each light and see if it reaches the sphere */
3739 /* note: the attenuation code MUST match LightingAtSample() */
3740 for( light = lights; light; light = light->next )
3742 /* check zero sized envelope */
3743 if( light->envelope <= 0 )
3745 lightsEnvelopeCulled++;
3750 if( !(light->flags & flags) )
3753 /* sunlight skips all this nonsense */
3754 if( light->type != EMIT_SUN )
3760 /* check against pvs cluster */
3761 if( numClusters > 0 && clusters != NULL )
3763 for( i = 0; i < numClusters; i++ )
3765 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3770 if( i == numClusters )
3772 lightsClusterCulled++;
3777 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3778 VectorSubtract( light->origin, origin, dir );
3779 dist = VectorLength( dir );
3780 dist -= light->envelope;
3784 lightsEnvelopeCulled++;
3788 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3791 for( i = 0; i < 3; i++ )
3793 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3798 lightsBoundsCulled++;
3804 /* planar surfaces (except twosided surfaces) have a couple more checks */
3805 if( length > 0.0f && trace->twoSided == qfalse )
3807 /* lights coplanar with a surface won't light it */
3808 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3810 lightsPlaneCulled++;
3814 /* check to see if light is behind the plane */
3815 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3817 lightsPlaneCulled++;
3822 /* add this light */
3823 trace->lights[ trace->numLights++ ] = light;
3826 /* make last night null */
3827 trace->lights[ trace->numLights ] = NULL;
3832 void FreeTraceLights( trace_t *trace )
3834 if( trace->lights != NULL )
3835 free( trace->lights );
3841 CreateTraceLightsForSurface()
3842 creates a list of lights that can potentially affect a drawsurface
3845 void CreateTraceLightsForSurface( int num, trace_t *trace )
3848 vec3_t mins, maxs, normal;
3850 bspDrawSurface_t *ds;
3851 surfaceInfo_t *info;
3858 /* get drawsurface and info */
3859 ds = &bspDrawSurfaces[ num ];
3860 info = &surfaceInfos[ num ];
3862 /* get the mins/maxs for the dsurf */
3863 ClearBounds( mins, maxs );
3864 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3865 for( i = 0; i < ds->numVerts; i++ )
3867 dv = &yDrawVerts[ ds->firstVert + i ];
3868 AddPointToBounds( dv->xyz, mins, maxs );
3869 if( !VectorCompare( dv->normal, normal ) )
3870 VectorClear( normal );
3873 /* create the lights for the bounding box */
3874 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
3877 /////////////////////////////////////////////////////////////
3879 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
3880 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
3881 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
3882 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
3884 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
3885 static int numFloodVectors = 0;
3887 void SetupFloodLight( void )
3890 float angle, elevation, angleStep, elevationStep;
3892 double v1,v2,v3,v4,v5;
3895 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
3897 /* calculate angular steps */
3898 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
3899 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
3903 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
3905 /* iterate elevation */
3906 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
3908 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
3909 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
3910 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
3915 /* emit some statistics */
3916 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
3919 value = ValueForKey( &entities[ 0 ], "_floodlight" );
3921 if( value[ 0 ] != '\0' )
3924 v4=floodlightDistance;
3925 v5=floodlightIntensity;
3927 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
3929 floodlightRGB[0]=v1;
3930 floodlightRGB[1]=v2;
3931 floodlightRGB[2]=v3;
3933 if (VectorLength(floodlightRGB)==0)
3935 VectorSet(floodlightRGB,240,240,255);
3941 floodlightDistance=v4;
3942 floodlightIntensity=v5;
3944 floodlighty = qtrue;
3945 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3949 VectorSet(floodlightRGB,240,240,255);
3950 //floodlighty = qtrue;
3951 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3953 VectorNormalize(floodlightRGB,floodlightRGB);
3956 //27 - lighttracer style ambient occlusion light hack.
3957 //Kudos to the dirtmapping author for most of this source.
3958 void FloodLightRawLightmap( int rawLightmapNum )
3960 int i, x, y, sx, sy, *cluster;
3961 float *origin, *normal, *floodlight, *floodlight2, average, samples;
3963 surfaceInfo_t *info;
3966 /* bail if this number exceeds the number of raw lightmaps */
3967 if( rawLightmapNum >= numRawLightmaps )
3971 lm = &rawLightmaps[ rawLightmapNum ];
3973 memset(&trace,0,sizeof(trace_t));
3975 trace.testOcclusion = qtrue;
3976 trace.forceSunlight = qfalse;
3977 trace.twoSided = qtrue;
3978 trace.recvShadows = lm->recvShadows;
3979 trace.numSurfaces = lm->numLightSurfaces;
3980 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
3981 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
3982 trace.testAll = qfalse;
3983 trace.distance = 1024;
3985 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
3986 //trace.twoSided = qfalse;
3987 for( i = 0; i < trace.numSurfaces; i++ )
3990 info = &surfaceInfos[ trace.surfaces[ i ] ];
3992 /* check twosidedness */
3993 if( info->si->twoSided )
3995 trace.twoSided = qtrue;
4001 for( y = 0; y < lm->sh; y++ )
4003 for( x = 0; x < lm->sw; x++ )
4006 cluster = SUPER_CLUSTER( x, y );
4007 origin = SUPER_ORIGIN( x, y );
4008 normal = SUPER_NORMAL( x, y );
4009 floodlight = SUPER_FLOODLIGHT( x, y );
4011 /* set default dirt */
4014 /* only look at mapped luxels */
4019 trace.cluster = *cluster;
4020 VectorCopy( origin, trace.origin );
4021 VectorCopy( normal, trace.normal );
4026 *floodlight = FloodLightForSample( &trace );
4030 /* testing no filtering */
4034 for( y = 0; y < lm->sh; y++ )
4036 for( x = 0; x < lm->sw; x++ )
4039 cluster = SUPER_CLUSTER( x, y );
4040 floodlight = SUPER_FLOODLIGHT( x, y );
4042 /* filter dirt by adjacency to unmapped luxels */
4043 average = *floodlight;
4045 for( sy = (y - 1); sy <= (y + 1); sy++ )
4047 if( sy < 0 || sy >= lm->sh )
4050 for( sx = (x - 1); sx <= (x + 1); sx++ )
4052 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4055 /* get neighboring luxel */
4056 cluster = SUPER_CLUSTER( sx, sy );
4057 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4058 if( *cluster < 0 || *floodlight2 <= 0.0f )
4062 average += *floodlight2;
4067 if( samples <= 0.0f )
4072 if( samples <= 0.0f )
4076 *floodlight = average / samples;
4082 FloodLightForSample()
4083 calculates floodlight value for a given sample
4084 once again, kudos to the dirtmapping coder
4086 float FloodLightForSample( trace_t *trace )
4092 float gatherLight, outLight;
4093 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4101 if( trace == NULL || trace->cluster < 0 )
4106 dd = floodlightDistance;
4107 VectorCopy( trace->normal, normal );
4109 /* check if the normal is aligned to the world-up */
4110 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f )
4112 if( normal[ 2 ] == 1.0f )
4114 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4115 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4117 else if( normal[ 2 ] == -1.0f )
4119 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4120 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4125 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4126 CrossProduct( normal, worldUp, myRt );
4127 VectorNormalize( myRt, myRt );
4128 CrossProduct( myRt, normal, myUp );
4129 VectorNormalize( myUp, myUp );
4132 /* iterate through ordered vectors */
4133 for( i = 0; i < numFloodVectors; i++ )
4135 if (floodlight_lowquality==qtrue)
4137 if (rand()%10 != 0 ) continue;
4142 /* transform vector into tangent space */
4143 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4144 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4145 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4148 VectorMA( trace->origin, dd, direction, trace->end );
4150 //VectorMA( trace->origin, 1, direction, trace->origin );
4152 SetupTrace( trace );
4157 if (trace->compileFlags & C_SKY )
4161 else if ( trace->opaque )
4163 VectorSubtract( trace->hit, trace->origin, displacement );
4164 d=VectorLength( displacement );
4166 // d=trace->distance;
4167 //if (d>256) gatherDirt+=1;
4169 if (contribution>1) contribution=1.0f;
4171 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4174 gatherLight+=contribution;
4178 if( gatherLight <= 0.0f )
4186 outLight=gatherLight;
4187 if( outLight > 1.0f )
4190 /* return to sender */