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 ColorToBytes( const float *color, byte *colorBytes, float scale )
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if( sample[ i ] < 0.0f )
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if (lightmapExposure == 1)
79 /* clamp with color normalization */
81 if( sample[ 1 ] > max )
83 if( sample[ 2 ] > max )
86 VectorScale( sample, (255.0f / max), sample );
90 if (lightmapExposure==0)
92 lightmapExposure=1.0f;
94 inv=1.f/lightmapExposure;
98 if( sample[ 1 ] > max )
100 if( sample[ 2 ] > max )
103 dif = (1- exp(-max * inv) ) * 255;
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, (1.0f / lightmapCompensate), sample );
125 colorBytes[ 0 ] = sample[ 0 ];
126 colorBytes[ 1 ] = sample[ 1 ];
127 colorBytes[ 2 ] = sample[ 2 ];
132 /* -------------------------------------------------------------------------------
134 this section deals with phong shading (normal interpolation across brush faces)
136 ------------------------------------------------------------------------------- */
140 smooths together coincident vertex normals across the bsp
143 #define MAX_SAMPLES 256
144 #define THETA_EPSILON 0.000001
145 #define EQUAL_NORMAL_EPSILON 0.01
147 void SmoothNormals( void )
149 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
150 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
151 bspDrawSurface_t *ds;
155 vec3_t average, diff;
156 int indexes[ MAX_SAMPLES ];
157 vec3_t votes[ MAX_SAMPLES ];
160 /* allocate shade angle table */
161 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
162 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
164 /* allocate smoothed table */
165 cs = (numBSPDrawVerts / 8) + 1;
166 smoothed = safe_malloc( cs );
167 memset( smoothed, 0, cs );
169 /* set default shade angle */
170 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
173 /* run through every surface and flag verts belonging to non-lightmapped surfaces
174 and set per-vertex smoothing angle */
175 for( i = 0; i < numBSPDrawSurfaces; i++ )
178 ds = &bspDrawSurfaces[ i ];
180 /* get shader for shade angle */
181 si = surfaceInfos[ i ].si;
182 if( si->shadeAngleDegrees )
183 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
185 shadeAngle = defaultShadeAngle;
186 if( shadeAngle > maxShadeAngle )
187 maxShadeAngle = shadeAngle;
190 for( j = 0; j < ds->numVerts; j++ )
192 f = ds->firstVert + j;
193 shadeAngles[ f ] = shadeAngle;
194 if( ds->surfaceType == MST_TRIANGLE_SOUP )
195 smoothed[ f >> 3 ] |= (1 << (f & 7));
198 /* ydnar: optional force-to-trisoup */
199 if( trisoup && ds->surfaceType == MST_PLANAR )
201 ds->surfaceType = MST_TRIANGLE_SOUP;
202 ds->lightmapNum[ 0 ] = -3;
206 /* bail if no surfaces have a shade angle */
207 if( maxShadeAngle == 0 )
216 start = I_FloatTime();
218 /* go through the list of vertexes */
219 for( i = 0; i < numBSPDrawVerts; i++ )
222 f = 10 * i / numBSPDrawVerts;
226 Sys_Printf( "%i...", f );
229 /* already smoothed? */
230 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
234 VectorClear( average );
238 /* build a table of coincident vertexes */
239 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
241 /* already smoothed? */
242 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
246 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
249 /* use smallest shade angle */
250 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
252 /* check shade angle */
253 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
256 else if( dot < -1.0 )
258 testAngle = acos( dot ) + THETA_EPSILON;
259 if( testAngle >= shadeAngle )
261 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
264 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
266 /* add to the list */
267 indexes[ numVerts++ ] = j;
270 smoothed[ j >> 3 ] |= (1 << (j & 7));
272 /* see if this normal has already been voted */
273 for( k = 0; k < numVotes; k++ )
275 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
276 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
277 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
278 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
282 /* add a new vote? */
283 if( k == numVotes && numVotes < MAX_SAMPLES )
285 VectorAdd( average, bspDrawVerts[ j ].normal, average );
286 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
291 /* don't average for less than 2 verts */
296 if( VectorNormalize( average, average ) > 0 )
299 for( j = 0; j < numVerts; j++ )
300 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
304 /* free the tables */
309 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
314 /* -------------------------------------------------------------------------------
316 this section deals with phong shaded lightmap tracing
318 ------------------------------------------------------------------------------- */
320 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
324 calculates the st tangent vectors for normalmapping
327 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
334 /* calculate barycentric basis for the triangle */
335 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 ]);
336 if( fabs( bb ) < 0.00000001f )
340 for( i = 0; i < numVerts; i++ )
342 /* calculate s tangent vector */
343 s = dv[ i ]->st[ 0 ] + 10.0f;
344 t = dv[ i ]->st[ 1 ];
345 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
346 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
347 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
349 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
350 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
351 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
353 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
354 VectorNormalize( stv[ i ], stv[ i ] );
356 /* calculate t tangent vector */
357 s = dv[ i ]->st[ 0 ];
358 t = dv[ i ]->st[ 1 ] + 10.0f;
359 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
360 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
361 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
363 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
367 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
368 VectorNormalize( ttv[ i ], ttv[ i ] );
371 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
372 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
375 /* return to caller */
384 perterbs the normal by the shader's normalmap in tangent space
387 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
394 VectorCopy( dv->normal, pNormal );
396 /* sample normalmap */
397 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
400 /* remap sampled normal from [0,255] to [-1,-1] */
401 for( i = 0; i < 3; i++ )
402 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
404 /* scale tangent vectors and add to original normal */
405 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
406 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
407 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
409 /* renormalize and return */
410 VectorNormalize( pNormal, pNormal );
417 maps a luxel for triangle bv at
421 #define BOGUS_NUDGE -99999.0f
423 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 ] )
425 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
426 float *luxel, *origin, *normal, d, lightmapSampleOffset;
433 vec4_t sideplane, hostplane;
438 static float nudges[][ 2 ] =
440 //%{ 0, 0 }, /* try center first */
441 { -NUDGE, 0 }, /* left */
442 { NUDGE, 0 }, /* right */
443 { 0, NUDGE }, /* up */
444 { 0, -NUDGE }, /* down */
445 { -NUDGE, NUDGE }, /* left/up */
446 { NUDGE, -NUDGE }, /* right/down */
447 { NUDGE, NUDGE }, /* right/up */
448 { -NUDGE, -NUDGE }, /* left/down */
449 { BOGUS_NUDGE, BOGUS_NUDGE }
453 /* find luxel xy coords (fixme: subtract 0.5?) */
454 x = dv->lightmap[ 0 ][ 0 ];
455 y = dv->lightmap[ 0 ][ 1 ];
458 else if( x >= lm->sw )
462 else if( y >= lm->sh )
465 /* set shader and cluster list */
469 numClusters = info->numSurfaceClusters;
470 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
479 /* get luxel, origin, cluster, and normal */
480 luxel = SUPER_LUXEL( 0, x, y );
481 origin = SUPER_ORIGIN( x, y );
482 normal = SUPER_NORMAL( x, y );
483 cluster = SUPER_CLUSTER( x, y );
485 /* don't attempt to remap occluded luxels for planar surfaces */
486 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
489 /* only average the normal for premapped luxels */
490 else if( (*cluster) >= 0 )
492 /* do bumpmap calculations */
494 PerturbNormal( dv, si, pNormal, stv, ttv );
496 VectorCopy( dv->normal, pNormal );
498 /* add the additional normal data */
499 VectorAdd( normal, pNormal, normal );
504 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
508 /* axial lightmap projection */
509 if( lm->vecs != NULL )
511 /* calculate an origin for the sample from the lightmap vectors */
512 VectorCopy( lm->origin, origin );
513 for( i = 0; i < 3; i++ )
515 /* add unless it's the axis, which is taken care of later */
516 if( i == lm->axisNum )
518 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
521 /* project the origin onto the plane */
522 d = DotProduct( origin, plane ) - plane[ 3 ];
523 d /= plane[ lm->axisNum ];
524 origin[ lm->axisNum ] -= d;
527 /* non axial lightmap projection (explicit xyz) */
529 VectorCopy( dv->xyz, origin );
531 //////////////////////
532 //27's test to make sure samples stay within the triangle boundaries
533 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
534 //2) if it does, nudge it onto the correct side.
536 if (worldverts!=NULL)
540 VectorCopy(worldverts[j],cverts[j]);
542 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
548 //build plane using 2 edges and a normal
551 VectorCopy(cverts[next],temp);
552 VectorAdd(temp,hostplane,temp);
553 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
555 //planetest sample point
556 e=DotProduct(origin,sideplane);
561 //VectorClear(origin);
562 //Move the sample point back inside triangle bounds
563 origin[0]-=sideplane[0]*(e+1);
564 origin[1]-=sideplane[1]*(e+1);
565 origin[2]-=sideplane[2]*(e+1);
574 ////////////////////////
576 /* planar surfaces have precalculated lightmap vectors for nudging */
577 if( lm->plane != NULL )
579 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
580 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
581 VectorCopy( lm->plane, vecs[ 2 ] );
584 /* non-planar surfaces must calculate them */
588 VectorCopy( plane, vecs[ 2 ] );
590 VectorCopy( dv->normal, vecs[ 2 ] );
591 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
594 /* push the origin off the surface a bit */
596 lightmapSampleOffset = si->lightmapSampleOffset;
598 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
599 if( lm->axisNum < 0 )
600 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
601 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
602 origin[ lm->axisNum ] -= lightmapSampleOffset;
604 origin[ lm->axisNum ] += lightmapSampleOffset;
606 VectorCopy(origin,origintwo);
607 origintwo[0]+=vecs[2][0];
608 origintwo[1]+=vecs[2][1];
609 origintwo[2]+=vecs[2][2];
612 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
614 /* another retarded hack, storing nudge count in luxel[ 1 ] */
617 /* point in solid? (except in dark mode) */
618 if( pointCluster < 0 && dark == qfalse )
620 /* nudge the the location around */
622 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
624 /* nudge the vector around a bit */
625 for( i = 0; i < 3; i++ )
627 /* set nudged point*/
628 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
632 /* get pvs cluster */
633 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
634 //if( pointCluster >= 0 )
635 // VectorCopy( nudged, origin );
640 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
641 if( pointCluster < 0 && si != NULL && dark == qfalse )
643 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
644 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
645 //if( pointCluster >= 0 )
646 // VectorCopy( nudged, origin );
651 if( pointCluster < 0 )
653 (*cluster) = CLUSTER_OCCLUDED;
654 VectorClear( origin );
655 VectorClear( normal );
661 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
663 /* do bumpmap calculations */
665 PerturbNormal( dv, si, pNormal, stv, ttv );
667 VectorCopy( dv->normal, pNormal );
669 /* store the cluster and normal */
670 (*cluster) = pointCluster;
671 VectorCopy( pNormal, normal );
673 /* store explicit mapping pass and implicit mapping pass */
688 recursively subdivides a triangle until its edges are shorter
689 than the distance between two luxels (thanks jc :)
692 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 ] )
694 bspDrawVert_t mid, *dv2[ 3 ];
698 /* map the vertexes */
700 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
701 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
702 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
708 float *a, *b, dx, dy, dist, maxDist;
711 /* find the longest edge and split it */
714 for( i = 0; i < 3; i++ )
717 a = dv[ i ]->lightmap[ 0 ];
718 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
721 dx = a[ 0 ] - b[ 0 ];
722 dy = a[ 1 ] - b[ 1 ];
723 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
733 /* try to early out */
734 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
738 /* split the longest edge and map it */
739 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
740 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
742 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
743 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
745 /* recurse to first triangle */
746 VectorCopy( dv, dv2 );
748 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
750 /* recurse to second triangle */
751 VectorCopy( dv, dv2 );
752 dv2[ (max + 1) % 3 ] = ∣
753 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
760 seed function for MapTriangle_r()
761 requires a cw ordered triangle
764 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
768 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
769 vec3_t worldverts[ 3 ];
772 /* get plane if possible */
773 if( lm->plane != NULL )
775 VectorCopy( lm->plane, plane );
776 plane[ 3 ] = lm->plane[ 3 ];
779 /* otherwise make one from the points */
780 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
783 /* check to see if we need to calculate texture->world tangent vectors */
784 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
795 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
796 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
797 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
799 /* map the vertexes */
800 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
801 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
802 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
804 /* 2002-11-20: prefer axial triangle edges */
807 /* subdivide the triangle */
808 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
812 for( i = 0; i < 3; i++ )
815 bspDrawVert_t *dv2[ 3 ];
819 a = dv[ i ]->lightmap[ 0 ];
820 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
822 /* make degenerate triangles for mapping edges */
823 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
826 dv2[ 1 ] = dv[ (i + 1) % 3 ];
827 dv2[ 2 ] = dv[ (i + 1) % 3 ];
829 /* map the degenerate triangle */
830 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
841 recursively subdivides a quad until its edges are shorter
842 than the distance between two luxels
845 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 ] )
847 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
854 float *a, *b, dx, dy, dist, maxDist;
857 /* find the longest edge and split it */
860 for( i = 0; i < 4; i++ )
863 a = dv[ i ]->lightmap[ 0 ];
864 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
867 dx = a[ 0 ] - b[ 0 ];
868 dy = a[ 1 ] - b[ 1 ];
869 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
879 /* try to early out */
880 if( max < 0 || maxDist <= subdivideThreshold )
884 /* we only care about even/odd edges */
887 /* split the longest edges */
888 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
889 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
891 /* map the vertexes */
892 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
893 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
898 /* recurse to first quad */
900 dv2[ 1 ] = &mid[ 0 ];
901 dv2[ 2 ] = &mid[ 1 ];
903 MapQuad_r( lm, info, dv2, plane, stv, ttv );
905 /* recurse to second quad */
906 dv2[ 0 ] = &mid[ 0 ];
909 dv2[ 3 ] = &mid[ 1 ];
910 MapQuad_r( lm, info, dv2, plane, stv, ttv );
916 /* recurse to first quad */
919 dv2[ 2 ] = &mid[ 0 ];
920 dv2[ 3 ] = &mid[ 1 ];
921 MapQuad_r( lm, info, dv2, plane, stv, ttv );
923 /* recurse to second quad */
924 dv2[ 0 ] = &mid[ 1 ];
925 dv2[ 1 ] = &mid[ 0 ];
928 MapQuad_r( lm, info, dv2, plane, stv, ttv );
936 seed function for MapQuad_r()
937 requires a cw ordered triangle quad
940 #define QUAD_PLANAR_EPSILON 0.5f
942 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
946 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
949 /* get plane if possible */
950 if( lm->plane != NULL )
952 VectorCopy( lm->plane, plane );
953 plane[ 3 ] = lm->plane[ 3 ];
956 /* otherwise make one from the points */
957 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
960 /* 4th point must fall on the plane */
961 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
962 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
965 /* check to see if we need to calculate texture->world tangent vectors */
966 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
977 /* map the vertexes */
978 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
979 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
980 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
981 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
983 /* subdivide the quad */
984 MapQuad_r( lm, info, dv, plane, stv, ttv );
992 maps the locations, normals, and pvs clusters for a raw lightmap
995 #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)
997 void MapRawLightmap( int rawLightmapNum )
999 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1000 float *luxel, *origin, *normal, samples, radius, pass;
1002 bspDrawSurface_t *ds;
1003 surfaceInfo_t *info;
1004 mesh_t src, *subdivided, *mesh;
1005 bspDrawVert_t *verts, *dv[ 4 ], fake;
1008 /* bail if this number exceeds the number of raw lightmaps */
1009 if( rawLightmapNum >= numRawLightmaps )
1013 lm = &rawLightmaps[ rawLightmapNum ];
1015 /* -----------------------------------------------------------------
1016 map referenced surfaces onto the raw lightmap
1017 ----------------------------------------------------------------- */
1019 /* walk the list of surfaces on this raw lightmap */
1020 for( n = 0; n < lm->numLightSurfaces; n++ )
1022 /* with > 1 surface per raw lightmap, clear occluded */
1025 for( y = 0; y < lm->sh; y++ )
1027 for( x = 0; x < lm->sw; x++ )
1030 cluster = SUPER_CLUSTER( x, y );
1032 *cluster = CLUSTER_UNMAPPED;
1038 num = lightSurfaces[ lm->firstLightSurface + n ];
1039 ds = &bspDrawSurfaces[ num ];
1040 info = &surfaceInfos[ num ];
1042 /* bail if no lightmap to calculate */
1043 if( info->lm != lm )
1049 /* map the surface onto the lightmap origin/cluster/normal buffers */
1050 switch( ds->surfaceType )
1054 verts = yDrawVerts + ds->firstVert;
1056 /* map the triangles */
1057 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1059 for( i = 0; i < ds->numIndexes; i += 3 )
1061 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1062 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1063 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1064 MapTriangle( lm, info, dv, mapNonAxial );
1070 /* make a mesh from the drawsurf */
1071 src.width = ds->patchWidth;
1072 src.height = ds->patchHeight;
1073 src.verts = &yDrawVerts[ ds->firstVert ];
1074 //% subdivided = SubdivideMesh( src, 8, 512 );
1075 subdivided = SubdivideMesh2( src, info->patchIterations );
1077 /* fit it to the curve and remove colinear verts on rows/columns */
1078 PutMeshOnCurve( *subdivided );
1079 mesh = RemoveLinearMeshColumnsRows( subdivided );
1080 FreeMesh( subdivided );
1083 verts = mesh->verts;
1089 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1090 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1091 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1092 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1096 /* map the mesh quads */
1099 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1101 for( y = 0; y < (mesh->height - 1); y++ )
1103 for( x = 0; x < (mesh->width - 1); x++ )
1106 pw[ 0 ] = x + (y * mesh->width);
1107 pw[ 1 ] = x + ((y + 1) * mesh->width);
1108 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1109 pw[ 3 ] = x + 1 + (y * mesh->width);
1110 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1115 /* get drawverts and map first triangle */
1116 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1117 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1118 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1119 MapTriangle( lm, info, dv, mapNonAxial );
1121 /* get drawverts and map second triangle */
1122 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1123 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1124 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1125 MapTriangle( lm, info, dv, mapNonAxial );
1132 for( y = 0; y < (mesh->height - 1); y++ )
1134 for( x = 0; x < (mesh->width - 1); x++ )
1137 pw[ 0 ] = x + (y * mesh->width);
1138 pw[ 1 ] = x + ((y + 1) * mesh->width);
1139 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1140 pw[ 3 ] = x + 1 + (y * mesh->width);
1146 /* attempt to map quad first */
1147 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1148 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1149 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1150 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1151 if( MapQuad( lm, info, dv ) )
1154 /* get drawverts and map first triangle */
1155 MapTriangle( lm, info, dv, mapNonAxial );
1157 /* get drawverts and map second triangle */
1158 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1159 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1160 MapTriangle( lm, info, dv, mapNonAxial );
1175 /* -----------------------------------------------------------------
1176 average and clean up luxel normals
1177 ----------------------------------------------------------------- */
1179 /* walk the luxels */
1180 for( y = 0; y < lm->sh; y++ )
1182 for( x = 0; x < lm->sw; x++ )
1185 luxel = SUPER_LUXEL( 0, x, y );
1186 normal = SUPER_NORMAL( x, y );
1187 cluster = SUPER_CLUSTER( x, y );
1189 /* only look at mapped luxels */
1193 /* the normal data could be the sum of multiple samples */
1194 if( luxel[ 3 ] > 1.0f )
1195 VectorNormalize( normal, normal );
1197 /* mark this luxel as having only one normal */
1202 /* non-planar surfaces stop here */
1203 if( lm->plane == NULL )
1206 /* -----------------------------------------------------------------
1207 map occluded or unuxed luxels
1208 ----------------------------------------------------------------- */
1210 /* walk the luxels */
1211 radius = floor( superSample / 2 );
1212 radius = radius > 0 ? radius : 1.0f;
1214 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1216 for( y = 0; y < lm->sh; y++ )
1218 for( x = 0; x < lm->sw; x++ )
1221 luxel = SUPER_LUXEL( 0, x, y );
1222 normal = SUPER_NORMAL( x, y );
1223 cluster = SUPER_CLUSTER( x, y );
1225 /* only look at unmapped luxels */
1226 if( *cluster != CLUSTER_UNMAPPED )
1229 /* divine a normal and origin from neighboring luxels */
1230 VectorClear( fake.xyz );
1231 VectorClear( fake.normal );
1232 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1233 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1235 for( sy = (y - 1); sy <= (y + 1); sy++ )
1237 if( sy < 0 || sy >= lm->sh )
1240 for( sx = (x - 1); sx <= (x + 1); sx++ )
1242 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1245 /* get neighboring luxel */
1246 luxel = SUPER_LUXEL( 0, sx, sy );
1247 origin = SUPER_ORIGIN( sx, sy );
1248 normal = SUPER_NORMAL( sx, sy );
1249 cluster = SUPER_CLUSTER( sx, sy );
1251 /* only consider luxels mapped in previous passes */
1252 if( *cluster < 0 || luxel[ 0 ] >= pass )
1255 /* add its distinctiveness to our own */
1256 VectorAdd( fake.xyz, origin, fake.xyz );
1257 VectorAdd( fake.normal, normal, fake.normal );
1258 samples += luxel[ 3 ];
1263 if( samples == 0.0f )
1267 VectorDivide( fake.xyz, samples, fake.xyz );
1268 //% VectorDivide( fake.normal, samples, fake.normal );
1269 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1272 /* map the fake vert */
1273 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1278 /* -----------------------------------------------------------------
1279 average and clean up luxel normals
1280 ----------------------------------------------------------------- */
1282 /* walk the luxels */
1283 for( y = 0; y < lm->sh; y++ )
1285 for( x = 0; x < lm->sw; x++ )
1288 luxel = SUPER_LUXEL( 0, x, y );
1289 normal = SUPER_NORMAL( x, y );
1290 cluster = SUPER_CLUSTER( x, y );
1292 /* only look at mapped luxels */
1296 /* the normal data could be the sum of multiple samples */
1297 if( luxel[ 3 ] > 1.0f )
1298 VectorNormalize( normal, normal );
1300 /* mark this luxel as having only one normal */
1308 for( y = 0; y < lm->sh; y++ )
1310 for( x = 0; x < lm->sw; x++ )
1315 cluster = SUPER_CLUSTER( x, y );
1316 origin = SUPER_ORIGIN( x, y );
1317 normal = SUPER_NORMAL( x, y );
1318 luxel = SUPER_LUXEL( x, y );
1323 /* check if within the bounding boxes of all surfaces referenced */
1324 ClearBounds( mins, maxs );
1325 for( n = 0; n < lm->numLightSurfaces; n++ )
1328 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1329 TOL = info->sampleSize + 2;
1330 AddPointToBounds( info->mins, mins, maxs );
1331 AddPointToBounds( info->maxs, mins, maxs );
1332 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1333 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1334 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1339 if( n < lm->numLightSurfaces )
1342 /* report bogus origin */
1343 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",
1344 rawLightmapNum, x, y, *cluster,
1345 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1346 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1347 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1358 sets up dirtmap (ambient occlusion)
1361 #define DIRT_CONE_ANGLE 88 /* degrees */
1362 #define DIRT_NUM_ANGLE_STEPS 16
1363 #define DIRT_NUM_ELEVATION_STEPS 3
1364 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1366 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1367 static int numDirtVectors = 0;
1369 void SetupDirt( void )
1372 float angle, elevation, angleStep, elevationStep;
1376 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1378 /* calculate angular steps */
1379 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1380 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1384 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1386 /* iterate elevation */
1387 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1389 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1390 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1391 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1396 /* emit some statistics */
1397 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1403 calculates dirt value for a given sample
1406 float DirtForSample( trace_t *trace )
1409 float gatherDirt, outDirt, angle, elevation, ooDepth;
1410 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1416 if( trace == NULL || trace->cluster < 0 )
1421 ooDepth = 1.0f / dirtDepth;
1422 VectorCopy( trace->normal, normal );
1424 /* check if the normal is aligned to the world-up */
1425 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f )
1427 if( normal[ 2 ] == 1.0f )
1429 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1430 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1432 else if( normal[ 2 ] == -1.0f )
1434 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1435 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1440 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1441 CrossProduct( normal, worldUp, myRt );
1442 VectorNormalize( myRt, myRt );
1443 CrossProduct( myRt, normal, myUp );
1444 VectorNormalize( myUp, myUp );
1447 /* 1 = random mode, 0 (well everything else) = non-random mode */
1451 for( i = 0; i < numDirtVectors; i++ )
1453 /* get random vector */
1454 angle = Random() * DEG2RAD( 360.0f );
1455 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1456 temp[ 0 ] = cos( angle ) * sin( elevation );
1457 temp[ 1 ] = sin( angle ) * sin( elevation );
1458 temp[ 2 ] = cos( elevation );
1460 /* transform into tangent space */
1461 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1462 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1463 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1466 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1467 SetupTrace( trace );
1473 VectorSubtract( trace->hit, trace->origin, displacement );
1474 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1480 /* iterate through ordered vectors */
1481 for( i = 0; i < numDirtVectors; i++ )
1483 /* transform vector into tangent space */
1484 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1485 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1486 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1489 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1490 SetupTrace( trace );
1496 VectorSubtract( trace->hit, trace->origin, displacement );
1497 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1503 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1504 SetupTrace( trace );
1510 VectorSubtract( trace->hit, trace->origin, displacement );
1511 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1515 if( gatherDirt <= 0.0f )
1518 /* apply gain (does this even do much? heh) */
1519 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1520 if( outDirt > 1.0f )
1524 outDirt *= dirtScale;
1525 if( outDirt > 1.0f )
1528 /* return to sender */
1529 return 1.0f - outDirt;
1536 calculates dirty fraction for each luxel
1539 void DirtyRawLightmap( int rawLightmapNum )
1541 int i, x, y, sx, sy, *cluster;
1542 float *origin, *normal, *dirt, *dirt2, average, samples;
1544 surfaceInfo_t *info;
1548 /* bail if this number exceeds the number of raw lightmaps */
1549 if( rawLightmapNum >= numRawLightmaps )
1553 lm = &rawLightmaps[ rawLightmapNum ];
1556 trace.testOcclusion = qtrue;
1557 trace.forceSunlight = qfalse;
1558 trace.recvShadows = lm->recvShadows;
1559 trace.numSurfaces = lm->numLightSurfaces;
1560 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1561 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1562 trace.testAll = qfalse;
1564 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1565 trace.twoSided = qfalse;
1566 for( i = 0; i < trace.numSurfaces; i++ )
1569 info = &surfaceInfos[ trace.surfaces[ i ] ];
1571 /* check twosidedness */
1572 if( info->si->twoSided )
1574 trace.twoSided = qtrue;
1580 for( y = 0; y < lm->sh; y++ )
1582 for( x = 0; x < lm->sw; x++ )
1585 cluster = SUPER_CLUSTER( x, y );
1586 origin = SUPER_ORIGIN( x, y );
1587 normal = SUPER_NORMAL( x, y );
1588 dirt = SUPER_DIRT( x, y );
1590 /* set default dirt */
1593 /* only look at mapped luxels */
1598 trace.cluster = *cluster;
1599 VectorCopy( origin, trace.origin );
1600 VectorCopy( normal, trace.normal );
1603 *dirt = DirtForSample( &trace );
1607 /* testing no filtering */
1611 for( y = 0; y < lm->sh; y++ )
1613 for( x = 0; x < lm->sw; x++ )
1616 cluster = SUPER_CLUSTER( x, y );
1617 dirt = SUPER_DIRT( x, y );
1619 /* filter dirt by adjacency to unmapped luxels */
1622 for( sy = (y - 1); sy <= (y + 1); sy++ )
1624 if( sy < 0 || sy >= lm->sh )
1627 for( sx = (x - 1); sx <= (x + 1); sx++ )
1629 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1632 /* get neighboring luxel */
1633 cluster = SUPER_CLUSTER( sx, sy );
1634 dirt2 = SUPER_DIRT( sx, sy );
1635 if( *cluster < 0 || *dirt2 <= 0.0f )
1644 if( samples <= 0.0f )
1649 if( samples <= 0.0f )
1653 *dirt = average / samples;
1662 calculates the pvs cluster, origin, normal of a sub-luxel
1665 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1667 int i, *cluster, *cluster2;
1668 float *origin, *origin2, *normal; //% , *normal2;
1669 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1672 /* calulate x vector */
1673 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1675 cluster = SUPER_CLUSTER( x, y );
1676 origin = SUPER_ORIGIN( x, y );
1677 //% normal = SUPER_NORMAL( x, y );
1678 cluster2 = SUPER_CLUSTER( x + 1, y );
1679 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1680 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1682 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1684 cluster = SUPER_CLUSTER( x - 1, y );
1685 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1686 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1687 cluster2 = SUPER_CLUSTER( x, y );
1688 origin2 = SUPER_ORIGIN( x, y );
1689 //% normal2 = SUPER_NORMAL( x, y );
1692 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1694 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1695 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1697 /* calulate y vector */
1698 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1700 cluster = SUPER_CLUSTER( x, y );
1701 origin = SUPER_ORIGIN( x, y );
1702 //% normal = SUPER_NORMAL( x, y );
1703 cluster2 = SUPER_CLUSTER( x, y + 1 );
1704 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1705 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1707 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1709 cluster = SUPER_CLUSTER( x, y - 1 );
1710 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1711 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1712 cluster2 = SUPER_CLUSTER( x, y );
1713 origin2 = SUPER_ORIGIN( x, y );
1714 //% normal2 = SUPER_NORMAL( x, y );
1717 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1719 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1720 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1722 /* calculate new origin */
1723 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1724 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1725 for( i = 0; i < 3; i++ )
1726 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1729 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1730 if( *sampleCluster < 0 )
1733 /* calculate new normal */
1734 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1735 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1736 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1738 normal = SUPER_NORMAL( x, y );
1739 VectorCopy( normal, sampleNormal );
1747 SubsampleRawLuxel_r()
1748 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1751 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )
1753 int b, samples, mapped, lighted;
1756 vec3_t origin[ 4 ], normal[ 4 ];
1757 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1758 vec3_t color, total;
1762 if( lightLuxel[ 3 ] >= lightSamples )
1766 VectorClear( total );
1770 /* make 2x2 subsample stamp */
1771 for( b = 0; b < 4; b++ )
1774 VectorCopy( sampleOrigin, origin[ b ] );
1776 /* calculate position */
1777 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1784 /* increment sample count */
1785 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1788 trace->cluster = *cluster;
1789 VectorCopy( origin[ b ], trace->origin );
1790 VectorCopy( normal[ b ], trace->normal );
1794 LightContributionToSample( trace );
1796 /* add to totals (fixme: make contrast function) */
1797 VectorCopy( trace->color, luxel[ b ] );
1798 VectorAdd( total, trace->color, total );
1799 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1803 /* subsample further? */
1804 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1805 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1806 lighted != 0 && lighted != mapped )
1808 for( b = 0; b < 4; b++ )
1810 if( cluster[ b ] < 0 )
1812 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.25f), luxel[ b ] );
1817 //% VectorClear( color );
1819 VectorCopy( lightLuxel, color );
1821 for( b = 0; b < 4; b++ )
1823 if( cluster[ b ] < 0 )
1825 VectorAdd( color, luxel[ b ], color );
1833 color[ 0 ] /= samples;
1834 color[ 1 ] /= samples;
1835 color[ 2 ] /= samples;
1838 VectorCopy( color, lightLuxel );
1839 lightLuxel[ 3 ] += 1.0f;
1846 IlluminateRawLightmap()
1847 illuminates the luxels
1850 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1851 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1853 void IlluminateRawLightmap( int rawLightmapNum )
1855 int i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
1856 int *cluster, *cluster2, mapped, lighted, totalLighted;
1858 surfaceInfo_t *info;
1859 qboolean filterColor, filterDir;
1861 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1862 float *lightLuxels, *lightLuxel, samples, filterRadius, weight;
1863 vec3_t color, averageColor, averageDir, total, temp, temp2;
1864 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1866 float stackLightLuxels[ STACK_LL_SIZE ];
1871 /* bail if this number exceeds the number of raw lightmaps */
1872 if( rawLightmapNum >= numRawLightmaps )
1876 lm = &rawLightmaps[ rawLightmapNum ];
1879 trace.testOcclusion = !noTrace;
1880 trace.forceSunlight = qfalse;
1881 trace.recvShadows = lm->recvShadows;
1882 trace.numSurfaces = lm->numLightSurfaces;
1883 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1884 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1886 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1887 trace.twoSided = qfalse;
1888 for( i = 0; i < trace.numSurfaces; i++ )
1891 info = &surfaceInfos[ trace.surfaces[ i ] ];
1893 /* check twosidedness */
1894 if( info->si->twoSided )
1896 trace.twoSided = qtrue;
1901 /* create a culled light list for this raw lightmap */
1902 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
1904 /* -----------------------------------------------------------------
1906 ----------------------------------------------------------------- */
1909 numLuxelsIlluminated += (lm->sw * lm->sh);
1911 /* test debugging state */
1912 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
1914 /* debug fill the luxels */
1915 for( y = 0; y < lm->sh; y++ )
1917 for( x = 0; x < lm->sw; x++ )
1920 cluster = SUPER_CLUSTER( x, y );
1922 /* only fill mapped luxels */
1926 /* get particulars */
1927 luxel = SUPER_LUXEL( 0, x, y );
1928 origin = SUPER_ORIGIN( x, y );
1929 normal = SUPER_NORMAL( x, y );
1931 /* color the luxel with raw lightmap num? */
1933 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
1935 /* color the luxel with lightmap axis? */
1936 else if( debugAxis )
1938 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
1939 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
1940 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
1943 /* color the luxel with luxel cluster? */
1944 else if( debugCluster )
1945 VectorCopy( debugColors[ *cluster % 12 ], luxel );
1947 /* color the luxel with luxel origin? */
1948 else if( debugOrigin )
1950 VectorSubtract( lm->maxs, lm->mins, temp );
1951 VectorScale( temp, (1.0f / 255.0f), temp );
1952 VectorSubtract( origin, lm->mins, temp2 );
1953 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
1954 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
1955 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
1958 /* color the luxel with the normal */
1959 else if( normalmap )
1961 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
1962 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
1963 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
1966 /* otherwise clear it */
1968 VectorClear( luxel );
1977 /* allocate temporary per-light luxel storage */
1978 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
1979 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
1980 lightLuxels = stackLightLuxels;
1982 lightLuxels = safe_malloc( llSize );
1985 //% memset( lm->superLuxels[ 0 ], 0, llSize );
1987 /* set ambient color */
1988 for( y = 0; y < lm->sh; y++ )
1990 for( x = 0; x < lm->sw; x++ )
1993 cluster = SUPER_CLUSTER( x, y );
1994 luxel = SUPER_LUXEL( 0, x, y );
1995 normal = SUPER_NORMAL( x, y );
1996 deluxel = SUPER_DELUXEL( x, y );
1998 /* blacken unmapped clusters */
2000 VectorClear( luxel );
2005 VectorCopy( ambientColor, luxel );
2007 VectorScale( normal, 0.00390625f, deluxel );
2013 /* clear styled lightmaps */
2014 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2015 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2017 if( lm->superLuxels[ lightmapNum ] != NULL )
2018 memset( lm->superLuxels[ lightmapNum ], 0, size );
2021 /* debugging code */
2022 //% if( trace.numLights <= 0 )
2023 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2025 /* walk light list */
2026 for( i = 0; i < trace.numLights; i++ )
2029 trace.light = trace.lights[ i ];
2032 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2034 if( lm->styles[ lightmapNum ] == trace.light->style ||
2035 lm->styles[ lightmapNum ] == LS_NONE )
2039 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2040 if( lightmapNum >= MAX_LIGHTMAPS )
2042 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2047 memset( lightLuxels, 0, llSize );
2050 /* initial pass, one sample per luxel */
2051 for( y = 0; y < lm->sh; y++ )
2053 for( x = 0; x < lm->sw; x++ )
2056 cluster = SUPER_CLUSTER( x, y );
2060 /* get particulars */
2061 lightLuxel = LIGHT_LUXEL( x, y );
2062 deluxel = SUPER_DELUXEL( x, y );
2063 origin = SUPER_ORIGIN( x, y );
2064 normal = SUPER_NORMAL( x, y );
2066 ////////// 27's temp hack for testing edge clipping ////
2067 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2069 lightLuxel[ 1 ] = 255;
2070 lightLuxel[ 3 ] = 1.0f;
2075 /* set contribution count */
2076 lightLuxel[ 3 ] = 1.0f;
2079 trace.cluster = *cluster;
2080 VectorCopy( origin, trace.origin );
2081 VectorCopy( normal, trace.normal );
2083 /* get light for this sample */
2084 LightContributionToSample( &trace );
2085 VectorCopy( trace.color, lightLuxel );
2088 if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2092 /* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */
2095 /* color to grayscale (photoshop rgb weighting) */
2096 brightness = trace.color[ 0 ] * 0.3f + trace.color[ 1 ] * 0.59f + trace.color[ 2 ] * 0.11f;
2097 brightness *= (1.0 / 255.0);
2098 VectorScale( trace.direction, brightness, trace.direction );
2099 VectorAdd( deluxel, trace.direction, deluxel );
2104 /* don't even bother with everything else if nothing was lit */
2105 if( totalLighted == 0 )
2108 /* determine filter radius */
2109 filterRadius = lm->filterRadius > trace.light->filterRadius
2111 : trace.light->filterRadius;
2112 if( filterRadius < 0.0f )
2113 filterRadius = 0.0f;
2115 /* set luxel filter radius */
2116 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2117 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2118 luxelFilterRadius = 1;
2120 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2121 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2122 if( lightSamples > 1 && luxelFilterRadius == 0 )
2125 for( y = 0; y < (lm->sh - 1); y++ )
2127 for( x = 0; x < (lm->sw - 1); x++ )
2132 VectorClear( total );
2134 /* test 2x2 stamp */
2135 for( t = 0; t < 4; t++ )
2137 /* set sample coords */
2138 sx = x + tests[ t ][ 0 ];
2139 sy = y + tests[ t ][ 1 ];
2142 cluster = SUPER_CLUSTER( sx, sy );
2148 lightLuxel = LIGHT_LUXEL( sx, sy );
2149 VectorAdd( total, lightLuxel, total );
2150 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2154 /* if total color is under a certain amount, then don't bother subsampling */
2155 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2158 /* if all 4 pixels are either in shadow or light, then don't subsample */
2159 if( lighted != 0 && lighted != mapped )
2161 for( t = 0; t < 4; t++ )
2163 /* set sample coords */
2164 sx = x + tests[ t ][ 0 ];
2165 sy = y + tests[ t ][ 1 ];
2168 cluster = SUPER_CLUSTER( sx, sy );
2171 lightLuxel = LIGHT_LUXEL( sx, sy );
2172 origin = SUPER_ORIGIN( sx, sy );
2174 /* only subsample shadowed luxels */
2175 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2179 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f, lightLuxel );
2181 /* debug code to colorize subsampled areas to yellow */
2182 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2183 //% VectorSet( luxel, 255, 204, 0 );
2190 /* tertiary pass, apply dirt map (ambient occlusion) */
2194 for( y = 0; y < lm->sh; y++ )
2196 for( x = 0; x < lm->sw; x++ )
2199 cluster = SUPER_CLUSTER( x, y );
2203 /* get particulars */
2204 lightLuxel = LIGHT_LUXEL( x, y );
2205 dirt = SUPER_DIRT( x, y );
2207 /* scale light value */
2208 VectorScale( lightLuxel, *dirt, lightLuxel );
2213 /* allocate sampling lightmap storage */
2214 if( lm->superLuxels[ lightmapNum ] == NULL )
2216 /* allocate sampling lightmap storage */
2217 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2218 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2219 memset( lm->superLuxels[ lightmapNum ], 0, size );
2223 if( lightmapNum > 0 )
2225 lm->styles[ lightmapNum ] = trace.light->style;
2226 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2229 /* copy to permanent luxels */
2230 for( y = 0; y < lm->sh; y++ )
2232 for( x = 0; x < lm->sw; x++ )
2234 /* get cluster and origin */
2235 cluster = SUPER_CLUSTER( x, y );
2238 origin = SUPER_ORIGIN( x, y );
2241 if( luxelFilterRadius )
2244 VectorClear( averageColor );
2247 /* cheaper distance-based filtering */
2248 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2250 if( sy < 0 || sy >= lm->sh )
2253 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2255 if( sx < 0 || sx >= lm->sw )
2258 /* get particulars */
2259 cluster = SUPER_CLUSTER( sx, sy );
2262 lightLuxel = LIGHT_LUXEL( sx, sy );
2265 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2266 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2268 /* scale luxel by filter weight */
2269 VectorScale( lightLuxel, weight, color );
2270 VectorAdd( averageColor, color, averageColor );
2276 if( samples <= 0.0f )
2279 /* scale into luxel */
2280 luxel = SUPER_LUXEL( lightmapNum, x, y );
2283 /* handle negative light */
2284 if( trace.light->flags & LIGHT_NEGATIVE )
2286 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2287 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2288 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2291 /* handle normal light */
2294 luxel[ 0 ] += averageColor[ 0 ] / samples;
2295 luxel[ 1 ] += averageColor[ 1 ] / samples;
2296 luxel[ 2 ] += averageColor[ 2 ] / samples;
2303 /* get particulars */
2304 lightLuxel = LIGHT_LUXEL( x, y );
2305 luxel = SUPER_LUXEL( lightmapNum, x, y );
2307 /* handle negative light */
2308 if( trace.light->flags & LIGHT_NEGATIVE )
2309 VectorScale( averageColor, -1.0f, averageColor );
2314 /* handle negative light */
2315 if( trace.light->flags & LIGHT_NEGATIVE )
2316 VectorSubtract( luxel, lightLuxel, luxel );
2318 /* handle normal light */
2320 VectorAdd( luxel, lightLuxel, luxel );
2326 /* free temporary luxels */
2327 if( lightLuxels != stackLightLuxels )
2328 free( lightLuxels );
2331 /* free light list */
2332 FreeTraceLights( &trace );
2334 /* -----------------------------------------------------------------
2336 ----------------------------------------------------------------- */
2340 /* walk lightmaps */
2341 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2344 if( lm->superLuxels[ lightmapNum ] == NULL )
2347 /* apply floodlight to each luxel */
2348 for( y = 0; y < lm->sh; y++ )
2350 for( x = 0; x < lm->sw; x++ )
2353 cluster = SUPER_CLUSTER( x, y );
2354 //% if( *cluster < 0 )
2357 /* get particulars */
2358 luxel = SUPER_LUXEL( lightmapNum, x, y );
2359 floodlight = SUPER_FLOODLIGHT( x, y );
2361 flood[0]=floodlightRGB[0]*floodlightIntensity;
2362 flood[1]=floodlightRGB[1]*floodlightIntensity;
2363 flood[2]=floodlightRGB[2]*floodlightIntensity;
2365 /* scale light value */
2366 VectorScale( flood, *floodlight, flood );
2371 if (luxel[3]==0) luxel[3]=1;
2379 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2382 if( lm->superLuxels[ lightmapNum ] == NULL )
2385 for( y = 0; y < lm->sh; y++ )
2387 for( x = 0; x < lm->sw; x++ )
2390 cluster = SUPER_CLUSTER( x, y );
2391 //% if( *cluster < 0 )
2394 /* get particulars */
2395 luxel = SUPER_LUXEL( lightmapNum, x, y );
2396 normal = SUPER_NORMAL ( x, y );
2398 luxel[0]=(normal[0]*127)+127;
2399 luxel[1]=(normal[1]*127)+127;
2400 luxel[2]=(normal[2]*127)+127;
2406 /* -----------------------------------------------------------------
2408 ----------------------------------------------------------------- */
2412 /* walk lightmaps */
2413 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2416 if( lm->superLuxels[ lightmapNum ] == NULL )
2419 /* apply dirt to each luxel */
2420 for( y = 0; y < lm->sh; y++ )
2422 for( x = 0; x < lm->sw; x++ )
2425 cluster = SUPER_CLUSTER( x, y );
2426 //% if( *cluster < 0 )
2429 /* get particulars */
2430 luxel = SUPER_LUXEL( lightmapNum, x, y );
2431 dirt = SUPER_DIRT( x, y );
2434 VectorScale( luxel, *dirt, luxel );
2438 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2444 /* -----------------------------------------------------------------
2446 ----------------------------------------------------------------- */
2448 /* walk lightmaps */
2449 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2452 if( lm->superLuxels[ lightmapNum ] == NULL )
2455 /* average occluded luxels from neighbors */
2456 for( y = 0; y < lm->sh; y++ )
2458 for( x = 0; x < lm->sw; x++ )
2460 /* get particulars */
2461 cluster = SUPER_CLUSTER( x, y );
2462 luxel = SUPER_LUXEL( lightmapNum, x, y );
2463 deluxel = SUPER_DELUXEL( x, y );
2464 normal = SUPER_NORMAL( x, y );
2466 /* determine if filtering is necessary */
2467 filterColor = qfalse;
2470 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2471 filterColor = qtrue;
2472 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2475 if( !filterColor && !filterDir )
2478 /* choose seed amount */
2479 VectorClear( averageColor );
2480 VectorClear( averageDir );
2483 /* walk 3x3 matrix */
2484 for( sy = (y - 1); sy <= (y + 1); sy++ )
2486 if( sy < 0 || sy >= lm->sh )
2489 for( sx = (x - 1); sx <= (x + 1); sx++ )
2491 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2494 /* get neighbor's particulars */
2495 cluster2 = SUPER_CLUSTER( sx, sy );
2496 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2497 deluxel2 = SUPER_DELUXEL( sx, sy );
2499 /* ignore unmapped/unlit luxels */
2500 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2501 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2504 /* add its distinctiveness to our own */
2505 VectorAdd( averageColor, luxel2, averageColor );
2506 samples += luxel2[ 3 ];
2508 VectorAdd( averageDir, deluxel2, averageDir );
2513 if( samples <= 0.0f )
2516 /* dark lightmap seams */
2519 if( lightmapNum == 0 )
2520 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2527 VectorDivide( averageColor, samples, luxel );
2531 VectorDivide( averageDir, samples, deluxel );
2533 /* set cluster to -3 */
2535 *cluster = CLUSTER_FLOODED;
2544 IlluminateVertexes()
2545 light the surface vertexes
2548 #define VERTEX_NUDGE 4.0f
2550 void IlluminateVertexes( int num )
2552 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2553 int lightmapNum, numAvg;
2554 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2555 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2556 bspDrawSurface_t *ds;
2557 surfaceInfo_t *info;
2559 bspDrawVert_t *verts;
2563 /* get surface, info, and raw lightmap */
2564 ds = &bspDrawSurfaces[ num ];
2565 info = &surfaceInfos[ num ];
2568 /* -----------------------------------------------------------------
2569 illuminate the vertexes
2570 ----------------------------------------------------------------- */
2572 /* calculate vertex lighting for surfaces without lightmaps */
2573 if( lm == NULL || cpmaHack )
2576 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2577 trace.forceSunlight = info->si->forceSunlight;
2578 trace.recvShadows = info->recvShadows;
2579 trace.numSurfaces = 1;
2580 trace.surfaces = #
2581 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2583 /* twosided lighting */
2584 trace.twoSided = info->si->twoSided;
2586 /* make light list for this surface */
2587 CreateTraceLightsForSurface( num, &trace );
2590 verts = yDrawVerts + ds->firstVert;
2592 memset( avgColors, 0, sizeof( avgColors ) );
2594 /* walk the surface verts */
2595 for( i = 0; i < ds->numVerts; i++ )
2597 /* get vertex luxel */
2598 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2600 /* color the luxel with raw lightmap num? */
2602 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2604 /* color the luxel with luxel origin? */
2605 else if( debugOrigin )
2607 VectorSubtract( info->maxs, info->mins, temp );
2608 VectorScale( temp, (1.0f / 255.0f), temp );
2609 VectorSubtract( origin, lm->mins, temp2 );
2610 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2611 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2612 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2615 /* color the luxel with the normal */
2616 else if( normalmap )
2618 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2619 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2620 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2623 /* illuminate the vertex */
2626 /* clear vertex luxel */
2627 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2629 /* try at initial origin */
2630 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2631 if( trace.cluster >= 0 )
2634 VectorCopy( verts[ i ].xyz, trace.origin );
2635 VectorCopy( verts[ i ].normal, trace.normal );
2639 dirt = DirtForSample( &trace );
2644 LightingAtSample( &trace, ds->vertexStyles, colors );
2647 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2650 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2653 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2654 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2655 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2659 /* is this sample bright enough? */
2660 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2661 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2662 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2663 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2665 /* nudge the sample point around a bit */
2666 for( x = 0; x < 4; x++ )
2668 /* two's complement 0, 1, -1, 2, -2, etc */
2669 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2671 for( y = 0; y < 4; y++ )
2673 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2675 for( z = 0; z < 4; z++ )
2677 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2680 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2681 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2682 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2684 /* try at nudged origin */
2685 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2686 if( trace.cluster < 0 )
2690 LightingAtSample( &trace, ds->vertexStyles, colors );
2693 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2696 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2699 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2700 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2703 /* bright enough? */
2704 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2705 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2706 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2707 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2714 /* add to average? */
2715 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2716 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2717 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2718 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2721 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2723 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2724 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2729 /* another happy customer */
2730 numVertsIlluminated++;
2733 /* set average color */
2736 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2737 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2741 VectorCopy( ambientColor, avgColors[ 0 ] );
2744 /* clean up and store vertex color */
2745 for( i = 0; i < ds->numVerts; i++ )
2747 /* get vertex luxel */
2748 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2750 /* store average in occluded vertexes */
2751 if( radVertLuxel[ 0 ] < 0.0f )
2753 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2755 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2756 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2759 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2764 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2767 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2768 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2771 if( bouncing || bounce == 0 || !bounceOnly )
2772 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2773 if( !info->si->noVertexLight )
2774 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2778 /* free light list */
2779 FreeTraceLights( &trace );
2781 /* return to sender */
2785 /* -----------------------------------------------------------------
2786 reconstitute vertex lighting from the luxels
2787 ----------------------------------------------------------------- */
2789 /* set styles from lightmap */
2790 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2791 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2793 /* get max search radius */
2795 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
2797 /* walk the surface verts */
2798 verts = yDrawVerts + ds->firstVert;
2799 for( i = 0; i < ds->numVerts; i++ )
2801 /* do each lightmap */
2802 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2805 if( lm->superLuxels[ lightmapNum ] == NULL )
2808 /* get luxel coords */
2809 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
2810 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
2813 else if( x >= lm->sw )
2817 else if( y >= lm->sh )
2820 /* get vertex luxels */
2821 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2822 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2824 /* color the luxel with the normal? */
2827 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2828 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2829 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2832 /* color the luxel with surface num? */
2833 else if( debugSurfaces )
2834 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2836 /* divine color from the superluxels */
2839 /* increasing radius */
2840 VectorClear( radVertLuxel );
2842 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
2844 /* sample within radius */
2845 for( sy = (y - radius); sy <= (y + radius); sy++ )
2847 if( sy < 0 || sy >= lm->sh )
2850 for( sx = (x - radius); sx <= (x + radius); sx++ )
2852 if( sx < 0 || sx >= lm->sw )
2855 /* get luxel particulars */
2856 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2857 cluster = SUPER_CLUSTER( sx, sy );
2861 /* testing: must be brigher than ambient color */
2862 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
2865 /* add its distinctiveness to our own */
2866 VectorAdd( radVertLuxel, luxel, radVertLuxel );
2867 samples += luxel[ 3 ];
2873 if( samples > 0.0f )
2874 VectorDivide( radVertLuxel, samples, radVertLuxel );
2876 VectorCopy( ambientColor, radVertLuxel );
2879 /* store into floating point storage */
2880 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2881 numVertsIlluminated++;
2883 /* store into bytes (for vertex approximation) */
2884 if( !info->si->noVertexLight )
2885 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
2892 /* -------------------------------------------------------------------------------
2894 light optimization (-fast)
2896 creates a list of lights that will affect a surface and stores it in tw
2897 this is to optimize surface lighting by culling out as many of the
2898 lights in the world as possible from further calculation
2900 ------------------------------------------------------------------------------- */
2904 determines opaque brushes in the world and find sky shaders for sunlight calculations
2907 void SetupBrushes( void )
2909 int i, j, b, compileFlags;
2912 bspBrushSide_t *side;
2913 bspShader_t *shader;
2918 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
2921 if( opaqueBrushes == NULL )
2922 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
2925 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
2926 numOpaqueBrushes = 0;
2928 /* walk the list of worldspawn brushes */
2929 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
2932 b = bspModels[ 0 ].firstBSPBrush + i;
2933 brush = &bspBrushes[ b ];
2935 /* check all sides */
2938 for( j = 0; j < brush->numSides && inside; j++ )
2940 /* do bsp shader calculations */
2941 side = &bspBrushSides[ brush->firstSide + j ];
2942 shader = &bspShaders[ side->shaderNum ];
2944 /* get shader info */
2945 si = ShaderInfoForShader( shader->shader );
2949 /* or together compile flags */
2950 compileFlags |= si->compileFlags;
2953 /* determine if this brush is opaque to light */
2954 if( !(compileFlags & C_TRANSLUCENT) )
2956 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
2962 /* emit some statistics */
2963 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
2970 determines if two clusters are visible to each other using the PVS
2973 qboolean ClusterVisible( int a, int b )
2975 int portalClusters, leafBytes;
2980 if( a < 0 || b < 0 )
2988 if( numBSPVisBytes <=8 )
2992 portalClusters = ((int *) bspVisBytes)[ 0 ];
2993 leafBytes = ((int*) bspVisBytes)[ 1 ];
2994 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
2997 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3006 borrowed from vlight.c
3009 int PointInLeafNum_r( vec3_t point, int nodenum )
3017 while( nodenum >= 0 )
3019 node = &bspNodes[ nodenum ];
3020 plane = &bspPlanes[ node->planeNum ];
3021 dist = DotProduct( point, plane->normal ) - plane->dist;
3023 nodenum = node->children[ 0 ];
3024 else if( dist < -0.1 )
3025 nodenum = node->children[ 1 ];
3028 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3029 if( bspLeafs[ leafnum ].cluster != -1 )
3031 nodenum = node->children[ 1 ];
3035 leafnum = -nodenum - 1;
3043 borrowed from vlight.c
3046 int PointInLeafNum( vec3_t point )
3048 return PointInLeafNum_r( point, 0 );
3054 ClusterVisibleToPoint() - ydnar
3055 returns qtrue if point can "see" cluster
3058 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3063 /* get leafNum for point */
3064 pointCluster = ClusterForPoint( point );
3065 if( pointCluster < 0 )
3069 return ClusterVisible( pointCluster, cluster );
3075 ClusterForPoint() - ydnar
3076 returns the pvs cluster for point
3079 int ClusterForPoint( vec3_t point )
3084 /* get leafNum for point */
3085 leafNum = PointInLeafNum( point );
3089 /* return the cluster */
3090 return bspLeafs[ leafNum ].cluster;
3096 ClusterForPointExt() - ydnar
3097 also takes brushes into account for occlusion testing
3100 int ClusterForPointExt( vec3_t point, float epsilon )
3102 int i, j, b, leafNum, cluster;
3105 int *brushes, numBSPBrushes;
3111 /* get leaf for point */
3112 leafNum = PointInLeafNum( point );
3115 leaf = &bspLeafs[ leafNum ];
3117 /* get the cluster */
3118 cluster = leaf->cluster;
3122 /* transparent leaf, so check point against all brushes in the leaf */
3123 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3124 numBSPBrushes = leaf->numBSPLeafBrushes;
3125 for( i = 0; i < numBSPBrushes; i++ )
3129 if( b > maxOpaqueBrush )
3131 brush = &bspBrushes[ b ];
3132 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3135 /* check point against all planes */
3137 for( j = 0; j < brush->numSides && inside; j++ )
3139 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3140 dot = DotProduct( point, plane->normal );
3146 /* if inside, return bogus cluster */
3151 /* if the point made it this far, it's not inside any opaque brushes */
3158 ClusterForPointExtFilter() - ydnar
3159 adds cluster checking against a list of known valid clusters
3162 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3167 /* get cluster for point */
3168 cluster = ClusterForPointExt( point, epsilon );
3170 /* check if filtering is necessary */
3171 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3175 for( i = 0; i < numClusters; i++ )
3177 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3188 ShaderForPointInLeaf() - ydnar
3189 checks a point against all brushes in a leaf, returning the shader of the brush
3190 also sets the cumulative surface and content flags for the brush hit
3193 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3198 int *brushes, numBSPBrushes;
3201 bspBrushSide_t *side;
3203 bspShader_t *shader;
3204 int allSurfaceFlags, allContentFlags;
3207 /* clear things out first */
3214 leaf = &bspLeafs[ leafNum ];
3216 /* transparent leaf, so check point against all brushes in the leaf */
3217 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3218 numBSPBrushes = leaf->numBSPLeafBrushes;
3219 for( i = 0; i < numBSPBrushes; i++ )
3222 brush = &bspBrushes[ brushes[ i ] ];
3224 /* check point against all planes */
3226 allSurfaceFlags = 0;
3227 allContentFlags = 0;
3228 for( j = 0; j < brush->numSides && inside; j++ )
3230 side = &bspBrushSides[ brush->firstSide + j ];
3231 plane = &bspPlanes[ side->planeNum ];
3232 dot = DotProduct( point, plane->normal );
3238 shader = &bspShaders[ side->shaderNum ];
3239 allSurfaceFlags |= shader->surfaceFlags;
3240 allContentFlags |= shader->contentFlags;
3244 /* handle if inside */
3247 /* if there are desired flags, check for same and continue if they aren't matched */
3248 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3250 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3253 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3254 *surfaceFlags = allSurfaceFlags;
3255 *contentFlags = allContentFlags;
3256 return brush->shaderNum;
3260 /* if the point made it this far, it's not inside any brushes */
3268 chops a bounding box by the plane defined by origin and normal
3269 returns qfalse if the bounds is entirely clipped away
3271 this is not exactly the fastest way to do this...
3274 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3276 /* FIXME: rewrite this so it doesn't use bloody brushes */
3284 calculates each light's effective envelope,
3285 taking into account brightness, type, and pvs.
3288 #define LIGHT_EPSILON 0.125f
3289 #define LIGHT_NUDGE 2.0f
3291 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3293 int i, x, y, z, x1, y1, z1;
3294 light_t *light, *light2, **owner;
3296 vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
3297 float radius, intensity;
3298 light_t *buckets[ 256 ];
3301 /* early out for weird cases where there are no lights */
3302 if( lights == NULL )
3306 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3310 numCulledLights = 0;
3312 while( *owner != NULL )
3317 /* handle negative lights */
3318 if( light->photons < 0.0f || light->add < 0.0f )
3320 light->photons *= -1.0f;
3321 light->add *= -1.0f;
3322 light->flags |= LIGHT_NEGATIVE;
3326 if( light->type == EMIT_SUN )
3330 light->envelope = MAX_WORLD_COORD * 8.0f;
3331 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3332 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3335 /* everything else */
3338 /* get pvs cluster for light */
3339 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3341 /* invalid cluster? */
3342 if( light->cluster < 0 )
3344 /* nudge the sample point around a bit */
3345 for( x = 0; x < 4; x++ )
3347 /* two's complement 0, 1, -1, 2, -2, etc */
3348 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3350 for( y = 0; y < 4; y++ )
3352 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3354 for( z = 0; z < 4; z++ )
3356 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3359 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3360 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3361 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3363 /* try at nudged origin */
3364 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3365 if( light->cluster < 0 )
3369 VectorCopy( origin, light->origin );
3375 /* only calculate for lights in pvs and outside of opaque brushes */
3376 if( light->cluster >= 0 )
3378 /* set light fast flag */
3380 light->flags |= LIGHT_FAST_TEMP;
3382 light->flags &= ~LIGHT_FAST_TEMP;
3383 if( light->si && light->si->noFast )
3384 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3386 /* clear light envelope */
3387 light->envelope = 0;
3389 /* handle area lights */
3390 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3392 /* ugly hack to calculate extent for area lights, but only done once */
3393 VectorScale( light->normal, -1.0f, dir );
3394 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3398 VectorMA( light->origin, radius, light->normal, origin );
3399 factor = PointToPolygonFormFactor( origin, dir, light->w );
3402 if( (factor * light->add) <= light->falloffTolerance )
3403 light->envelope = radius;
3406 /* check for fast mode */
3407 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3408 light->envelope = MAX_WORLD_COORD * 8.0f;
3413 intensity = light->photons;
3417 if( light->envelope <= 0.0f )
3419 /* solve distance for non-distance lights */
3420 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3421 light->envelope = MAX_WORLD_COORD * 8.0f;
3423 /* solve distance for linear lights */
3424 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3425 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3426 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3429 add = angle * light->photons * linearScale - (dist * light->fade);
3430 T = (light->photons * linearScale) - (dist * light->fade);
3431 T + (dist * light->fade) = (light->photons * linearScale);
3432 dist * light->fade = (light->photons * linearScale) - T;
3433 dist = ((light->photons * linearScale) - T) / light->fade;
3436 /* solve for inverse square falloff */
3438 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3441 add = light->photons / (dist * dist);
3442 T = light->photons / (dist * dist);
3443 T * (dist * dist) = light->photons;
3444 dist = sqrt( light->photons / T );
3448 /* chop radius against pvs */
3451 ClearBounds( mins, maxs );
3453 /* check all leaves */
3454 for( i = 0; i < numBSPLeafs; i++ )
3457 leaf = &bspLeafs[ i ];
3460 if( leaf->cluster < 0 )
3462 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3465 /* add this leafs bbox to the bounds */
3466 VectorCopy( leaf->mins, origin );
3467 AddPointToBounds( origin, mins, maxs );
3468 VectorCopy( leaf->maxs, origin );
3469 AddPointToBounds( origin, mins, maxs );
3472 /* test to see if bounds encompass light */
3473 for( i = 0; i < 3; i++ )
3475 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3477 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3478 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3479 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3480 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3481 AddPointToBounds( light->origin, mins, maxs );
3485 /* chop the bounds by a plane for area lights and spotlights */
3486 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3487 ChopBounds( mins, maxs, light->origin, light->normal );
3490 VectorCopy( mins, light->mins );
3491 VectorCopy( maxs, light->maxs );
3493 /* reflect bounds around light origin */
3494 //% VectorMA( light->origin, -1.0f, origin, origin );
3495 VectorScale( light->origin, 2, origin );
3496 VectorSubtract( origin, maxs, origin );
3497 AddPointToBounds( origin, mins, maxs );
3498 //% VectorMA( light->origin, -1.0f, mins, origin );
3499 VectorScale( light->origin, 2, origin );
3500 VectorSubtract( origin, mins, origin );
3501 AddPointToBounds( origin, mins, maxs );
3503 /* calculate spherical bounds */
3504 VectorSubtract( maxs, light->origin, dir );
3505 radius = (float) VectorLength( dir );
3507 /* if this radius is smaller than the envelope, then set the envelope to it */
3508 if( radius < light->envelope )
3510 light->envelope = radius;
3511 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3514 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3517 /* add grid/surface only check */
3520 if( !(light->flags & LIGHT_GRID) )
3521 light->envelope = 0.0f;
3525 if( !(light->flags & LIGHT_SURFACES) )
3526 light->envelope = 0.0f;
3531 if( light->cluster < 0 || light->envelope <= 0.0f )
3534 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3536 /* delete the light */
3538 *owner = light->next;
3539 if( light->w != NULL )
3546 /* square envelope */
3547 light->envelope2 = (light->envelope * light->envelope);
3549 /* increment light count */
3552 /* set next light */
3553 owner = &((**owner).next);
3556 /* bucket sort lights by style */
3557 memset( buckets, 0, sizeof( buckets ) );
3559 for( light = lights; light != NULL; light = light2 )
3561 /* get next light */
3562 light2 = light->next;
3564 /* filter into correct bucket */
3565 light->next = buckets[ light->style ];
3566 buckets[ light->style ] = light;
3568 /* if any styled light is present, automatically set nocollapse */
3569 if( light->style != LS_NORMAL )
3573 /* filter back into light list */
3575 for( i = 255; i >= 0; i-- )
3578 for( light = buckets[ i ]; light != NULL; light = light2 )
3580 light2 = light->next;
3581 light->next = lights;
3586 /* emit some statistics */
3587 Sys_Printf( "%9d total lights\n", numLights );
3588 Sys_Printf( "%9d culled lights\n", numCulledLights );
3594 CreateTraceLightsForBounds()
3595 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3598 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3602 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3603 float radius, dist, length;
3606 /* potential pre-setup */
3607 if( numLights == 0 )
3608 SetupEnvelopes( qfalse, fast );
3611 //% 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 ] );
3613 /* allocate the light list */
3614 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3615 trace->numLights = 0;
3617 /* calculate spherical bounds */
3618 VectorAdd( mins, maxs, origin );
3619 VectorScale( origin, 0.5f, origin );
3620 VectorSubtract( maxs, origin, dir );
3621 radius = (float) VectorLength( dir );
3623 /* get length of normal vector */
3624 if( normal != NULL )
3625 length = VectorLength( normal );
3628 normal = nullVector;
3632 /* test each light and see if it reaches the sphere */
3633 /* note: the attenuation code MUST match LightingAtSample() */
3634 for( light = lights; light; light = light->next )
3636 /* check zero sized envelope */
3637 if( light->envelope <= 0 )
3639 lightsEnvelopeCulled++;
3644 if( !(light->flags & flags) )
3647 /* sunlight skips all this nonsense */
3648 if( light->type != EMIT_SUN )
3654 /* check against pvs cluster */
3655 if( numClusters > 0 && clusters != NULL )
3657 for( i = 0; i < numClusters; i++ )
3659 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3664 if( i == numClusters )
3666 lightsClusterCulled++;
3671 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3672 VectorSubtract( light->origin, origin, dir );
3673 dist = VectorLength( dir );
3674 dist -= light->envelope;
3678 lightsEnvelopeCulled++;
3682 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3685 for( i = 0; i < 3; i++ )
3687 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3692 lightsBoundsCulled++;
3698 /* planar surfaces (except twosided surfaces) have a couple more checks */
3699 if( length > 0.0f && trace->twoSided == qfalse )
3701 /* lights coplanar with a surface won't light it */
3702 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3704 lightsPlaneCulled++;
3708 /* check to see if light is behind the plane */
3709 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3711 lightsPlaneCulled++;
3716 /* add this light */
3717 trace->lights[ trace->numLights++ ] = light;
3720 /* make last night null */
3721 trace->lights[ trace->numLights ] = NULL;
3726 void FreeTraceLights( trace_t *trace )
3728 if( trace->lights != NULL )
3729 free( trace->lights );
3735 CreateTraceLightsForSurface()
3736 creates a list of lights that can potentially affect a drawsurface
3739 void CreateTraceLightsForSurface( int num, trace_t *trace )
3742 vec3_t mins, maxs, normal;
3744 bspDrawSurface_t *ds;
3745 surfaceInfo_t *info;
3752 /* get drawsurface and info */
3753 ds = &bspDrawSurfaces[ num ];
3754 info = &surfaceInfos[ num ];
3756 /* get the mins/maxs for the dsurf */
3757 ClearBounds( mins, maxs );
3758 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3759 for( i = 0; i < ds->numVerts; i++ )
3761 dv = &yDrawVerts[ ds->firstVert + i ];
3762 AddPointToBounds( dv->xyz, mins, maxs );
3763 if( !VectorCompare( dv->normal, normal ) )
3764 VectorClear( normal );
3767 /* create the lights for the bounding box */
3768 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
3771 /////////////////////////////////////////////////////////////
3773 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
3774 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
3775 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
3776 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
3778 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
3779 static int numFloodVectors = 0;
3781 void SetupFloodLight( void )
3784 float angle, elevation, angleStep, elevationStep;
3786 double v1,v2,v3,v4,v5;
3789 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
3791 /* calculate angular steps */
3792 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
3793 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
3797 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
3799 /* iterate elevation */
3800 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
3802 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
3803 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
3804 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
3809 /* emit some statistics */
3810 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
3813 value = ValueForKey( &entities[ 0 ], "_floodlight" );
3815 if( value[ 0 ] != '\0' )
3818 v4=floodlightDistance;
3819 v5=floodlightIntensity;
3821 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
3823 floodlightRGB[0]=v1;
3824 floodlightRGB[1]=v2;
3825 floodlightRGB[2]=v3;
3827 if (VectorLength(floodlightRGB)==0)
3829 VectorSet(floodlightRGB,240,240,255);
3835 floodlightDistance=v4;
3836 floodlightIntensity=v5;
3838 floodlighty = qtrue;
3839 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3843 VectorSet(floodlightRGB,240,240,255);
3844 //floodlighty = qtrue;
3845 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3847 VectorNormalize(floodlightRGB,floodlightRGB);
3850 //27 - lighttracer style ambient occlusion light hack.
3851 //Kudos to the dirtmapping author for most of this source.
3852 void FloodLightRawLightmap( int rawLightmapNum )
3854 int i, x, y, sx, sy, *cluster;
3855 float *origin, *normal, *floodlight, *floodlight2, average, samples;
3857 surfaceInfo_t *info;
3860 /* bail if this number exceeds the number of raw lightmaps */
3861 if( rawLightmapNum >= numRawLightmaps )
3865 lm = &rawLightmaps[ rawLightmapNum ];
3867 memset(&trace,0,sizeof(trace_t));
3869 trace.testOcclusion = qtrue;
3870 trace.forceSunlight = qfalse;
3871 trace.twoSided = qtrue;
3872 trace.recvShadows = lm->recvShadows;
3873 trace.numSurfaces = lm->numLightSurfaces;
3874 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
3875 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
3876 trace.testAll = qfalse;
3877 trace.distance = 1024;
3879 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
3880 //trace.twoSided = qfalse;
3881 for( i = 0; i < trace.numSurfaces; i++ )
3884 info = &surfaceInfos[ trace.surfaces[ i ] ];
3886 /* check twosidedness */
3887 if( info->si->twoSided )
3889 trace.twoSided = qtrue;
3895 for( y = 0; y < lm->sh; y++ )
3897 for( x = 0; x < lm->sw; x++ )
3900 cluster = SUPER_CLUSTER( x, y );
3901 origin = SUPER_ORIGIN( x, y );
3902 normal = SUPER_NORMAL( x, y );
3903 floodlight = SUPER_FLOODLIGHT( x, y );
3905 /* set default dirt */
3908 /* only look at mapped luxels */
3913 trace.cluster = *cluster;
3914 VectorCopy( origin, trace.origin );
3915 VectorCopy( normal, trace.normal );
3920 *floodlight = FloodLightForSample( &trace );
3924 /* testing no filtering */
3928 for( y = 0; y < lm->sh; y++ )
3930 for( x = 0; x < lm->sw; x++ )
3933 cluster = SUPER_CLUSTER( x, y );
3934 floodlight = SUPER_FLOODLIGHT( x, y );
3936 /* filter dirt by adjacency to unmapped luxels */
3937 average = *floodlight;
3939 for( sy = (y - 1); sy <= (y + 1); sy++ )
3941 if( sy < 0 || sy >= lm->sh )
3944 for( sx = (x - 1); sx <= (x + 1); sx++ )
3946 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
3949 /* get neighboring luxel */
3950 cluster = SUPER_CLUSTER( sx, sy );
3951 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
3952 if( *cluster < 0 || *floodlight2 <= 0.0f )
3956 average += *floodlight2;
3961 if( samples <= 0.0f )
3966 if( samples <= 0.0f )
3970 *floodlight = average / samples;
3976 FloodLightForSample()
3977 calculates floodlight value for a given sample
3978 once again, kudos to the dirtmapping coder
3980 float FloodLightForSample( trace_t *trace )
3986 float gatherLight, outLight;
3987 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
3995 if( trace == NULL || trace->cluster < 0 )
4000 dd = floodlightDistance;
4001 VectorCopy( trace->normal, normal );
4003 /* check if the normal is aligned to the world-up */
4004 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f )
4006 if( normal[ 2 ] == 1.0f )
4008 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4009 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4011 else if( normal[ 2 ] == -1.0f )
4013 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4014 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4019 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4020 CrossProduct( normal, worldUp, myRt );
4021 VectorNormalize( myRt, myRt );
4022 CrossProduct( myRt, normal, myUp );
4023 VectorNormalize( myUp, myUp );
4026 /* iterate through ordered vectors */
4027 for( i = 0; i < numFloodVectors; i++ )
4029 if (floodlight_lowquality==qtrue)
4031 if (rand()%10 != 0 ) continue;
4036 /* transform vector into tangent space */
4037 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4038 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4039 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4042 VectorMA( trace->origin, dd, direction, trace->end );
4044 //VectorMA( trace->origin, 1, direction, trace->origin );
4046 SetupTrace( trace );
4051 if (trace->compileFlags & C_SKY )
4055 else if ( trace->opaque )
4057 VectorSubtract( trace->hit, trace->origin, displacement );
4058 d=VectorLength( displacement );
4060 // d=trace->distance;
4061 //if (d>256) gatherDirt+=1;
4063 if (contribution>1) contribution=1.0f;
4065 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4068 gatherLight+=contribution;
4072 if( gatherLight <= 0.0f )
4080 outLight=gatherLight;
4081 if( outLight > 1.0f )
4084 /* return to sender */