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 ){
54 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
55 if ( scale <= 0.0f ) {
59 scale *= lightmapBrightness;
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 ) {
75 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
78 if ( lightmapExposure == 0 ) {
79 /* clamp with color normalization */
81 if ( sample[ 1 ] > max ) {
84 if ( sample[ 2 ] > max ) {
88 VectorScale( sample, ( 255.0f / max ), sample );
93 inv = 1.f / lightmapExposure;
97 if ( sample[ 1 ] > max ) {
100 if ( sample[ 2 ] > max ) {
104 dif = ( 1 - exp( -max * inv ) ) * 255;
114 for ( i = 0; i < 3; i++ )
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );
125 if ( lightmapContrast != 1.0f ){
126 for ( i = 0; i < 3; i++ ){
127 sample[i] = lightmapContrast * ( sample[i] - 128 ) + 128;
128 if ( sample[i] < 0 ){
132 if ( ( sample[0] > 255 ) || ( sample[1] > 255 ) || ( sample[2] > 255 ) ) {
133 max = sample[0] > sample[1] ? sample[0] : sample[1];
134 max = max > sample[2] ? max : sample[2];
135 sample[0] = sample[0] * 255 / max;
136 sample[1] = sample[1] * 255 / max;
137 sample[2] = sample[2] * 255 / max;
142 if ( lightmapsRGB ) {
143 sample[0] = floor( Image_sRGBFloatFromLinearFloat( sample[0] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
144 sample[1] = floor( Image_sRGBFloatFromLinearFloat( sample[1] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
145 sample[2] = floor( Image_sRGBFloatFromLinearFloat( sample[2] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
149 colorBytes[ 0 ] = sample[ 0 ];
150 colorBytes[ 1 ] = sample[ 1 ];
151 colorBytes[ 2 ] = sample[ 2 ];
156 /* -------------------------------------------------------------------------------
158 this section deals with phong shading (normal interpolation across brush faces)
160 ------------------------------------------------------------------------------- */
164 smooths together coincident vertex normals across the bsp
167 #define MAX_SAMPLES 256
168 #define THETA_EPSILON 0.000001
169 #define EQUAL_NORMAL_EPSILON 0.01
171 void SmoothNormals( void ){
172 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
173 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
174 bspDrawSurface_t *ds;
178 vec3_t average, diff;
179 int indexes[ MAX_SAMPLES ];
180 vec3_t votes[ MAX_SAMPLES ];
183 /* allocate shade angle table */
184 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
185 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
187 /* allocate smoothed table */
188 cs = ( numBSPDrawVerts / 8 ) + 1;
189 smoothed = safe_malloc( cs );
190 memset( smoothed, 0, cs );
192 /* set default shade angle */
193 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
196 /* run through every surface and flag verts belonging to non-lightmapped surfaces
197 and set per-vertex smoothing angle */
198 for ( i = 0; i < numBSPDrawSurfaces; i++ )
201 ds = &bspDrawSurfaces[ i ];
203 /* get shader for shade angle */
204 si = surfaceInfos[ i ].si;
205 if ( si->shadeAngleDegrees ) {
206 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
209 shadeAngle = defaultShadeAngle;
211 if ( shadeAngle > maxShadeAngle ) {
212 maxShadeAngle = shadeAngle;
216 for ( j = 0; j < ds->numVerts; j++ )
218 f = ds->firstVert + j;
219 shadeAngles[ f ] = shadeAngle;
220 if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
221 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
225 /* ydnar: optional force-to-trisoup */
226 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
227 ds->surfaceType = MST_TRIANGLE_SOUP;
228 ds->lightmapNum[ 0 ] = -3;
232 /* bail if no surfaces have a shade angle */
233 if ( maxShadeAngle == 0 ) {
241 start = I_FloatTime();
243 /* go through the list of vertexes */
244 for ( i = 0; i < numBSPDrawVerts; i++ )
247 f = 10 * i / numBSPDrawVerts;
250 Sys_Printf( "%i...", f );
253 /* already smoothed? */
254 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
259 VectorClear( average );
263 /* build a table of coincident vertexes */
264 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
266 /* already smoothed? */
267 if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
272 if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
276 /* use smallest shade angle */
277 shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
279 /* check shade angle */
280 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
284 else if ( dot < -1.0 ) {
287 testAngle = acos( dot ) + THETA_EPSILON;
288 if ( testAngle >= shadeAngle ) {
289 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
292 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
294 /* add to the list */
295 indexes[ numVerts++ ] = j;
298 smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
300 /* see if this normal has already been voted */
301 for ( k = 0; k < numVotes; k++ )
303 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
304 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
305 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
306 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
311 /* add a new vote? */
312 if ( k == numVotes && numVotes < MAX_SAMPLES ) {
313 VectorAdd( average, bspDrawVerts[ j ].normal, average );
314 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
319 /* don't average for less than 2 verts */
320 if ( numVerts < 2 ) {
325 if ( VectorNormalize( average, average ) > 0 ) {
327 for ( j = 0; j < numVerts; j++ )
328 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
332 /* free the tables */
337 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
342 /* -------------------------------------------------------------------------------
344 this section deals with phong shaded lightmap tracing
346 ------------------------------------------------------------------------------- */
348 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
352 calculates the st tangent vectors for normalmapping
355 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
361 /* calculate barycentric basis for the triangle */
362 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 ] );
363 if ( fabs( bb ) < 0.00000001f ) {
368 for ( i = 0; i < numVerts; i++ )
370 /* calculate s tangent vector */
371 s = dv[ i ]->st[ 0 ] + 10.0f;
372 t = dv[ i ]->st[ 1 ];
373 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
374 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
375 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
377 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
378 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
379 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
381 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
382 VectorNormalize( stv[ i ], stv[ i ] );
384 /* calculate t tangent vector */
385 s = dv[ i ]->st[ 0 ];
386 t = dv[ i ]->st[ 1 ] + 10.0f;
387 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
388 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
389 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
391 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
392 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
393 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
395 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
396 VectorNormalize( ttv[ i ], ttv[ i ] );
399 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
400 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
403 /* return to caller */
412 perterbs the normal by the shader's normalmap in tangent space
415 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
421 VectorCopy( dv->normal, pNormal );
423 /* sample normalmap */
424 if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
428 /* remap sampled normal from [0,255] to [-1,-1] */
429 for ( i = 0; i < 3; i++ )
430 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
432 /* scale tangent vectors and add to original normal */
433 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
434 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
435 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
437 /* renormalize and return */
438 VectorNormalize( pNormal, pNormal );
445 maps a luxel for triangle bv at
449 #define BOGUS_NUDGE -99999.0f
451 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 ] ){
452 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
453 float *luxel, *origin, *normal, d, lightmapSampleOffset;
460 vec4_t sideplane, hostplane;
465 static float nudges[][ 2 ] =
467 //%{ 0, 0 }, /* try center first */
468 { -NUDGE, 0 }, /* left */
469 { NUDGE, 0 }, /* right */
470 { 0, NUDGE }, /* up */
471 { 0, -NUDGE }, /* down */
472 { -NUDGE, NUDGE }, /* left/up */
473 { NUDGE, -NUDGE }, /* right/down */
474 { NUDGE, NUDGE }, /* right/up */
475 { -NUDGE, -NUDGE }, /* left/down */
476 { BOGUS_NUDGE, BOGUS_NUDGE }
480 /* find luxel xy coords (fixme: subtract 0.5?) */
481 x = dv->lightmap[ 0 ][ 0 ];
482 y = dv->lightmap[ 0 ][ 1 ];
486 else if ( x >= lm->sw ) {
492 else if ( y >= lm->sh ) {
496 /* set shader and cluster list */
497 if ( info != NULL ) {
499 numClusters = info->numSurfaceClusters;
500 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
509 /* get luxel, origin, cluster, and normal */
510 luxel = SUPER_LUXEL( 0, x, y );
511 origin = SUPER_ORIGIN( x, y );
512 normal = SUPER_NORMAL( x, y );
513 cluster = SUPER_CLUSTER( x, y );
515 /* don't attempt to remap occluded luxels for planar surfaces */
516 if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
520 /* only average the normal for premapped luxels */
521 else if ( ( *cluster ) >= 0 ) {
522 /* do bumpmap calculations */
524 PerturbNormal( dv, si, pNormal, stv, ttv );
527 VectorCopy( dv->normal, pNormal );
530 /* add the additional normal data */
531 VectorAdd( normal, pNormal, normal );
536 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
540 /* axial lightmap projection */
541 if ( lm->vecs != NULL ) {
542 /* calculate an origin for the sample from the lightmap vectors */
543 VectorCopy( lm->origin, origin );
544 for ( i = 0; i < 3; i++ )
546 /* add unless it's the axis, which is taken care of later */
547 if ( i == lm->axisNum ) {
550 origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
553 /* project the origin onto the plane */
554 d = DotProduct( origin, plane ) - plane[ 3 ];
555 d /= plane[ lm->axisNum ];
556 origin[ lm->axisNum ] -= d;
559 /* non axial lightmap projection (explicit xyz) */
561 VectorCopy( dv->xyz, origin );
564 //////////////////////
565 //27's test to make sure samples stay within the triangle boundaries
566 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
567 //2) if it does, nudge it onto the correct side.
569 if ( worldverts != NULL && lightmapTriangleCheck ) {
570 for ( j = 0; j < 3; j++ )
572 VectorCopy( worldverts[j],cverts[j] );
574 PlaneFromPoints( hostplane,cverts[0],cverts[1],cverts[2] );
576 for ( j = 0; j < 3; j++ )
578 for ( i = 0; i < 3; i++ )
580 //build plane using 2 edges and a normal
581 next = ( i + 1 ) % 3;
583 VectorCopy( cverts[next],temp );
584 VectorAdd( temp,hostplane,temp );
585 PlaneFromPoints( sideplane,cverts[i],cverts[ next ], temp );
587 //planetest sample point
588 e = DotProduct( origin,sideplane );
589 e = e - sideplane[3];
592 //VectorClear(origin);
593 //Move the sample point back inside triangle bounds
594 origin[0] -= sideplane[0] * ( e + 1 );
595 origin[1] -= sideplane[1] * ( e + 1 );
596 origin[2] -= sideplane[2] * ( e + 1 );
598 VectorClear( origin );
605 ////////////////////////
607 /* planar surfaces have precalculated lightmap vectors for nudging */
608 if ( lm->plane != NULL ) {
609 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
610 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
611 VectorCopy( lm->plane, vecs[ 2 ] );
614 /* non-planar surfaces must calculate them */
617 if ( plane != NULL ) {
618 VectorCopy( plane, vecs[ 2 ] );
621 VectorCopy( dv->normal, vecs[ 2 ] );
623 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
626 /* push the origin off the surface a bit */
628 lightmapSampleOffset = si->lightmapSampleOffset;
631 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
633 if ( lm->axisNum < 0 ) {
634 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
636 else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
637 origin[ lm->axisNum ] -= lightmapSampleOffset;
640 origin[ lm->axisNum ] += lightmapSampleOffset;
643 VectorCopy( origin,origintwo );
644 if ( lightmapExtraVisClusterNudge ) {
645 origintwo[0] += vecs[2][0];
646 origintwo[1] += vecs[2][1];
647 origintwo[2] += vecs[2][2];
651 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
653 /* another retarded hack, storing nudge count in luxel[ 1 ] */
656 /* point in solid? (except in dark mode) */
657 if ( pointCluster < 0 && dark == qfalse ) {
658 /* nudge the the location around */
660 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
662 /* nudge the vector around a bit */
663 for ( i = 0; i < 3; i++ )
665 /* set nudged point*/
666 nudged[ i ] = origintwo[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
670 /* get pvs cluster */
671 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
672 if ( pointCluster >= 0 ) {
673 VectorCopy( nudged, origin );
679 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
680 if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
681 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
682 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
683 if ( pointCluster >= 0 ) {
684 VectorCopy( nudged, origin );
690 if ( pointCluster < 0 ) {
691 ( *cluster ) = CLUSTER_OCCLUDED;
692 VectorClear( origin );
693 VectorClear( normal );
699 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
701 /* do bumpmap calculations */
703 PerturbNormal( dv, si, pNormal, stv, ttv );
706 VectorCopy( dv->normal, pNormal );
709 /* store the cluster and normal */
710 ( *cluster ) = pointCluster;
711 VectorCopy( pNormal, normal );
713 /* store explicit mapping pass and implicit mapping pass */
728 recursively subdivides a triangle until its edges are shorter
729 than the distance between two luxels (thanks jc :)
732 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 ] ){
733 bspDrawVert_t mid, *dv2[ 3 ];
737 /* map the vertexes */
739 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
740 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
741 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
747 float *a, *b, dx, dy, dist, maxDist;
750 /* find the longest edge and split it */
753 for ( i = 0; i < 3; i++ )
756 a = dv[ i ]->lightmap[ 0 ];
757 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
760 dx = a[ 0 ] - b[ 0 ];
761 dy = a[ 1 ] - b[ 1 ];
762 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
765 if ( dist > maxDist ) {
771 /* try to early out */
772 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
777 /* split the longest edge and map it */
778 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
779 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
781 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
782 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
784 /* recurse to first triangle */
785 VectorCopy( dv, dv2 );
787 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
789 /* recurse to second triangle */
790 VectorCopy( dv, dv2 );
791 dv2[ ( max + 1 ) % 3 ] = ∣
792 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
799 seed function for MapTriangle_r()
800 requires a cw ordered triangle
803 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
806 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
807 vec3_t worldverts[ 3 ];
810 /* get plane if possible */
811 if ( lm->plane != NULL ) {
812 VectorCopy( lm->plane, plane );
813 plane[ 3 ] = lm->plane[ 3 ];
816 /* otherwise make one from the points */
817 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
821 /* check to see if we need to calculate texture->world tangent vectors */
822 if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
832 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
833 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
834 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
836 /* map the vertexes */
837 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
838 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
839 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
841 /* 2002-11-20: prefer axial triangle edges */
843 /* subdivide the triangle */
844 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
848 for ( i = 0; i < 3; i++ )
851 bspDrawVert_t *dv2[ 3 ];
855 a = dv[ i ]->lightmap[ 0 ];
856 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
858 /* make degenerate triangles for mapping edges */
859 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
861 dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
862 dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
864 /* map the degenerate triangle */
865 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
876 recursively subdivides a quad until its edges are shorter
877 than the distance between two luxels
880 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 ] ){
881 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
888 float *a, *b, dx, dy, dist, maxDist;
891 /* find the longest edge and split it */
894 for ( i = 0; i < 4; i++ )
897 a = dv[ i ]->lightmap[ 0 ];
898 b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
901 dx = a[ 0 ] - b[ 0 ];
902 dy = a[ 1 ] - b[ 1 ];
903 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
906 if ( dist > maxDist ) {
912 /* try to early out */
913 if ( max < 0 || maxDist <= subdivideThreshold ) {
918 /* we only care about even/odd edges */
921 /* split the longest edges */
922 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
923 LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
925 /* map the vertexes */
926 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
927 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
931 /* recurse to first quad */
933 dv2[ 1 ] = &mid[ 0 ];
934 dv2[ 2 ] = &mid[ 1 ];
936 MapQuad_r( lm, info, dv2, plane, stv, ttv );
938 /* recurse to second quad */
939 dv2[ 0 ] = &mid[ 0 ];
942 dv2[ 3 ] = &mid[ 1 ];
943 MapQuad_r( lm, info, dv2, plane, stv, ttv );
949 /* recurse to first quad */
952 dv2[ 2 ] = &mid[ 0 ];
953 dv2[ 3 ] = &mid[ 1 ];
954 MapQuad_r( lm, info, dv2, plane, stv, ttv );
956 /* recurse to second quad */
957 dv2[ 0 ] = &mid[ 1 ];
958 dv2[ 1 ] = &mid[ 0 ];
961 MapQuad_r( lm, info, dv2, plane, stv, ttv );
969 seed function for MapQuad_r()
970 requires a cw ordered triangle quad
973 #define QUAD_PLANAR_EPSILON 0.5f
975 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
978 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
981 /* get plane if possible */
982 if ( lm->plane != NULL ) {
983 VectorCopy( lm->plane, plane );
984 plane[ 3 ] = lm->plane[ 3 ];
987 /* otherwise make one from the points */
988 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
992 /* 4th point must fall on the plane */
993 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
994 if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
998 /* check to see if we need to calculate texture->world tangent vectors */
999 if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
1009 /* map the vertexes */
1010 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
1011 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
1012 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
1013 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
1015 /* subdivide the quad */
1016 MapQuad_r( lm, info, dv, plane, stv, ttv );
1024 maps the locations, normals, and pvs clusters for a raw lightmap
1027 #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)
1029 void MapRawLightmap( int rawLightmapNum ){
1030 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1031 float *luxel, *origin, *normal, samples, radius, pass;
1033 bspDrawSurface_t *ds;
1034 surfaceInfo_t *info;
1035 mesh_t src, *subdivided, *mesh;
1036 bspDrawVert_t *verts, *dv[ 4 ], fake;
1039 /* bail if this number exceeds the number of raw lightmaps */
1040 if ( rawLightmapNum >= numRawLightmaps ) {
1045 lm = &rawLightmaps[ rawLightmapNum ];
1047 /* -----------------------------------------------------------------
1048 map referenced surfaces onto the raw lightmap
1049 ----------------------------------------------------------------- */
1051 /* walk the list of surfaces on this raw lightmap */
1052 for ( n = 0; n < lm->numLightSurfaces; n++ )
1054 /* with > 1 surface per raw lightmap, clear occluded */
1056 for ( y = 0; y < lm->sh; y++ )
1058 for ( x = 0; x < lm->sw; x++ )
1061 cluster = SUPER_CLUSTER( x, y );
1062 if ( *cluster < 0 ) {
1063 *cluster = CLUSTER_UNMAPPED;
1070 num = lightSurfaces[ lm->firstLightSurface + n ];
1071 ds = &bspDrawSurfaces[ num ];
1072 info = &surfaceInfos[ num ];
1074 /* bail if no lightmap to calculate */
1075 if ( info->lm != lm ) {
1080 /* map the surface onto the lightmap origin/cluster/normal buffers */
1081 switch ( ds->surfaceType )
1085 verts = yDrawVerts + ds->firstVert;
1087 /* map the triangles */
1088 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1090 for ( i = 0; i < ds->numIndexes; i += 3 )
1092 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1093 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1094 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1095 MapTriangle( lm, info, dv, mapNonAxial );
1101 /* make a mesh from the drawsurf */
1102 src.width = ds->patchWidth;
1103 src.height = ds->patchHeight;
1104 src.verts = &yDrawVerts[ ds->firstVert ];
1105 //% subdivided = SubdivideMesh( src, 8, 512 );
1106 subdivided = SubdivideMesh2( src, info->patchIterations );
1108 /* fit it to the curve and remove colinear verts on rows/columns */
1109 PutMeshOnCurve( *subdivided );
1110 mesh = RemoveLinearMeshColumnsRows( subdivided );
1111 FreeMesh( subdivided );
1114 verts = mesh->verts;
1119 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1120 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1121 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1122 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1126 /* map the mesh quads */
1129 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1131 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1133 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1136 pw[ 0 ] = x + ( y * mesh->width );
1137 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1138 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1139 pw[ 3 ] = x + 1 + ( y * mesh->width );
1140 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1145 /* get drawverts and map first triangle */
1146 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1147 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1148 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1149 MapTriangle( lm, info, dv, mapNonAxial );
1151 /* get drawverts and map second triangle */
1152 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1153 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1154 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1155 MapTriangle( lm, info, dv, mapNonAxial );
1162 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1164 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1167 pw[ 0 ] = x + ( y * mesh->width );
1168 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1169 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1170 pw[ 3 ] = x + 1 + ( y * mesh->width );
1176 /* attempt to map quad first */
1177 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1178 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1179 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1180 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1181 if ( MapQuad( lm, info, dv ) ) {
1185 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1187 /* get drawverts and map first triangle */
1188 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1189 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1190 MapTriangle( lm, info, dv, mapNonAxial );
1192 /* get drawverts and map second triangle */
1193 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1194 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1195 MapTriangle( lm, info, dv, mapNonAxial );
1211 /* -----------------------------------------------------------------
1212 average and clean up luxel normals
1213 ----------------------------------------------------------------- */
1215 /* walk the luxels */
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 mapped luxels */
1226 if ( *cluster < 0 ) {
1230 /* the normal data could be the sum of multiple samples */
1231 if ( luxel[ 3 ] > 1.0f ) {
1232 VectorNormalize( normal, normal );
1235 /* mark this luxel as having only one normal */
1240 /* non-planar surfaces stop here */
1241 if ( lm->plane == NULL ) {
1245 /* -----------------------------------------------------------------
1246 map occluded or unuxed luxels
1247 ----------------------------------------------------------------- */
1249 /* walk the luxels */
1250 radius = floor( superSample / 2 );
1251 radius = radius > 0 ? radius : 1.0f;
1253 for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1255 for ( y = 0; y < lm->sh; y++ )
1257 for ( x = 0; x < lm->sw; x++ )
1260 luxel = SUPER_LUXEL( 0, x, y );
1261 normal = SUPER_NORMAL( x, y );
1262 cluster = SUPER_CLUSTER( x, y );
1264 /* only look at unmapped luxels */
1265 if ( *cluster != CLUSTER_UNMAPPED ) {
1269 /* divine a normal and origin from neighboring luxels */
1270 VectorClear( fake.xyz );
1271 VectorClear( fake.normal );
1272 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1273 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1275 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1277 if ( sy < 0 || sy >= lm->sh ) {
1281 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1283 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1287 /* get neighboring luxel */
1288 luxel = SUPER_LUXEL( 0, sx, sy );
1289 origin = SUPER_ORIGIN( sx, sy );
1290 normal = SUPER_NORMAL( sx, sy );
1291 cluster = SUPER_CLUSTER( sx, sy );
1293 /* only consider luxels mapped in previous passes */
1294 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1298 /* add its distinctiveness to our own */
1299 VectorAdd( fake.xyz, origin, fake.xyz );
1300 VectorAdd( fake.normal, normal, fake.normal );
1301 samples += luxel[ 3 ];
1306 if ( samples == 0.0f ) {
1311 VectorDivide( fake.xyz, samples, fake.xyz );
1312 //% VectorDivide( fake.normal, samples, fake.normal );
1313 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1317 /* map the fake vert */
1318 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1323 /* -----------------------------------------------------------------
1324 average and clean up luxel normals
1325 ----------------------------------------------------------------- */
1327 /* walk the luxels */
1328 for ( y = 0; y < lm->sh; y++ )
1330 for ( x = 0; x < lm->sw; x++ )
1333 luxel = SUPER_LUXEL( 0, x, y );
1334 normal = SUPER_NORMAL( x, y );
1335 cluster = SUPER_CLUSTER( x, y );
1337 /* only look at mapped luxels */
1338 if ( *cluster < 0 ) {
1342 /* the normal data could be the sum of multiple samples */
1343 if ( luxel[ 3 ] > 1.0f ) {
1344 VectorNormalize( normal, normal );
1347 /* mark this luxel as having only one normal */
1355 for ( y = 0; y < lm->sh; y++ )
1357 for ( x = 0; x < lm->sw; x++ )
1362 cluster = SUPER_CLUSTER( x, y );
1363 origin = SUPER_ORIGIN( x, y );
1364 normal = SUPER_NORMAL( x, y );
1365 luxel = SUPER_LUXEL( x, y );
1367 if ( *cluster < 0 ) {
1371 /* check if within the bounding boxes of all surfaces referenced */
1372 ClearBounds( mins, maxs );
1373 for ( n = 0; n < lm->numLightSurfaces; n++ )
1376 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1377 TOL = info->sampleSize + 2;
1378 AddPointToBounds( info->mins, mins, maxs );
1379 AddPointToBounds( info->maxs, mins, maxs );
1380 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1381 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1382 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1388 if ( n < lm->numLightSurfaces ) {
1392 /* report bogus origin */
1393 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",
1394 rawLightmapNum, x, y, *cluster,
1395 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1396 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1397 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1408 sets up dirtmap (ambient occlusion)
1411 #define DIRT_CONE_ANGLE 88 /* degrees */
1412 #define DIRT_NUM_ANGLE_STEPS 16
1413 #define DIRT_NUM_ELEVATION_STEPS 3
1414 #define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1416 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1417 static int numDirtVectors = 0;
1419 void SetupDirt( void ){
1421 float angle, elevation, angleStep, elevationStep;
1425 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1427 /* calculate angular steps */
1428 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1429 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1433 for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1435 /* iterate elevation */
1436 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1438 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1439 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1440 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1445 /* emit some statistics */
1446 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1452 calculates dirt value for a given sample
1455 float DirtForSample( trace_t *trace ){
1457 float gatherDirt, outDirt, angle, elevation, ooDepth;
1458 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1465 if ( trace == NULL || trace->cluster < 0 ) {
1471 ooDepth = 1.0f / dirtDepth;
1472 VectorCopy( trace->normal, normal );
1474 /* check if the normal is aligned to the world-up */
1475 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
1476 if ( normal[ 2 ] == 1.0f ) {
1477 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1478 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1480 else if ( normal[ 2 ] == -1.0f ) {
1481 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1482 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1487 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1488 CrossProduct( normal, worldUp, myRt );
1489 VectorNormalize( myRt, myRt );
1490 CrossProduct( myRt, normal, myUp );
1491 VectorNormalize( myUp, myUp );
1494 /* 1 = random mode, 0 (well everything else) = non-random mode */
1495 if ( dirtMode == 1 ) {
1497 for ( i = 0; i < numDirtVectors; i++ )
1499 /* get random vector */
1500 angle = Random() * DEG2RAD( 360.0f );
1501 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1502 temp[ 0 ] = cos( angle ) * sin( elevation );
1503 temp[ 1 ] = sin( angle ) * sin( elevation );
1504 temp[ 2 ] = cos( elevation );
1506 /* transform into tangent space */
1507 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1508 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1509 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1512 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1513 SetupTrace( trace );
1514 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1518 if ( trace->opaque && !( trace->compileFlags & C_SKY ) ) {
1519 VectorSubtract( trace->hit, trace->origin, displacement );
1520 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1526 /* iterate through ordered vectors */
1527 for ( i = 0; i < numDirtVectors; i++ )
1529 /* transform vector into tangent space */
1530 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1531 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1532 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1535 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1536 SetupTrace( trace );
1537 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1541 if ( trace->opaque ) {
1542 VectorSubtract( trace->hit, trace->origin, displacement );
1543 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1549 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1550 SetupTrace( trace );
1551 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1555 if ( trace->opaque ) {
1556 VectorSubtract( trace->hit, trace->origin, displacement );
1557 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1561 if ( gatherDirt <= 0.0f ) {
1565 /* apply gain (does this even do much? heh) */
1566 outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1567 if ( outDirt > 1.0f ) {
1572 outDirt *= dirtScale;
1573 if ( outDirt > 1.0f ) {
1577 /* return to sender */
1578 return 1.0f - outDirt;
1585 calculates dirty fraction for each luxel
1588 void DirtyRawLightmap( int rawLightmapNum ){
1589 int i, x, y, sx, sy, *cluster;
1590 float *origin, *normal, *dirt, *dirt2, average, samples;
1592 surfaceInfo_t *info;
1597 /* bail if this number exceeds the number of raw lightmaps */
1598 if ( rawLightmapNum >= numRawLightmaps ) {
1603 lm = &rawLightmaps[ rawLightmapNum ];
1606 trace.testOcclusion = qtrue;
1607 trace.forceSunlight = qfalse;
1608 trace.recvShadows = lm->recvShadows;
1609 trace.numSurfaces = lm->numLightSurfaces;
1610 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1611 trace.inhibitRadius = 0.0f;
1612 trace.testAll = qfalse;
1614 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1615 trace.twoSided = qfalse;
1616 for ( i = 0; i < trace.numSurfaces; i++ )
1619 info = &surfaceInfos[ trace.surfaces[ i ] ];
1621 /* check twosidedness */
1622 if ( info->si->twoSided ) {
1623 trace.twoSided = qtrue;
1629 for ( i = 0; i < trace.numSurfaces; i++ )
1632 info = &surfaceInfos[ trace.surfaces[ i ] ];
1634 /* check twosidedness */
1635 if ( info->si->noDirty ) {
1642 for ( y = 0; y < lm->sh; y++ )
1644 for ( x = 0; x < lm->sw; x++ )
1647 cluster = SUPER_CLUSTER( x, y );
1648 origin = SUPER_ORIGIN( x, y );
1649 normal = SUPER_NORMAL( x, y );
1650 dirt = SUPER_DIRT( x, y );
1652 /* set default dirt */
1655 /* only look at mapped luxels */
1656 if ( *cluster < 0 ) {
1660 /* don't apply dirty on this surface */
1667 trace.cluster = *cluster;
1668 VectorCopy( origin, trace.origin );
1669 VectorCopy( normal, trace.normal );
1672 *dirt = DirtForSample( &trace );
1676 /* testing no filtering */
1680 for ( y = 0; y < lm->sh; y++ )
1682 for ( x = 0; x < lm->sw; x++ )
1685 cluster = SUPER_CLUSTER( x, y );
1686 dirt = SUPER_DIRT( x, y );
1688 /* filter dirt by adjacency to unmapped luxels */
1691 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1693 if ( sy < 0 || sy >= lm->sh ) {
1697 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1699 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1703 /* get neighboring luxel */
1704 cluster = SUPER_CLUSTER( sx, sy );
1705 dirt2 = SUPER_DIRT( sx, sy );
1706 if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1716 if ( samples <= 0.0f ) {
1722 if ( samples <= 0.0f ) {
1727 *dirt = average / samples;
1736 calculates the pvs cluster, origin, normal of a sub-luxel
1739 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1740 int i, *cluster, *cluster2;
1741 float *origin, *origin2, *normal; //% , *normal2;
1742 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1745 /* calulate x vector */
1746 if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1747 cluster = SUPER_CLUSTER( x, y );
1748 origin = SUPER_ORIGIN( x, y );
1749 //% normal = SUPER_NORMAL( x, y );
1750 cluster2 = SUPER_CLUSTER( x + 1, y );
1751 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1752 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1754 else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1755 cluster = SUPER_CLUSTER( x - 1, y );
1756 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1757 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1758 cluster2 = SUPER_CLUSTER( x, y );
1759 origin2 = SUPER_ORIGIN( x, y );
1760 //% normal2 = SUPER_NORMAL( x, y );
1763 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap S vector\n" );
1766 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1767 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1769 /* calulate y vector */
1770 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1771 cluster = SUPER_CLUSTER( x, y );
1772 origin = SUPER_ORIGIN( x, y );
1773 //% normal = SUPER_NORMAL( x, y );
1774 cluster2 = SUPER_CLUSTER( x, y + 1 );
1775 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1776 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1778 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1779 cluster = SUPER_CLUSTER( x, y - 1 );
1780 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1781 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1782 cluster2 = SUPER_CLUSTER( x, y );
1783 origin2 = SUPER_ORIGIN( x, y );
1784 //% normal2 = SUPER_NORMAL( x, y );
1787 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap T vector\n" );
1790 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1792 /* calculate new origin */
1793 for ( i = 0; i < 3; i++ )
1794 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1797 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1798 if ( *sampleCluster < 0 ) {
1802 /* calculate new normal */
1803 normal = SUPER_NORMAL( x, y );
1804 VectorCopy( normal, sampleNormal );
1812 SubsampleRawLuxel_r()
1813 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1816 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1817 int b, samples, mapped, lighted;
1820 vec3_t deluxel[ 4 ];
1821 vec3_t origin[ 4 ], normal[ 4 ];
1822 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1823 vec3_t color, direction = { 0, 0, 0 }, total;
1827 if ( lightLuxel[ 3 ] >= lightSamples ) {
1832 VectorClear( total );
1836 /* make 2x2 subsample stamp */
1837 for ( b = 0; b < 4; b++ )
1840 VectorCopy( sampleOrigin, origin[ b ] );
1842 /* calculate position */
1843 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1849 /* increment sample count */
1850 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1853 trace->cluster = *cluster;
1854 VectorCopy( origin[ b ], trace->origin );
1855 VectorCopy( normal[ b ], trace->normal );
1859 LightContributionToSample( trace );
1860 if ( trace->forceSubsampling > 1.0f ) {
1861 /* alphashadow: we subsample as deep as we can */
1867 /* add to totals (fixme: make contrast function) */
1868 VectorCopy( trace->color, luxel[ b ] );
1869 if ( lightDeluxel ) {
1870 VectorCopy( trace->directionContribution, deluxel[ b ] );
1872 VectorAdd( total, trace->color, total );
1873 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1878 /* subsample further? */
1879 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1880 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1881 lighted != 0 && lighted != mapped ) {
1882 for ( b = 0; b < 4; b++ )
1884 if ( cluster[ b ] < 0 ) {
1887 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1892 //% VectorClear( color );
1894 VectorCopy( lightLuxel, color );
1895 if ( lightDeluxel ) {
1896 VectorCopy( lightDeluxel, direction );
1899 for ( b = 0; b < 4; b++ )
1901 if ( cluster[ b ] < 0 ) {
1904 VectorAdd( color, luxel[ b ], color );
1905 if ( lightDeluxel ) {
1906 VectorAdd( direction, deluxel[ b ], direction );
1912 if ( samples > 0 ) {
1914 color[ 0 ] /= samples;
1915 color[ 1 ] /= samples;
1916 color[ 2 ] /= samples;
1919 VectorCopy( color, lightLuxel );
1920 lightLuxel[ 3 ] += 1.0f;
1922 if ( lightDeluxel ) {
1923 direction[ 0 ] /= samples;
1924 direction[ 1 ] /= samples;
1925 direction[ 2 ] /= samples;
1926 VectorCopy( direction, lightDeluxel );
1931 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1932 static void GaussLikeRandom( float sigma, float *x, float *y ){
1934 r = Random() * 2 * Q_PI;
1935 *x = sigma * 2.73861278752581783822 * cos( r );
1936 *y = sigma * 2.73861278752581783822 * sin( r );
1943 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1946 vec3_t origin, normal;
1947 vec3_t total, totaldirection;
1950 VectorClear( total );
1951 VectorClear( totaldirection );
1953 for ( b = 0; b < lightSamples; ++b )
1956 VectorCopy( sampleOrigin, origin );
1957 GaussLikeRandom( bias, &dx, &dy );
1959 /* calculate position */
1960 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1966 trace->cluster = cluster;
1967 VectorCopy( origin, trace->origin );
1968 VectorCopy( normal, trace->normal );
1970 LightContributionToSample( trace );
1971 VectorAdd( total, trace->color, total );
1972 if ( lightDeluxel ) {
1973 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1980 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1981 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1982 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1984 if ( lightDeluxel ) {
1985 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1986 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1987 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1995 IlluminateRawLightmap()
1996 illuminates the luxels
1999 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
2000 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
2001 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
2003 void IlluminateRawLightmap( int rawLightmapNum ){
2004 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
2005 int *cluster, *cluster2, mapped, lighted, totalLighted;
2006 size_t llSize, ldSize;
2008 surfaceInfo_t *info;
2009 qboolean filterColor, filterDir;
2011 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
2012 unsigned char *flag;
2013 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2014 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2015 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2017 float stackLightLuxels[ STACK_LL_SIZE ];
2020 /* bail if this number exceeds the number of raw lightmaps */
2021 if ( rawLightmapNum >= numRawLightmaps ) {
2026 lm = &rawLightmaps[ rawLightmapNum ];
2029 trace.testOcclusion = !noTrace;
2030 trace.forceSunlight = qfalse;
2031 trace.recvShadows = lm->recvShadows;
2032 trace.numSurfaces = lm->numLightSurfaces;
2033 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2034 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2036 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2037 trace.twoSided = qfalse;
2038 for ( i = 0; i < trace.numSurfaces; i++ )
2041 info = &surfaceInfos[ trace.surfaces[ i ] ];
2043 /* check twosidedness */
2044 if ( info->si->twoSided ) {
2045 trace.twoSided = qtrue;
2050 /* create a culled light list for this raw lightmap */
2051 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2053 /* -----------------------------------------------------------------
2055 ----------------------------------------------------------------- */
2058 numLuxelsIlluminated += ( lm->sw * lm->sh );
2060 /* test debugging state */
2061 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2062 /* debug fill the luxels */
2063 for ( y = 0; y < lm->sh; y++ )
2065 for ( x = 0; x < lm->sw; x++ )
2068 cluster = SUPER_CLUSTER( x, y );
2070 /* only fill mapped luxels */
2071 if ( *cluster < 0 ) {
2075 /* get particulars */
2076 luxel = SUPER_LUXEL( 0, x, y );
2077 origin = SUPER_ORIGIN( x, y );
2078 normal = SUPER_NORMAL( x, y );
2080 /* color the luxel with raw lightmap num? */
2081 if ( debugSurfaces ) {
2082 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2085 /* color the luxel with lightmap axis? */
2086 else if ( debugAxis ) {
2087 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2088 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2089 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2092 /* color the luxel with luxel cluster? */
2093 else if ( debugCluster ) {
2094 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2097 /* color the luxel with luxel origin? */
2098 else if ( debugOrigin ) {
2099 VectorSubtract( lm->maxs, lm->mins, temp );
2100 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2101 VectorSubtract( origin, lm->mins, temp2 );
2102 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2103 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2104 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2107 /* color the luxel with the normal */
2108 else if ( normalmap ) {
2109 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2110 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2111 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2114 /* otherwise clear it */
2116 VectorClear( luxel );
2126 /* allocate temporary per-light luxel storage */
2127 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2128 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2129 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2130 lightLuxels = stackLightLuxels;
2133 lightLuxels = safe_malloc( llSize );
2136 lightDeluxels = safe_malloc( ldSize );
2139 lightDeluxels = NULL;
2143 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2145 /* set ambient color */
2146 for ( y = 0; y < lm->sh; y++ )
2148 for ( x = 0; x < lm->sw; x++ )
2151 cluster = SUPER_CLUSTER( x, y );
2152 luxel = SUPER_LUXEL( 0, x, y );
2153 normal = SUPER_NORMAL( x, y );
2154 deluxel = SUPER_DELUXEL( x, y );
2156 /* blacken unmapped clusters */
2157 if ( *cluster < 0 ) {
2158 VectorClear( luxel );
2164 VectorCopy( ambientColor, luxel );
2166 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2168 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2169 if ( brightness < 0.00390625f ) {
2170 brightness = 0.00390625f;
2173 VectorScale( normal, brightness, deluxel );
2180 /* clear styled lightmaps */
2181 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2182 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2184 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2185 memset( lm->superLuxels[ lightmapNum ], 0, size );
2189 /* debugging code */
2190 //% if( trace.numLights <= 0 )
2191 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2193 /* walk light list */
2194 for ( i = 0; i < trace.numLights; i++ )
2197 trace.light = trace.lights[ i ];
2200 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2202 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2203 lm->styles[ lightmapNum ] == LS_NONE ) {
2208 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2209 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2210 Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2215 memset( lightLuxels, 0, llSize );
2217 memset( lightDeluxels, 0, ldSize );
2221 /* determine filter radius */
2222 filterRadius = lm->filterRadius > trace.light->filterRadius
2224 : trace.light->filterRadius;
2225 if ( filterRadius < 0.0f ) {
2226 filterRadius = 0.0f;
2229 /* set luxel filter radius */
2230 luxelFilterRadius = lm->sampleSize != 0 ? superSample * filterRadius / lm->sampleSize : 0;
2231 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2232 luxelFilterRadius = 1;
2235 /* allocate sampling flags storage */
2236 if ( lightSamples > 1 || lightRandomSamples ) {
2237 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2238 if ( lm->superFlags == NULL ) {
2239 lm->superFlags = safe_malloc( size );
2241 memset( (void *) lm->superFlags, 0, size );
2244 /* initial pass, one sample per luxel */
2245 for ( y = 0; y < lm->sh; y++ )
2247 for ( x = 0; x < lm->sw; x++ )
2250 cluster = SUPER_CLUSTER( x, y );
2251 if ( *cluster < 0 ) {
2255 /* get particulars */
2256 lightLuxel = LIGHT_LUXEL( x, y );
2257 lightDeluxel = LIGHT_DELUXEL( x, y );
2258 origin = SUPER_ORIGIN( x, y );
2259 normal = SUPER_NORMAL( x, y );
2260 flag = SUPER_FLAG( x, y );
2262 /* set contribution count */
2263 lightLuxel[ 3 ] = 1.0f;
2266 trace.cluster = *cluster;
2267 VectorCopy( origin, trace.origin );
2268 VectorCopy( normal, trace.normal );
2270 /* get light for this sample */
2271 LightContributionToSample( &trace );
2272 VectorCopy( trace.color, lightLuxel );
2274 /* add the contribution to the deluxemap */
2276 VectorCopy( trace.directionContribution, lightDeluxel );
2279 /* check for evilness */
2280 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) ) {
2282 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2285 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2291 /* don't even bother with everything else if nothing was lit */
2292 if ( totalLighted == 0 ) {
2296 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2297 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2298 if ( lightSamples > 1 || lightRandomSamples ) {
2300 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2302 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2307 VectorClear( total );
2309 /* test 2x2 stamp */
2310 for ( t = 0; t < 4; t++ )
2312 /* set sample coords */
2313 sx = x + tests[ t ][ 0 ];
2314 sy = y + tests[ t ][ 1 ];
2317 cluster = SUPER_CLUSTER( sx, sy );
2318 if ( *cluster < 0 ) {
2324 flag = SUPER_FLAG( sx, sy );
2325 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2326 /* force a lighted/mapped discrepancy so we subsample */
2331 lightLuxel = LIGHT_LUXEL( sx, sy );
2332 VectorAdd( total, lightLuxel, total );
2333 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2338 /* if total color is under a certain amount, then don't bother subsampling */
2339 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2343 /* if all 4 pixels are either in shadow or light, then don't subsample */
2344 if ( lighted != 0 && lighted != mapped ) {
2345 for ( t = 0; t < 4; t++ )
2347 /* set sample coords */
2348 sx = x + tests[ t ][ 0 ];
2349 sy = y + tests[ t ][ 1 ];
2352 cluster = SUPER_CLUSTER( sx, sy );
2353 if ( *cluster < 0 ) {
2356 flag = SUPER_FLAG( sx, sy );
2357 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2360 lightLuxel = LIGHT_LUXEL( sx, sy );
2361 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2362 origin = SUPER_ORIGIN( sx, sy );
2364 /* only subsample shadowed luxels */
2365 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2369 if ( lightRandomSamples ) {
2370 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2373 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2376 *flag |= FLAG_ALREADY_SUBSAMPLED;
2378 /* debug code to colorize subsampled areas to yellow */
2379 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2380 //% VectorSet( luxel, 255, 204, 0 );
2387 /* tertiary pass, apply dirt map (ambient occlusion) */
2390 for ( y = 0; y < lm->sh; y++ )
2392 for ( x = 0; x < lm->sw; x++ )
2395 cluster = SUPER_CLUSTER( x, y );
2396 if ( *cluster < 0 ) {
2400 /* get particulars */
2401 lightLuxel = LIGHT_LUXEL( x, y );
2402 dirt = SUPER_DIRT( x, y );
2404 /* scale light value */
2405 VectorScale( lightLuxel, *dirt, lightLuxel );
2410 /* allocate sampling lightmap storage */
2411 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2412 /* allocate sampling lightmap storage */
2413 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2414 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2415 memset( lm->superLuxels[ lightmapNum ], 0, size );
2419 if ( lightmapNum > 0 ) {
2420 lm->styles[ lightmapNum ] = trace.light->style;
2421 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2424 /* copy to permanent luxels */
2425 for ( y = 0; y < lm->sh; y++ )
2427 for ( x = 0; x < lm->sw; x++ )
2429 /* get cluster and origin */
2430 cluster = SUPER_CLUSTER( x, y );
2431 if ( *cluster < 0 ) {
2434 origin = SUPER_ORIGIN( x, y );
2437 if ( luxelFilterRadius ) {
2439 VectorClear( averageColor );
2440 VectorClear( averageDir );
2443 /* cheaper distance-based filtering */
2444 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2446 if ( sy < 0 || sy >= lm->sh ) {
2450 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2452 if ( sx < 0 || sx >= lm->sw ) {
2456 /* get particulars */
2457 cluster = SUPER_CLUSTER( sx, sy );
2458 if ( *cluster < 0 ) {
2461 lightLuxel = LIGHT_LUXEL( sx, sy );
2462 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2465 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2466 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2468 /* scale luxel by filter weight */
2469 VectorScale( lightLuxel, weight, color );
2470 VectorAdd( averageColor, color, averageColor );
2472 VectorScale( lightDeluxel, weight, direction );
2473 VectorAdd( averageDir, direction, averageDir );
2480 if ( samples <= 0.0f ) {
2484 /* scale into luxel */
2485 luxel = SUPER_LUXEL( lightmapNum, x, y );
2488 /* handle negative light */
2489 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2490 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2491 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2492 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2495 /* handle normal light */
2498 luxel[ 0 ] += averageColor[ 0 ] / samples;
2499 luxel[ 1 ] += averageColor[ 1 ] / samples;
2500 luxel[ 2 ] += averageColor[ 2 ] / samples;
2504 /* scale into luxel */
2505 deluxel = SUPER_DELUXEL( x, y );
2506 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2507 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2508 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2515 /* get particulars */
2516 lightLuxel = LIGHT_LUXEL( x, y );
2517 lightDeluxel = LIGHT_DELUXEL( x, y );
2518 luxel = SUPER_LUXEL( lightmapNum, x, y );
2519 deluxel = SUPER_DELUXEL( x, y );
2521 /* handle negative light */
2522 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2523 VectorScale( averageColor, -1.0f, averageColor );
2529 /* handle negative light */
2530 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2531 VectorSubtract( luxel, lightLuxel, luxel );
2534 /* handle normal light */
2536 VectorAdd( luxel, lightLuxel, luxel );
2540 VectorAdd( deluxel, lightDeluxel, deluxel );
2547 /* free temporary luxels */
2548 if ( lightLuxels != stackLightLuxels ) {
2549 free( lightLuxels );
2553 free( lightDeluxels );
2557 /* free light list */
2558 FreeTraceLights( &trace );
2560 /* floodlight pass */
2561 if ( floodlighty ) {
2562 FloodlightIlluminateLightmap( lm );
2565 if ( debugnormals ) {
2566 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2569 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2573 for ( y = 0; y < lm->sh; y++ )
2575 for ( x = 0; x < lm->sw; x++ )
2578 cluster = SUPER_CLUSTER( x, y );
2579 //% if( *cluster < 0 )
2582 /* get particulars */
2583 luxel = SUPER_LUXEL( lightmapNum, x, y );
2584 normal = SUPER_NORMAL( x, y );
2586 luxel[0] = ( normal[0] * 127 ) + 127;
2587 luxel[1] = ( normal[1] * 127 ) + 127;
2588 luxel[2] = ( normal[2] * 127 ) + 127;
2594 /* -----------------------------------------------------------------
2596 ----------------------------------------------------------------- */
2599 /* walk lightmaps */
2600 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2603 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2607 /* apply dirt to each luxel */
2608 for ( y = 0; y < lm->sh; y++ )
2610 for ( x = 0; x < lm->sw; x++ )
2613 cluster = SUPER_CLUSTER( x, y );
2615 /* get particulars */
2616 luxel = SUPER_LUXEL( lightmapNum, x, y );
2617 dirt = SUPER_DIRT( x, y );
2620 VectorScale( luxel, *dirt, luxel );
2624 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2631 /* -----------------------------------------------------------------
2633 ----------------------------------------------------------------- */
2635 /* walk lightmaps */
2636 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2639 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2643 /* average occluded luxels from neighbors */
2644 for ( y = 0; y < lm->sh; y++ )
2646 for ( x = 0; x < lm->sw; x++ )
2648 /* get particulars */
2649 cluster = SUPER_CLUSTER( x, y );
2650 luxel = SUPER_LUXEL( lightmapNum, x, y );
2651 deluxel = SUPER_DELUXEL( x, y );
2652 normal = SUPER_NORMAL( x, y );
2654 /* determine if filtering is necessary */
2655 filterColor = qfalse;
2657 if ( *cluster < 0 ||
2658 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2659 filterColor = qtrue;
2662 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2666 if ( !filterColor && !filterDir ) {
2670 /* choose seed amount */
2671 VectorClear( averageColor );
2672 VectorClear( averageDir );
2675 /* walk 3x3 matrix */
2676 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2678 if ( sy < 0 || sy >= lm->sh ) {
2682 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2684 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2688 /* get neighbor's particulars */
2689 cluster2 = SUPER_CLUSTER( sx, sy );
2690 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2691 deluxel2 = SUPER_DELUXEL( sx, sy );
2693 /* ignore unmapped/unlit luxels */
2694 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2695 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2699 /* add its distinctiveness to our own */
2700 VectorAdd( averageColor, luxel2, averageColor );
2701 samples += luxel2[ 3 ];
2703 VectorAdd( averageDir, deluxel2, averageDir );
2709 if ( samples <= 0.0f ) {
2713 /* dark lightmap seams */
2715 if ( lightmapNum == 0 ) {
2716 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2722 if ( filterColor ) {
2723 VectorDivide( averageColor, samples, luxel );
2727 VectorDivide( averageDir, samples, deluxel );
2730 /* set cluster to -3 */
2731 if ( *cluster < 0 ) {
2732 *cluster = CLUSTER_FLOODED;
2742 IlluminateVertexes()
2743 light the surface vertexes
2746 #define VERTEX_NUDGE 4.0f
2748 void IlluminateVertexes( int num ){
2749 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2750 int lightmapNum, numAvg;
2751 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2752 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2753 bspDrawSurface_t *ds;
2754 surfaceInfo_t *info;
2756 bspDrawVert_t *verts;
2758 float floodLightAmount;
2762 /* get surface, info, and raw lightmap */
2763 ds = &bspDrawSurfaces[ num ];
2764 info = &surfaceInfos[ num ];
2767 /* -----------------------------------------------------------------
2768 illuminate the vertexes
2769 ----------------------------------------------------------------- */
2771 /* calculate vertex lighting for surfaces without lightmaps */
2772 if ( lm == NULL || cpmaHack ) {
2774 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2775 trace.forceSunlight = info->si->forceSunlight;
2776 trace.recvShadows = info->recvShadows;
2777 trace.numSurfaces = 1;
2778 trace.surfaces = #
2779 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2781 /* twosided lighting */
2782 trace.twoSided = info->si->twoSided;
2784 /* make light list for this surface */
2785 CreateTraceLightsForSurface( num, &trace );
2788 verts = yDrawVerts + ds->firstVert;
2790 memset( avgColors, 0, sizeof( avgColors ) );
2792 /* walk the surface verts */
2793 for ( i = 0; i < ds->numVerts; i++ )
2795 /* get vertex luxel */
2796 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2798 /* color the luxel with raw lightmap num? */
2799 if ( debugSurfaces ) {
2800 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2803 /* color the luxel with luxel origin? */
2804 else if ( debugOrigin ) {
2805 VectorSubtract( info->maxs, info->mins, temp );
2806 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2807 VectorSubtract( origin, lm->mins, temp2 );
2808 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2809 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2810 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2813 /* color the luxel with the normal */
2814 else if ( normalmap ) {
2815 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2816 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2817 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2820 else if ( info->si->noVertexLight ) {
2821 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
2824 else if ( noVertexLighting > 0 ) {
2825 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
2828 /* illuminate the vertex */
2831 /* clear vertex luxel */
2832 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2834 /* try at initial origin */
2835 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2836 if ( trace.cluster >= 0 ) {
2838 VectorCopy( verts[ i ].xyz, trace.origin );
2839 VectorCopy( verts[ i ].normal, trace.normal );
2842 if ( dirty && !bouncing ) {
2843 dirt = DirtForSample( &trace );
2849 /* jal: floodlight */
2850 floodLightAmount = 0.0f;
2851 VectorClear( floodColor );
2852 if ( floodlighty && !bouncing ) {
2853 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2854 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2858 LightingAtSample( &trace, ds->vertexStyles, colors );
2861 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2864 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2866 /* jal: floodlight */
2867 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2870 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2871 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2872 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2876 /* is this sample bright enough? */
2877 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2878 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2879 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2880 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2881 /* nudge the sample point around a bit */
2882 for ( x = 0; x < 5; x++ )
2884 /* two's complement 0, 1, -1, 2, -2, etc */
2885 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2887 for ( y = 0; y < 5; y++ )
2889 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2891 for ( z = 0; z < 5; z++ )
2893 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2896 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2897 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2898 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2900 /* try at nudged origin */
2901 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2902 if ( trace.cluster < 0 ) {
2907 if ( dirty && !bouncing ) {
2908 dirt = DirtForSample( &trace );
2914 /* jal: floodlight */
2915 floodLightAmount = 0.0f;
2916 VectorClear( floodColor );
2917 if ( floodlighty && !bouncing ) {
2918 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2919 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2923 LightingAtSample( &trace, ds->vertexStyles, colors );
2926 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2929 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2931 /* jal: floodlight */
2932 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2935 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2936 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2939 /* bright enough? */
2940 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2941 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2942 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2943 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2951 /* add to average? */
2952 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2953 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2954 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2955 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2957 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2959 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2960 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2965 /* another happy customer */
2966 numVertsIlluminated++;
2969 /* set average color */
2971 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2972 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2976 VectorCopy( ambientColor, avgColors[ 0 ] );
2979 /* clean up and store vertex color */
2980 for ( i = 0; i < ds->numVerts; i++ )
2982 /* get vertex luxel */
2983 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2985 /* store average in occluded vertexes */
2986 if ( radVertLuxel[ 0 ] < 0.0f ) {
2987 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2989 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2990 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2993 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2998 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3001 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3002 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3005 if ( bouncing || bounce == 0 || !bounceOnly ) {
3006 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3008 if ( !info->si->noVertexLight ) {
3009 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3014 /* free light list */
3015 FreeTraceLights( &trace );
3017 /* return to sender */
3021 /* -----------------------------------------------------------------
3022 reconstitute vertex lighting from the luxels
3023 ----------------------------------------------------------------- */
3025 /* set styles from lightmap */
3026 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3027 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3029 /* get max search radius */
3031 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3033 /* walk the surface verts */
3034 verts = yDrawVerts + ds->firstVert;
3035 for ( i = 0; i < ds->numVerts; i++ )
3037 /* do each lightmap */
3038 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3041 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3045 /* get luxel coords */
3046 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3047 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3051 else if ( x >= lm->sw ) {
3057 else if ( y >= lm->sh ) {
3061 /* get vertex luxels */
3062 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3063 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3065 /* color the luxel with the normal? */
3067 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3068 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3069 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3072 /* color the luxel with surface num? */
3073 else if ( debugSurfaces ) {
3074 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3077 else if ( info->si->noVertexLight ) {
3078 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
3081 else if ( noVertexLighting > 0 ) {
3082 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
3085 /* divine color from the superluxels */
3088 /* increasing radius */
3089 VectorClear( radVertLuxel );
3091 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3093 /* sample within radius */
3094 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3096 if ( sy < 0 || sy >= lm->sh ) {
3100 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3102 if ( sx < 0 || sx >= lm->sw ) {
3106 /* get luxel particulars */
3107 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3108 cluster = SUPER_CLUSTER( sx, sy );
3109 if ( *cluster < 0 ) {
3113 /* testing: must be brigher than ambient color */
3114 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3117 /* add its distinctiveness to our own */
3118 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3119 samples += luxel[ 3 ];
3125 if ( samples > 0.0f ) {
3126 VectorDivide( radVertLuxel, samples, radVertLuxel );
3129 VectorCopy( ambientColor, radVertLuxel );
3133 /* store into floating point storage */
3134 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3135 numVertsIlluminated++;
3137 /* store into bytes (for vertex approximation) */
3138 if ( !info->si->noVertexLight ) {
3139 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3147 /* -------------------------------------------------------------------------------
3149 light optimization (-fast)
3151 creates a list of lights that will affect a surface and stores it in tw
3152 this is to optimize surface lighting by culling out as many of the
3153 lights in the world as possible from further calculation
3155 ------------------------------------------------------------------------------- */
3159 determines opaque brushes in the world and find sky shaders for sunlight calculations
3162 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3164 unsigned int compileFlags, allCompileFlags;
3166 bspBrushSide_t *side;
3167 bspShader_t *shader;
3172 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3175 if ( opaqueBrushes == NULL ) {
3176 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3180 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3181 numOpaqueBrushes = 0;
3183 /* walk the list of worldspawn brushes */
3184 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3187 b = bspModels[ 0 ].firstBSPBrush + i;
3188 brush = &bspBrushes[ b ];
3190 /* check all sides */
3192 allCompileFlags = ~( 0u );
3193 for ( j = 0; j < brush->numSides; j++ )
3195 /* do bsp shader calculations */
3196 side = &bspBrushSides[ brush->firstSide + j ];
3197 shader = &bspShaders[ side->shaderNum ];
3199 /* get shader info */
3200 si = ShaderInfoForShaderNull( shader->shader );
3205 /* or together compile flags */
3206 compileFlags |= si->compileFlags;
3207 allCompileFlags &= si->compileFlags;
3210 /* determine if this brush is opaque to light */
3211 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3212 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3218 /* emit some statistics */
3219 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3221 void SetupBrushes( void ){
3222 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3229 determines if two clusters are visible to each other using the PVS
3232 qboolean ClusterVisible( int a, int b ){
3238 if ( a < 0 || b < 0 ) {
3248 if ( numBSPVisBytes <= 8 ) {
3253 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3254 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3255 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3258 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3268 borrowed from vlight.c
3271 int PointInLeafNum_r( vec3_t point, int nodenum ){
3278 while ( nodenum >= 0 )
3280 node = &bspNodes[ nodenum ];
3281 plane = &bspPlanes[ node->planeNum ];
3282 dist = DotProduct( point, plane->normal ) - plane->dist;
3284 nodenum = node->children[ 0 ];
3286 else if ( dist < -0.1 ) {
3287 nodenum = node->children[ 1 ];
3291 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3292 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3295 nodenum = node->children[ 1 ];
3299 leafnum = -nodenum - 1;
3307 borrowed from vlight.c
3310 int PointInLeafNum( vec3_t point ){
3311 return PointInLeafNum_r( point, 0 );
3317 ClusterVisibleToPoint() - ydnar
3318 returns qtrue if point can "see" cluster
3321 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3325 /* get leafNum for point */
3326 pointCluster = ClusterForPoint( point );
3327 if ( pointCluster < 0 ) {
3332 return ClusterVisible( pointCluster, cluster );
3338 ClusterForPoint() - ydnar
3339 returns the pvs cluster for point
3342 int ClusterForPoint( vec3_t point ){
3346 /* get leafNum for point */
3347 leafNum = PointInLeafNum( point );
3348 if ( leafNum < 0 ) {
3352 /* return the cluster */
3353 return bspLeafs[ leafNum ].cluster;
3359 ClusterForPointExt() - ydnar
3360 also takes brushes into account for occlusion testing
3363 int ClusterForPointExt( vec3_t point, float epsilon ){
3364 int i, j, b, leafNum, cluster;
3367 int *brushes, numBSPBrushes;
3373 /* get leaf for point */
3374 leafNum = PointInLeafNum( point );
3375 if ( leafNum < 0 ) {
3378 leaf = &bspLeafs[ leafNum ];
3380 /* get the cluster */
3381 cluster = leaf->cluster;
3382 if ( cluster < 0 ) {
3386 /* transparent leaf, so check point against all brushes in the leaf */
3387 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3388 numBSPBrushes = leaf->numBSPLeafBrushes;
3389 for ( i = 0; i < numBSPBrushes; i++ )
3393 if ( b > maxOpaqueBrush ) {
3396 brush = &bspBrushes[ b ];
3397 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3401 /* check point against all planes */
3403 for ( j = 0; j < brush->numSides && inside; j++ )
3405 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3406 dot = DotProduct( point, plane->normal );
3408 if ( dot > epsilon ) {
3413 /* if inside, return bogus cluster */
3419 /* if the point made it this far, it's not inside any opaque brushes */
3426 ClusterForPointExtFilter() - ydnar
3427 adds cluster checking against a list of known valid clusters
3430 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3434 /* get cluster for point */
3435 cluster = ClusterForPointExt( point, epsilon );
3437 /* check if filtering is necessary */
3438 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3443 for ( i = 0; i < numClusters; i++ )
3445 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3457 ShaderForPointInLeaf() - ydnar
3458 checks a point against all brushes in a leaf, returning the shader of the brush
3459 also sets the cumulative surface and content flags for the brush hit
3462 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3466 int *brushes, numBSPBrushes;
3469 bspBrushSide_t *side;
3471 bspShader_t *shader;
3472 int allSurfaceFlags, allContentFlags;
3475 /* clear things out first */
3480 if ( leafNum < 0 ) {
3483 leaf = &bspLeafs[ leafNum ];
3485 /* transparent leaf, so check point against all brushes in the leaf */
3486 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3487 numBSPBrushes = leaf->numBSPLeafBrushes;
3488 for ( i = 0; i < numBSPBrushes; i++ )
3491 brush = &bspBrushes[ brushes[ i ] ];
3493 /* check point against all planes */
3495 allSurfaceFlags = 0;
3496 allContentFlags = 0;
3497 for ( j = 0; j < brush->numSides && inside; j++ )
3499 side = &bspBrushSides[ brush->firstSide + j ];
3500 plane = &bspPlanes[ side->planeNum ];
3501 dot = DotProduct( point, plane->normal );
3503 if ( dot > epsilon ) {
3508 shader = &bspShaders[ side->shaderNum ];
3509 allSurfaceFlags |= shader->surfaceFlags;
3510 allContentFlags |= shader->contentFlags;
3514 /* handle if inside */
3516 /* if there are desired flags, check for same and continue if they aren't matched */
3517 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3520 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3524 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3525 *surfaceFlags = allSurfaceFlags;
3526 *contentFlags = allContentFlags;
3527 return brush->shaderNum;
3531 /* if the point made it this far, it's not inside any brushes */
3539 chops a bounding box by the plane defined by origin and normal
3540 returns qfalse if the bounds is entirely clipped away
3542 this is not exactly the fastest way to do this...
3545 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3546 /* FIXME: rewrite this so it doesn't use bloody brushes */
3554 calculates each light's effective envelope,
3555 taking into account brightness, type, and pvs.
3558 #define LIGHT_EPSILON 0.125f
3559 #define LIGHT_NUDGE 2.0f
3561 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3562 int i, x, y, z, x1, y1, z1;
3563 light_t *light, *light2, **owner;
3565 vec3_t origin, dir, mins, maxs;
3566 float radius, intensity;
3567 light_t *buckets[ 256 ];
3570 /* early out for weird cases where there are no lights */
3571 if ( lights == NULL ) {
3576 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3580 numCulledLights = 0;
3582 while ( *owner != NULL )
3587 /* handle negative lights */
3588 if ( light->photons < 0.0f || light->add < 0.0f ) {
3589 light->photons *= -1.0f;
3590 light->add *= -1.0f;
3591 light->flags |= LIGHT_NEGATIVE;
3595 if ( light->type == EMIT_SUN ) {
3598 light->envelope = MAX_WORLD_COORD * 8.0f;
3599 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3600 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3603 /* everything else */
3606 /* get pvs cluster for light */
3607 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3609 /* invalid cluster? */
3610 if ( light->cluster < 0 ) {
3611 /* nudge the sample point around a bit */
3612 for ( x = 0; x < 4; x++ )
3614 /* two's complement 0, 1, -1, 2, -2, etc */
3615 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3617 for ( y = 0; y < 4; y++ )
3619 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3621 for ( z = 0; z < 4; z++ )
3623 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3626 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3627 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3628 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3630 /* try at nudged origin */
3631 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3632 if ( light->cluster < 0 ) {
3637 VectorCopy( origin, light->origin );
3643 /* only calculate for lights in pvs and outside of opaque brushes */
3644 if ( light->cluster >= 0 ) {
3645 /* set light fast flag */
3647 light->flags |= LIGHT_FAST_TEMP;
3650 light->flags &= ~LIGHT_FAST_TEMP;
3652 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3653 light->flags |= LIGHT_FAST_TEMP;
3655 if ( light->si && light->si->noFast ) {
3656 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3659 /* clear light envelope */
3660 light->envelope = 0;
3662 /* handle area lights */
3663 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3664 light->envelope = MAX_WORLD_COORD * 8.0f;
3666 /* check for fast mode */
3667 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3668 /* ugly hack to calculate extent for area lights, but only done once */
3669 VectorScale( light->normal, -1.0f, dir );
3670 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3674 VectorMA( light->origin, radius, light->normal, origin );
3675 factor = PointToPolygonFormFactor( origin, dir, light->w );
3676 if ( factor < 0.0f ) {
3679 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3680 light->envelope = radius;
3686 intensity = light->photons; /* hopefully not used */
3691 intensity = light->photons;
3695 if ( light->envelope <= 0.0f ) {
3696 /* solve distance for non-distance lights */
3697 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3698 light->envelope = MAX_WORLD_COORD * 8.0f;
3701 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3702 /* solve distance for linear lights */
3703 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3704 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3708 add = angle * light->photons * linearScale - (dist * light->fade);
3709 T = (light->photons * linearScale) - (dist * light->fade);
3710 T + (dist * light->fade) = (light->photons * linearScale);
3711 dist * light->fade = (light->photons * linearScale) - T;
3712 dist = ((light->photons * linearScale) - T) / light->fade;
3715 /* solve for inverse square falloff */
3717 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3721 add = light->photons / (dist * dist);
3722 T = light->photons / (dist * dist);
3723 T * (dist * dist) = light->photons;
3724 dist = sqrt( light->photons / T );
3729 /* solve distance for linear lights */
3730 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3731 light->envelope = ( intensity * linearScale ) / light->fade;
3734 /* can't cull these */
3736 light->envelope = MAX_WORLD_COORD * 8.0f;
3741 /* chop radius against pvs */
3744 ClearBounds( mins, maxs );
3746 /* check all leaves */
3747 for ( i = 0; i < numBSPLeafs; i++ )
3750 leaf = &bspLeafs[ i ];
3753 if ( leaf->cluster < 0 ) {
3756 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3760 /* add this leafs bbox to the bounds */
3761 VectorCopy( leaf->mins, origin );
3762 AddPointToBounds( origin, mins, maxs );
3763 VectorCopy( leaf->maxs, origin );
3764 AddPointToBounds( origin, mins, maxs );
3767 /* test to see if bounds encompass light */
3768 for ( i = 0; i < 3; i++ )
3770 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3771 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3772 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3773 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3774 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3775 AddPointToBounds( light->origin, mins, maxs );
3779 /* chop the bounds by a plane for area lights and spotlights */
3780 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3781 ChopBounds( mins, maxs, light->origin, light->normal );
3785 VectorCopy( mins, light->mins );
3786 VectorCopy( maxs, light->maxs );
3788 /* reflect bounds around light origin */
3789 //% VectorMA( light->origin, -1.0f, origin, origin );
3790 VectorScale( light->origin, 2, origin );
3791 VectorSubtract( origin, maxs, origin );
3792 AddPointToBounds( origin, mins, maxs );
3793 //% VectorMA( light->origin, -1.0f, mins, origin );
3794 VectorScale( light->origin, 2, origin );
3795 VectorSubtract( origin, mins, origin );
3796 AddPointToBounds( origin, mins, maxs );
3798 /* calculate spherical bounds */
3799 VectorSubtract( maxs, light->origin, dir );
3800 radius = (float) VectorLength( dir );
3802 /* if this radius is smaller than the envelope, then set the envelope to it */
3803 if ( radius < light->envelope ) {
3804 light->envelope = radius;
3805 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3808 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3811 /* add grid/surface only check */
3813 if ( !( light->flags & LIGHT_GRID ) ) {
3814 light->envelope = 0.0f;
3819 if ( !( light->flags & LIGHT_SURFACES ) ) {
3820 light->envelope = 0.0f;
3826 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3828 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3830 /* delete the light */
3832 *owner = light->next;
3833 if ( light->w != NULL ) {
3841 /* square envelope */
3842 light->envelope2 = ( light->envelope * light->envelope );
3844 /* increment light count */
3847 /* set next light */
3848 owner = &( ( **owner ).next );
3851 /* bucket sort lights by style */
3852 memset( buckets, 0, sizeof( buckets ) );
3854 for ( light = lights; light != NULL; light = light2 )
3856 /* get next light */
3857 light2 = light->next;
3859 /* filter into correct bucket */
3860 light->next = buckets[ light->style ];
3861 buckets[ light->style ] = light;
3863 /* if any styled light is present, automatically set nocollapse */
3864 if ( light->style != LS_NORMAL ) {
3869 /* filter back into light list */
3871 for ( i = 255; i >= 0; i-- )
3874 for ( light = buckets[ i ]; light != NULL; light = light2 )
3876 light2 = light->next;
3877 light->next = lights;
3882 /* emit some statistics */
3883 Sys_Printf( "%9d total lights\n", numLights );
3884 Sys_Printf( "%9d culled lights\n", numCulledLights );
3890 CreateTraceLightsForBounds()
3891 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3894 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3897 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3898 float radius, dist, length;
3901 /* potential pre-setup */
3902 if ( numLights == 0 ) {
3903 SetupEnvelopes( qfalse, fast );
3907 //% 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 ] );
3909 /* allocate the light list */
3910 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3911 trace->numLights = 0;
3913 /* calculate spherical bounds */
3914 VectorAdd( mins, maxs, origin );
3915 VectorScale( origin, 0.5f, origin );
3916 VectorSubtract( maxs, origin, dir );
3917 radius = (float) VectorLength( dir );
3919 /* get length of normal vector */
3920 if ( normal != NULL ) {
3921 length = VectorLength( normal );
3925 normal = nullVector;
3929 /* test each light and see if it reaches the sphere */
3930 /* note: the attenuation code MUST match LightingAtSample() */
3931 for ( light = lights; light; light = light->next )
3933 /* check zero sized envelope */
3934 if ( light->envelope <= 0 ) {
3935 lightsEnvelopeCulled++;
3940 if ( !( light->flags & flags ) ) {
3944 /* sunlight skips all this nonsense */
3945 if ( light->type != EMIT_SUN ) {
3951 /* check against pvs cluster */
3952 if ( numClusters > 0 && clusters != NULL ) {
3953 for ( i = 0; i < numClusters; i++ )
3955 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3961 if ( i == numClusters ) {
3962 lightsClusterCulled++;
3967 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3968 VectorSubtract( light->origin, origin, dir );
3969 dist = VectorLength( dir );
3970 dist -= light->envelope;
3973 lightsEnvelopeCulled++;
3977 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3980 for ( i = 0; i < 3; i++ )
3982 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3987 lightsBoundsCulled++;
3993 /* planar surfaces (except twosided surfaces) have a couple more checks */
3994 if ( length > 0.0f && trace->twoSided == qfalse ) {
3995 /* lights coplanar with a surface won't light it */
3996 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
3997 lightsPlaneCulled++;
4001 /* check to see if light is behind the plane */
4002 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
4003 lightsPlaneCulled++;
4008 /* add this light */
4009 trace->lights[ trace->numLights++ ] = light;
4012 /* make last night null */
4013 trace->lights[ trace->numLights ] = NULL;
4018 void FreeTraceLights( trace_t *trace ){
4019 if ( trace->lights != NULL ) {
4020 free( trace->lights );
4027 CreateTraceLightsForSurface()
4028 creates a list of lights that can potentially affect a drawsurface
4031 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4033 vec3_t mins, maxs, normal;
4035 bspDrawSurface_t *ds;
4036 surfaceInfo_t *info;
4044 /* get drawsurface and info */
4045 ds = &bspDrawSurfaces[ num ];
4046 info = &surfaceInfos[ num ];
4048 /* get the mins/maxs for the dsurf */
4049 ClearBounds( mins, maxs );
4050 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4051 for ( i = 0; i < ds->numVerts; i++ )
4053 dv = &yDrawVerts[ ds->firstVert + i ];
4054 AddPointToBounds( dv->xyz, mins, maxs );
4055 if ( !VectorCompare( dv->normal, normal ) ) {
4056 VectorClear( normal );
4060 /* create the lights for the bounding box */
4061 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4064 /////////////////////////////////////////////////////////////
4066 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4067 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4068 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4069 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4071 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4072 static int numFloodVectors = 0;
4074 void SetupFloodLight( void ){
4076 float angle, elevation, angleStep, elevationStep;
4078 double v1,v2,v3,v4,v5,v6;
4081 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4083 /* calculate angular steps */
4084 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4085 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4089 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4091 /* iterate elevation */
4092 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4094 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4095 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4096 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4101 /* emit some statistics */
4102 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4105 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4107 if ( value[ 0 ] != '\0' ) {
4109 v4 = floodlightDistance;
4110 v5 = floodlightIntensity;
4111 v6 = floodlightDirectionScale;
4113 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4115 floodlightRGB[0] = v1;
4116 floodlightRGB[1] = v2;
4117 floodlightRGB[2] = v3;
4119 if ( VectorLength( floodlightRGB ) == 0 ) {
4120 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4133 floodlightDistance = v4;
4134 floodlightIntensity = v5;
4135 floodlightDirectionScale = v6;
4137 floodlighty = qtrue;
4138 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4142 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4145 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4146 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4147 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4149 ColorNormalize( floodlightRGB,floodlightRGB );
4153 FloodLightForSample()
4154 calculates floodlight value for a given sample
4155 once again, kudos to the dirtmapping coder
4158 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4163 float gatherLight, outLight;
4164 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4172 if ( trace == NULL || trace->cluster < 0 ) {
4178 dd = floodLightDistance;
4179 VectorCopy( trace->normal, normal );
4181 /* check if the normal is aligned to the world-up */
4182 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4183 if ( normal[ 2 ] == 1.0f ) {
4184 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4185 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4187 else if ( normal[ 2 ] == -1.0f ) {
4188 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4189 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4194 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4195 CrossProduct( normal, worldUp, myRt );
4196 VectorNormalize( myRt, myRt );
4197 CrossProduct( myRt, normal, myUp );
4198 VectorNormalize( myUp, myUp );
4201 /* vortex: optimise floodLightLowQuality a bit */
4202 if ( floodLightLowQuality == qtrue ) {
4203 /* iterate through ordered vectors */
4204 for ( i = 0; i < numFloodVectors; i++ )
4205 if ( rand() % 10 != 0 ) {
4211 /* iterate through ordered vectors */
4212 for ( i = 0; i < numFloodVectors; i++ )
4216 /* transform vector into tangent space */
4217 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4218 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4219 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4222 VectorMA( trace->origin, dd, direction, trace->end );
4224 //VectorMA( trace->origin, 1, direction, trace->origin );
4226 SetupTrace( trace );
4227 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4232 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4233 contribution = 1.0f;
4235 else if ( trace->opaque ) {
4236 VectorSubtract( trace->hit, trace->origin, displacement );
4237 d = VectorLength( displacement );
4239 // d=trace->distance;
4240 //if (d>256) gatherDirt+=1;
4241 contribution = d / dd;
4242 if ( contribution > 1 ) {
4243 contribution = 1.0f;
4246 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4249 gatherLight += contribution;
4254 if ( gatherLight <= 0.0f ) {
4263 gatherLight /= ( sub );
4265 outLight = gatherLight;
4266 if ( outLight > 1.0f ) {
4270 /* return to sender */
4275 FloodLightRawLightmap
4276 lighttracer style ambient occlusion light hack.
4277 Kudos to the dirtmapping author for most of this source.
4278 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4279 VorteX: fixed problems with deluxemapping
4282 // floodlight pass on a lightmap
4283 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4284 int i, x, y, *cluster;
4285 float *origin, *normal, *floodlight, floodLightAmount;
4286 surfaceInfo_t *info;
4289 // float samples, average, *floodlight2;
4291 memset( &trace,0,sizeof( trace_t ) );
4294 trace.testOcclusion = qtrue;
4295 trace.forceSunlight = qfalse;
4296 trace.twoSided = qtrue;
4297 trace.recvShadows = lm->recvShadows;
4298 trace.numSurfaces = lm->numLightSurfaces;
4299 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4300 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4301 trace.testAll = qfalse;
4302 trace.distance = 1024;
4304 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4305 //trace.twoSided = qfalse;
4306 for ( i = 0; i < trace.numSurfaces; i++ )
4309 info = &surfaceInfos[ trace.surfaces[ i ] ];
4311 /* check twosidedness */
4312 if ( info->si->twoSided ) {
4313 trace.twoSided = qtrue;
4318 /* gather floodlight */
4319 for ( y = 0; y < lm->sh; y++ )
4321 for ( x = 0; x < lm->sw; x++ )
4324 cluster = SUPER_CLUSTER( x, y );
4325 origin = SUPER_ORIGIN( x, y );
4326 normal = SUPER_NORMAL( x, y );
4327 floodlight = SUPER_FLOODLIGHT( x, y );
4329 /* set default dirt */
4332 /* only look at mapped luxels */
4333 if ( *cluster < 0 ) {
4338 trace.cluster = *cluster;
4339 VectorCopy( origin, trace.origin );
4340 VectorCopy( normal, trace.normal );
4342 /* get floodlight */
4343 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4345 /* add floodlight */
4346 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4347 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4348 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4349 floodlight[3] += floodlightDirectionScale;
4353 /* testing no filtering */
4359 for ( y = 0; y < lm->sh; y++ )
4361 for ( x = 0; x < lm->sw; x++ )
4364 cluster = SUPER_CLUSTER( x, y );
4365 floodlight = SUPER_FLOODLIGHT( x, y );
4367 /* filter dirt by adjacency to unmapped luxels */
4368 average = *floodlight;
4370 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4372 if ( sy < 0 || sy >= lm->sh ) {
4376 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4378 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4382 /* get neighboring luxel */
4383 cluster = SUPER_CLUSTER( sx, sy );
4384 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4385 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4390 average += *floodlight2;
4395 if ( samples <= 0.0f ) {
4401 if ( samples <= 0.0f ) {
4406 *floodlight = average / samples;
4412 void FloodLightRawLightmap( int rawLightmapNum ){
4415 /* bail if this number exceeds the number of raw lightmaps */
4416 if ( rawLightmapNum >= numRawLightmaps ) {
4420 lm = &rawLightmaps[ rawLightmapNum ];
4423 if ( floodlighty && floodlightIntensity ) {
4424 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4428 if ( lm->floodlightIntensity ) {
4429 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4430 numSurfacesFloodlighten += 1;
4434 void FloodlightRawLightmaps(){
4435 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4436 numSurfacesFloodlighten = 0;
4437 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4438 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4442 FloodLightIlluminate()
4443 illuminate floodlight into lightmap luxels
4446 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4447 float *luxel, *floodlight, *deluxel, *normal;
4450 int x, y, lightmapNum;
4452 /* walk lightmaps */
4453 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4456 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4460 /* apply floodlight to each luxel */
4461 for ( y = 0; y < lm->sh; y++ )
4463 for ( x = 0; x < lm->sw; x++ )
4465 /* get floodlight */
4466 floodlight = SUPER_FLOODLIGHT( x, y );
4467 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4472 cluster = SUPER_CLUSTER( x, y );
4474 /* only process mapped luxels */
4475 if ( *cluster < 0 ) {
4479 /* get particulars */
4480 luxel = SUPER_LUXEL( lightmapNum, x, y );
4481 deluxel = SUPER_DELUXEL( x, y );
4483 /* add to lightmap */
4484 luxel[0] += floodlight[0];
4485 luxel[1] += floodlight[1];
4486 luxel[2] += floodlight[2];
4488 if ( luxel[3] == 0 ) {
4492 /* add to deluxemap */
4493 if ( deluxemap && floodlight[3] > 0 ) {
4496 normal = SUPER_NORMAL( x, y );
4497 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4499 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4500 if ( brightness < 0.00390625f ) {
4501 brightness = 0.00390625f;
4504 VectorScale( normal, brightness, lightvector );
4505 VectorAdd( deluxel, lightvector, deluxel );