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 #define Image_sRGBFloatFromLinear(c) (((c) < 0.8014848f) ? (c) * 0.05046875f : 1.055f * (float)pow((c)*(1.0f/256.0f), 1.0f/2.4f) - 0.055f)
48 void ColorToBytes( const float *color, byte *colorBytes, float scale )
56 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
60 /* make a local copy */
61 VectorScale( color, scale, sample );
64 gamma = 1.0f / lightmapGamma;
65 for( i = 0; i < 3; i++ )
67 /* handle negative light */
68 if( sample[ i ] < 0.0f )
75 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
78 if (lightmapExposure == 1)
80 /* clamp with color normalization */
82 if( sample[ 1 ] > max )
84 if( sample[ 2 ] > max )
87 VectorScale( sample, (255.0f / max), sample );
91 if (lightmapExposure==0)
93 lightmapExposure=1.0f;
95 inv=1.f/lightmapExposure;
99 if( sample[ 1 ] > max )
101 if( sample[ 2 ] > max )
104 dif = (1- exp(-max * inv) ) * 255;
122 /* compensate for ingame overbrighting/bitshifting */
123 VectorScale( sample, (1.0f / lightmapCompensate), sample );
128 sample[0] = floor(Image_sRGBFloatFromLinear(sample[0]) * 255.0 + 0.5);
129 sample[1] = floor(Image_sRGBFloatFromLinear(sample[1]) * 255.0 + 0.5);
130 sample[2] = floor(Image_sRGBFloatFromLinear(sample[2]) * 255.0 + 0.5);
134 colorBytes[ 0 ] = sample[ 0 ];
135 colorBytes[ 1 ] = sample[ 1 ];
136 colorBytes[ 2 ] = sample[ 2 ];
141 /* -------------------------------------------------------------------------------
143 this section deals with phong shading (normal interpolation across brush faces)
145 ------------------------------------------------------------------------------- */
149 smooths together coincident vertex normals across the bsp
152 #define MAX_SAMPLES 256
153 #define THETA_EPSILON 0.000001
154 #define EQUAL_NORMAL_EPSILON 0.01
156 void SmoothNormals( void )
158 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
159 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
160 bspDrawSurface_t *ds;
164 vec3_t average, diff;
165 int indexes[ MAX_SAMPLES ];
166 vec3_t votes[ MAX_SAMPLES ];
169 /* allocate shade angle table */
170 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
171 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
173 /* allocate smoothed table */
174 cs = (numBSPDrawVerts / 8) + 1;
175 smoothed = safe_malloc( cs );
176 memset( smoothed, 0, cs );
178 /* set default shade angle */
179 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
182 /* run through every surface and flag verts belonging to non-lightmapped surfaces
183 and set per-vertex smoothing angle */
184 for( i = 0; i < numBSPDrawSurfaces; i++ )
187 ds = &bspDrawSurfaces[ i ];
189 /* get shader for shade angle */
190 si = surfaceInfos[ i ].si;
191 if( si->shadeAngleDegrees )
192 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
194 shadeAngle = defaultShadeAngle;
195 if( shadeAngle > maxShadeAngle )
196 maxShadeAngle = shadeAngle;
199 for( j = 0; j < ds->numVerts; j++ )
201 f = ds->firstVert + j;
202 shadeAngles[ f ] = shadeAngle;
203 if( ds->surfaceType == MST_TRIANGLE_SOUP )
204 smoothed[ f >> 3 ] |= (1 << (f & 7));
207 /* ydnar: optional force-to-trisoup */
208 if( trisoup && ds->surfaceType == MST_PLANAR )
210 ds->surfaceType = MST_TRIANGLE_SOUP;
211 ds->lightmapNum[ 0 ] = -3;
215 /* bail if no surfaces have a shade angle */
216 if( maxShadeAngle == 0 )
225 start = I_FloatTime();
227 /* go through the list of vertexes */
228 for( i = 0; i < numBSPDrawVerts; i++ )
231 f = 10 * i / numBSPDrawVerts;
235 Sys_Printf( "%i...", f );
238 /* already smoothed? */
239 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
243 VectorClear( average );
247 /* build a table of coincident vertexes */
248 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
250 /* already smoothed? */
251 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
255 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
258 /* use smallest shade angle */
259 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
261 /* check shade angle */
262 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
265 else if( dot < -1.0 )
267 testAngle = acos( dot ) + THETA_EPSILON;
268 if( testAngle >= shadeAngle )
270 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
273 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
275 /* add to the list */
276 indexes[ numVerts++ ] = j;
279 smoothed[ j >> 3 ] |= (1 << (j & 7));
281 /* see if this normal has already been voted */
282 for( k = 0; k < numVotes; k++ )
284 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
285 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
286 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
287 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
291 /* add a new vote? */
292 if( k == numVotes && numVotes < MAX_SAMPLES )
294 VectorAdd( average, bspDrawVerts[ j ].normal, average );
295 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
300 /* don't average for less than 2 verts */
305 if( VectorNormalize( average, average ) > 0 )
308 for( j = 0; j < numVerts; j++ )
309 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
313 /* free the tables */
318 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
323 /* -------------------------------------------------------------------------------
325 this section deals with phong shaded lightmap tracing
327 ------------------------------------------------------------------------------- */
329 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
333 calculates the st tangent vectors for normalmapping
336 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
343 /* calculate barycentric basis for the triangle */
344 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 ]);
345 if( fabs( bb ) < 0.00000001f )
349 for( i = 0; i < numVerts; i++ )
351 /* calculate s tangent vector */
352 s = dv[ i ]->st[ 0 ] + 10.0f;
353 t = dv[ i ]->st[ 1 ];
354 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
355 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
356 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
358 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
359 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
360 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
362 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
363 VectorNormalize( stv[ i ], stv[ i ] );
365 /* calculate t tangent vector */
366 s = dv[ i ]->st[ 0 ];
367 t = dv[ i ]->st[ 1 ] + 10.0f;
368 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
369 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
370 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
372 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
373 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
374 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
376 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
377 VectorNormalize( ttv[ i ], ttv[ i ] );
380 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
381 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
384 /* return to caller */
393 perterbs the normal by the shader's normalmap in tangent space
396 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
403 VectorCopy( dv->normal, pNormal );
405 /* sample normalmap */
406 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
409 /* remap sampled normal from [0,255] to [-1,-1] */
410 for( i = 0; i < 3; i++ )
411 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
413 /* scale tangent vectors and add to original normal */
414 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
415 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
416 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
418 /* renormalize and return */
419 VectorNormalize( pNormal, pNormal );
426 maps a luxel for triangle bv at
430 #define BOGUS_NUDGE -99999.0f
432 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 ] )
434 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
435 float *luxel, *origin, *normal, d, lightmapSampleOffset;
442 vec4_t sideplane, hostplane;
447 static float nudges[][ 2 ] =
449 //%{ 0, 0 }, /* try center first */
450 { -NUDGE, 0 }, /* left */
451 { NUDGE, 0 }, /* right */
452 { 0, NUDGE }, /* up */
453 { 0, -NUDGE }, /* down */
454 { -NUDGE, NUDGE }, /* left/up */
455 { NUDGE, -NUDGE }, /* right/down */
456 { NUDGE, NUDGE }, /* right/up */
457 { -NUDGE, -NUDGE }, /* left/down */
458 { BOGUS_NUDGE, BOGUS_NUDGE }
462 /* find luxel xy coords (fixme: subtract 0.5?) */
463 x = dv->lightmap[ 0 ][ 0 ];
464 y = dv->lightmap[ 0 ][ 1 ];
467 else if( x >= lm->sw )
471 else if( y >= lm->sh )
474 /* set shader and cluster list */
478 numClusters = info->numSurfaceClusters;
479 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
488 /* get luxel, origin, cluster, and normal */
489 luxel = SUPER_LUXEL( 0, x, y );
490 origin = SUPER_ORIGIN( x, y );
491 normal = SUPER_NORMAL( x, y );
492 cluster = SUPER_CLUSTER( x, y );
494 /* don't attempt to remap occluded luxels for planar surfaces */
495 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
498 /* only average the normal for premapped luxels */
499 else if( (*cluster) >= 0 )
501 /* do bumpmap calculations */
503 PerturbNormal( dv, si, pNormal, stv, ttv );
505 VectorCopy( dv->normal, pNormal );
507 /* add the additional normal data */
508 VectorAdd( normal, pNormal, normal );
513 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
517 /* axial lightmap projection */
518 if( lm->vecs != NULL )
520 /* calculate an origin for the sample from the lightmap vectors */
521 VectorCopy( lm->origin, origin );
522 for( i = 0; i < 3; i++ )
524 /* add unless it's the axis, which is taken care of later */
525 if( i == lm->axisNum )
527 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
530 /* project the origin onto the plane */
531 d = DotProduct( origin, plane ) - plane[ 3 ];
532 d /= plane[ lm->axisNum ];
533 origin[ lm->axisNum ] -= d;
536 /* non axial lightmap projection (explicit xyz) */
538 VectorCopy( dv->xyz, origin );
540 //////////////////////
541 //27's test to make sure samples stay within the triangle boundaries
542 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
543 //2) if it does, nudge it onto the correct side.
545 if (worldverts!=NULL && lightmapTriangleCheck)
549 VectorCopy(worldverts[j],cverts[j]);
551 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
557 //build plane using 2 edges and a normal
560 VectorCopy(cverts[next],temp);
561 VectorAdd(temp,hostplane,temp);
562 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
564 //planetest sample point
565 e=DotProduct(origin,sideplane);
570 //VectorClear(origin);
571 //Move the sample point back inside triangle bounds
572 origin[0]-=sideplane[0]*(e+1);
573 origin[1]-=sideplane[1]*(e+1);
574 origin[2]-=sideplane[2]*(e+1);
583 ////////////////////////
585 /* planar surfaces have precalculated lightmap vectors for nudging */
586 if( lm->plane != NULL )
588 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
589 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
590 VectorCopy( lm->plane, vecs[ 2 ] );
593 /* non-planar surfaces must calculate them */
597 VectorCopy( plane, vecs[ 2 ] );
599 VectorCopy( dv->normal, vecs[ 2 ] );
600 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
603 /* push the origin off the surface a bit */
605 lightmapSampleOffset = si->lightmapSampleOffset;
607 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
608 if( lm->axisNum < 0 )
609 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
610 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
611 origin[ lm->axisNum ] -= lightmapSampleOffset;
613 origin[ lm->axisNum ] += lightmapSampleOffset;
615 VectorCopy(origin,origintwo);
616 if(lightmapExtraVisClusterNudge)
618 origintwo[0]+=vecs[2][0];
619 origintwo[1]+=vecs[2][1];
620 origintwo[2]+=vecs[2][2];
624 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
626 /* another retarded hack, storing nudge count in luxel[ 1 ] */
629 /* point in solid? (except in dark mode) */
630 if( pointCluster < 0 && dark == qfalse )
632 /* nudge the the location around */
634 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
636 /* nudge the vector around a bit */
637 for( i = 0; i < 3; i++ )
639 /* set nudged point*/
640 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
644 /* get pvs cluster */
645 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
646 if( pointCluster >= 0 )
647 VectorCopy( nudged, origin );
652 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
653 if( pointCluster < 0 && si != NULL && dark == qfalse )
655 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
656 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
657 if( pointCluster >= 0 )
658 VectorCopy( nudged, origin );
663 if( pointCluster < 0 )
665 (*cluster) = CLUSTER_OCCLUDED;
666 VectorClear( origin );
667 VectorClear( normal );
673 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
675 /* do bumpmap calculations */
677 PerturbNormal( dv, si, pNormal, stv, ttv );
679 VectorCopy( dv->normal, pNormal );
681 /* store the cluster and normal */
682 (*cluster) = pointCluster;
683 VectorCopy( pNormal, normal );
685 /* store explicit mapping pass and implicit mapping pass */
700 recursively subdivides a triangle until its edges are shorter
701 than the distance between two luxels (thanks jc :)
704 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 ] )
706 bspDrawVert_t mid, *dv2[ 3 ];
710 /* map the vertexes */
712 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
713 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
714 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
720 float *a, *b, dx, dy, dist, maxDist;
723 /* find the longest edge and split it */
726 for( i = 0; i < 3; i++ )
729 a = dv[ i ]->lightmap[ 0 ];
730 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
733 dx = a[ 0 ] - b[ 0 ];
734 dy = a[ 1 ] - b[ 1 ];
735 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
745 /* try to early out */
746 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
750 /* split the longest edge and map it */
751 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
752 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
754 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
755 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
757 /* recurse to first triangle */
758 VectorCopy( dv, dv2 );
760 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
762 /* recurse to second triangle */
763 VectorCopy( dv, dv2 );
764 dv2[ (max + 1) % 3 ] = ∣
765 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
772 seed function for MapTriangle_r()
773 requires a cw ordered triangle
776 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
780 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
781 vec3_t worldverts[ 3 ];
784 /* get plane if possible */
785 if( lm->plane != NULL )
787 VectorCopy( lm->plane, plane );
788 plane[ 3 ] = lm->plane[ 3 ];
791 /* otherwise make one from the points */
792 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
795 /* check to see if we need to calculate texture->world tangent vectors */
796 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
807 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
808 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
809 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
811 /* map the vertexes */
812 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
813 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
814 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
816 /* 2002-11-20: prefer axial triangle edges */
819 /* subdivide the triangle */
820 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
824 for( i = 0; i < 3; i++ )
827 bspDrawVert_t *dv2[ 3 ];
831 a = dv[ i ]->lightmap[ 0 ];
832 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
834 /* make degenerate triangles for mapping edges */
835 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
838 dv2[ 1 ] = dv[ (i + 1) % 3 ];
839 dv2[ 2 ] = dv[ (i + 1) % 3 ];
841 /* map the degenerate triangle */
842 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
853 recursively subdivides a quad until its edges are shorter
854 than the distance between two luxels
857 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 ] )
859 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
866 float *a, *b, dx, dy, dist, maxDist;
869 /* find the longest edge and split it */
872 for( i = 0; i < 4; i++ )
875 a = dv[ i ]->lightmap[ 0 ];
876 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
879 dx = a[ 0 ] - b[ 0 ];
880 dy = a[ 1 ] - b[ 1 ];
881 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
891 /* try to early out */
892 if( max < 0 || maxDist <= subdivideThreshold )
896 /* we only care about even/odd edges */
899 /* split the longest edges */
900 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
901 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
903 /* map the vertexes */
904 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
905 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
910 /* recurse to first quad */
912 dv2[ 1 ] = &mid[ 0 ];
913 dv2[ 2 ] = &mid[ 1 ];
915 MapQuad_r( lm, info, dv2, plane, stv, ttv );
917 /* recurse to second quad */
918 dv2[ 0 ] = &mid[ 0 ];
921 dv2[ 3 ] = &mid[ 1 ];
922 MapQuad_r( lm, info, dv2, plane, stv, ttv );
928 /* recurse to first quad */
931 dv2[ 2 ] = &mid[ 0 ];
932 dv2[ 3 ] = &mid[ 1 ];
933 MapQuad_r( lm, info, dv2, plane, stv, ttv );
935 /* recurse to second quad */
936 dv2[ 0 ] = &mid[ 1 ];
937 dv2[ 1 ] = &mid[ 0 ];
940 MapQuad_r( lm, info, dv2, plane, stv, ttv );
948 seed function for MapQuad_r()
949 requires a cw ordered triangle quad
952 #define QUAD_PLANAR_EPSILON 0.5f
954 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
958 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
961 /* get plane if possible */
962 if( lm->plane != NULL )
964 VectorCopy( lm->plane, plane );
965 plane[ 3 ] = lm->plane[ 3 ];
968 /* otherwise make one from the points */
969 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
972 /* 4th point must fall on the plane */
973 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
974 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
977 /* check to see if we need to calculate texture->world tangent vectors */
978 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
989 /* map the vertexes */
990 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
991 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
992 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
993 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
995 /* subdivide the quad */
996 MapQuad_r( lm, info, dv, plane, stv, ttv );
1004 maps the locations, normals, and pvs clusters for a raw lightmap
1007 #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)
1009 void MapRawLightmap( int rawLightmapNum )
1011 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1012 float *luxel, *origin, *normal, samples, radius, pass;
1014 bspDrawSurface_t *ds;
1015 surfaceInfo_t *info;
1016 mesh_t src, *subdivided, *mesh;
1017 bspDrawVert_t *verts, *dv[ 4 ], fake;
1020 /* bail if this number exceeds the number of raw lightmaps */
1021 if( rawLightmapNum >= numRawLightmaps )
1025 lm = &rawLightmaps[ rawLightmapNum ];
1027 /* -----------------------------------------------------------------
1028 map referenced surfaces onto the raw lightmap
1029 ----------------------------------------------------------------- */
1031 /* walk the list of surfaces on this raw lightmap */
1032 for( n = 0; n < lm->numLightSurfaces; n++ )
1034 /* with > 1 surface per raw lightmap, clear occluded */
1037 for( y = 0; y < lm->sh; y++ )
1039 for( x = 0; x < lm->sw; x++ )
1042 cluster = SUPER_CLUSTER( x, y );
1044 *cluster = CLUSTER_UNMAPPED;
1050 num = lightSurfaces[ lm->firstLightSurface + n ];
1051 ds = &bspDrawSurfaces[ num ];
1052 info = &surfaceInfos[ num ];
1054 /* bail if no lightmap to calculate */
1055 if( info->lm != lm )
1061 /* map the surface onto the lightmap origin/cluster/normal buffers */
1062 switch( ds->surfaceType )
1066 verts = yDrawVerts + ds->firstVert;
1068 /* map the triangles */
1069 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1071 for( i = 0; i < ds->numIndexes; i += 3 )
1073 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1074 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1075 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1076 MapTriangle( lm, info, dv, mapNonAxial );
1082 /* make a mesh from the drawsurf */
1083 src.width = ds->patchWidth;
1084 src.height = ds->patchHeight;
1085 src.verts = &yDrawVerts[ ds->firstVert ];
1086 //% subdivided = SubdivideMesh( src, 8, 512 );
1087 subdivided = SubdivideMesh2( src, info->patchIterations );
1089 /* fit it to the curve and remove colinear verts on rows/columns */
1090 PutMeshOnCurve( *subdivided );
1091 mesh = RemoveLinearMeshColumnsRows( subdivided );
1092 FreeMesh( subdivided );
1095 verts = mesh->verts;
1101 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1102 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1103 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1104 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1108 /* map the mesh quads */
1111 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1113 for( y = 0; y < (mesh->height - 1); y++ )
1115 for( x = 0; x < (mesh->width - 1); x++ )
1118 pw[ 0 ] = x + (y * mesh->width);
1119 pw[ 1 ] = x + ((y + 1) * mesh->width);
1120 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1121 pw[ 3 ] = x + 1 + (y * mesh->width);
1122 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1127 /* get drawverts and map first triangle */
1128 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1129 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1130 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1131 MapTriangle( lm, info, dv, mapNonAxial );
1133 /* get drawverts and map second triangle */
1134 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1135 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1136 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1137 MapTriangle( lm, info, dv, mapNonAxial );
1144 for( y = 0; y < (mesh->height - 1); y++ )
1146 for( x = 0; x < (mesh->width - 1); x++ )
1149 pw[ 0 ] = x + (y * mesh->width);
1150 pw[ 1 ] = x + ((y + 1) * mesh->width);
1151 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1152 pw[ 3 ] = x + 1 + (y * mesh->width);
1158 /* attempt to map quad first */
1159 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1160 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1161 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1162 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1163 if( MapQuad( lm, info, dv ) )
1166 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1168 /* get drawverts and map first triangle */
1169 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1170 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1171 MapTriangle( lm, info, dv, mapNonAxial );
1173 /* get drawverts and map second triangle */
1174 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1175 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1176 MapTriangle( lm, info, dv, mapNonAxial );
1192 /* -----------------------------------------------------------------
1193 average and clean up luxel normals
1194 ----------------------------------------------------------------- */
1196 /* walk the luxels */
1197 for( y = 0; y < lm->sh; y++ )
1199 for( x = 0; x < lm->sw; x++ )
1202 luxel = SUPER_LUXEL( 0, x, y );
1203 normal = SUPER_NORMAL( x, y );
1204 cluster = SUPER_CLUSTER( x, y );
1206 /* only look at mapped luxels */
1210 /* the normal data could be the sum of multiple samples */
1211 if( luxel[ 3 ] > 1.0f )
1212 VectorNormalize( normal, normal );
1214 /* mark this luxel as having only one normal */
1219 /* non-planar surfaces stop here */
1220 if( lm->plane == NULL )
1223 /* -----------------------------------------------------------------
1224 map occluded or unuxed luxels
1225 ----------------------------------------------------------------- */
1227 /* walk the luxels */
1228 radius = floor( superSample / 2 );
1229 radius = radius > 0 ? radius : 1.0f;
1231 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1233 for( y = 0; y < lm->sh; y++ )
1235 for( x = 0; x < lm->sw; x++ )
1238 luxel = SUPER_LUXEL( 0, x, y );
1239 normal = SUPER_NORMAL( x, y );
1240 cluster = SUPER_CLUSTER( x, y );
1242 /* only look at unmapped luxels */
1243 if( *cluster != CLUSTER_UNMAPPED )
1246 /* divine a normal and origin from neighboring luxels */
1247 VectorClear( fake.xyz );
1248 VectorClear( fake.normal );
1249 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1250 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1252 for( sy = (y - 1); sy <= (y + 1); sy++ )
1254 if( sy < 0 || sy >= lm->sh )
1257 for( sx = (x - 1); sx <= (x + 1); sx++ )
1259 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1262 /* get neighboring luxel */
1263 luxel = SUPER_LUXEL( 0, sx, sy );
1264 origin = SUPER_ORIGIN( sx, sy );
1265 normal = SUPER_NORMAL( sx, sy );
1266 cluster = SUPER_CLUSTER( sx, sy );
1268 /* only consider luxels mapped in previous passes */
1269 if( *cluster < 0 || luxel[ 0 ] >= pass )
1272 /* add its distinctiveness to our own */
1273 VectorAdd( fake.xyz, origin, fake.xyz );
1274 VectorAdd( fake.normal, normal, fake.normal );
1275 samples += luxel[ 3 ];
1280 if( samples == 0.0f )
1284 VectorDivide( fake.xyz, samples, fake.xyz );
1285 //% VectorDivide( fake.normal, samples, fake.normal );
1286 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1289 /* map the fake vert */
1290 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1295 /* -----------------------------------------------------------------
1296 average and clean up luxel normals
1297 ----------------------------------------------------------------- */
1299 /* walk the luxels */
1300 for( y = 0; y < lm->sh; y++ )
1302 for( x = 0; x < lm->sw; x++ )
1305 luxel = SUPER_LUXEL( 0, x, y );
1306 normal = SUPER_NORMAL( x, y );
1307 cluster = SUPER_CLUSTER( x, y );
1309 /* only look at mapped luxels */
1313 /* the normal data could be the sum of multiple samples */
1314 if( luxel[ 3 ] > 1.0f )
1315 VectorNormalize( normal, normal );
1317 /* mark this luxel as having only one normal */
1325 for( y = 0; y < lm->sh; y++ )
1327 for( x = 0; x < lm->sw; x++ )
1332 cluster = SUPER_CLUSTER( x, y );
1333 origin = SUPER_ORIGIN( x, y );
1334 normal = SUPER_NORMAL( x, y );
1335 luxel = SUPER_LUXEL( x, y );
1340 /* check if within the bounding boxes of all surfaces referenced */
1341 ClearBounds( mins, maxs );
1342 for( n = 0; n < lm->numLightSurfaces; n++ )
1345 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1346 TOL = info->sampleSize + 2;
1347 AddPointToBounds( info->mins, mins, maxs );
1348 AddPointToBounds( info->maxs, mins, maxs );
1349 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1350 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1351 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1356 if( n < lm->numLightSurfaces )
1359 /* report bogus origin */
1360 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",
1361 rawLightmapNum, x, y, *cluster,
1362 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1363 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1364 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1375 sets up dirtmap (ambient occlusion)
1378 #define DIRT_CONE_ANGLE 88 /* degrees */
1379 #define DIRT_NUM_ANGLE_STEPS 16
1380 #define DIRT_NUM_ELEVATION_STEPS 3
1381 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1383 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1384 static int numDirtVectors = 0;
1386 void SetupDirt( void )
1389 float angle, elevation, angleStep, elevationStep;
1393 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1395 /* calculate angular steps */
1396 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1397 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1401 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1403 /* iterate elevation */
1404 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1406 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1407 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1408 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1413 /* emit some statistics */
1414 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1420 calculates dirt value for a given sample
1423 float DirtForSample( trace_t *trace )
1426 float gatherDirt, outDirt, angle, elevation, ooDepth;
1427 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1433 if( trace == NULL || trace->cluster < 0 )
1438 ooDepth = 1.0f / dirtDepth;
1439 VectorCopy( trace->normal, normal );
1441 /* check if the normal is aligned to the world-up */
1442 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1444 if( normal[ 2 ] == 1.0f )
1446 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1447 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1449 else if( normal[ 2 ] == -1.0f )
1451 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1452 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1457 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1458 CrossProduct( normal, worldUp, myRt );
1459 VectorNormalize( myRt, myRt );
1460 CrossProduct( myRt, normal, myUp );
1461 VectorNormalize( myUp, myUp );
1464 /* 1 = random mode, 0 (well everything else) = non-random mode */
1468 for( i = 0; i < numDirtVectors; i++ )
1470 /* get random vector */
1471 angle = Random() * DEG2RAD( 360.0f );
1472 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1473 temp[ 0 ] = cos( angle ) * sin( elevation );
1474 temp[ 1 ] = sin( angle ) * sin( elevation );
1475 temp[ 2 ] = cos( elevation );
1477 /* transform into tangent space */
1478 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1479 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1480 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1483 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1484 SetupTrace( trace );
1488 if( trace->opaque && !(trace->compileFlags & C_SKY) )
1490 VectorSubtract( trace->hit, trace->origin, displacement );
1491 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1497 /* iterate through ordered vectors */
1498 for( i = 0; i < numDirtVectors; i++ )
1500 /* transform vector into tangent space */
1501 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1502 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1503 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1506 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1507 SetupTrace( trace );
1513 VectorSubtract( trace->hit, trace->origin, displacement );
1514 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1520 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1521 SetupTrace( trace );
1527 VectorSubtract( trace->hit, trace->origin, displacement );
1528 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1532 if( gatherDirt <= 0.0f )
1535 /* apply gain (does this even do much? heh) */
1536 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1537 if( outDirt > 1.0f )
1541 outDirt *= dirtScale;
1542 if( outDirt > 1.0f )
1545 /* return to sender */
1546 return 1.0f - outDirt;
1553 calculates dirty fraction for each luxel
1556 void DirtyRawLightmap( int rawLightmapNum )
1558 int i, x, y, sx, sy, *cluster;
1559 float *origin, *normal, *dirt, *dirt2, average, samples;
1561 surfaceInfo_t *info;
1566 /* bail if this number exceeds the number of raw lightmaps */
1567 if( rawLightmapNum >= numRawLightmaps )
1571 lm = &rawLightmaps[ rawLightmapNum ];
1574 trace.testOcclusion = qtrue;
1575 trace.forceSunlight = qfalse;
1576 trace.recvShadows = lm->recvShadows;
1577 trace.numSurfaces = lm->numLightSurfaces;
1578 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1579 trace.inhibitRadius = 0.0f;
1580 trace.testAll = qfalse;
1582 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1583 trace.twoSided = qfalse;
1584 for( i = 0; i < trace.numSurfaces; i++ )
1587 info = &surfaceInfos[ trace.surfaces[ i ] ];
1589 /* check twosidedness */
1590 if( info->si->twoSided )
1592 trace.twoSided = qtrue;
1598 for( i = 0; i < trace.numSurfaces; i++ )
1601 info = &surfaceInfos[ trace.surfaces[ i ] ];
1603 /* check twosidedness */
1604 if( info->si->noDirty )
1612 for( y = 0; y < lm->sh; y++ )
1614 for( x = 0; x < lm->sw; x++ )
1617 cluster = SUPER_CLUSTER( x, y );
1618 origin = SUPER_ORIGIN( x, y );
1619 normal = SUPER_NORMAL( x, y );
1620 dirt = SUPER_DIRT( x, y );
1622 /* set default dirt */
1625 /* only look at mapped luxels */
1629 /* don't apply dirty on this surface */
1637 trace.cluster = *cluster;
1638 VectorCopy( origin, trace.origin );
1639 VectorCopy( normal, trace.normal );
1642 *dirt = DirtForSample( &trace );
1646 /* testing no filtering */
1650 for( y = 0; y < lm->sh; y++ )
1652 for( x = 0; x < lm->sw; x++ )
1655 cluster = SUPER_CLUSTER( x, y );
1656 dirt = SUPER_DIRT( x, y );
1658 /* filter dirt by adjacency to unmapped luxels */
1661 for( sy = (y - 1); sy <= (y + 1); sy++ )
1663 if( sy < 0 || sy >= lm->sh )
1666 for( sx = (x - 1); sx <= (x + 1); sx++ )
1668 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1671 /* get neighboring luxel */
1672 cluster = SUPER_CLUSTER( sx, sy );
1673 dirt2 = SUPER_DIRT( sx, sy );
1674 if( *cluster < 0 || *dirt2 <= 0.0f )
1683 if( samples <= 0.0f )
1688 if( samples <= 0.0f )
1692 *dirt = average / samples;
1701 calculates the pvs cluster, origin, normal of a sub-luxel
1704 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1706 int i, *cluster, *cluster2;
1707 float *origin, *origin2, *normal; //% , *normal2;
1708 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1711 /* calulate x vector */
1712 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1714 cluster = SUPER_CLUSTER( x, y );
1715 origin = SUPER_ORIGIN( x, y );
1716 //% normal = SUPER_NORMAL( x, y );
1717 cluster2 = SUPER_CLUSTER( x + 1, y );
1718 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1719 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1721 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1723 cluster = SUPER_CLUSTER( x - 1, y );
1724 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1725 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1726 cluster2 = SUPER_CLUSTER( x, y );
1727 origin2 = SUPER_ORIGIN( x, y );
1728 //% normal2 = SUPER_NORMAL( x, y );
1732 Error( "Spurious lightmap S vector\n" );
1735 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1736 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1738 /* calulate y vector */
1739 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1741 cluster = SUPER_CLUSTER( x, y );
1742 origin = SUPER_ORIGIN( x, y );
1743 //% normal = SUPER_NORMAL( x, y );
1744 cluster2 = SUPER_CLUSTER( x, y + 1 );
1745 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1746 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1748 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1750 cluster = SUPER_CLUSTER( x, y - 1 );
1751 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1752 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1753 cluster2 = SUPER_CLUSTER( x, y );
1754 origin2 = SUPER_ORIGIN( x, y );
1755 //% normal2 = SUPER_NORMAL( x, y );
1758 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1760 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1761 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1763 /* calculate new origin */
1764 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1765 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1766 for( i = 0; i < 3; i++ )
1767 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1770 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1771 if( *sampleCluster < 0 )
1774 /* calculate new normal */
1775 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1776 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1777 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1779 normal = SUPER_NORMAL( x, y );
1780 VectorCopy( normal, sampleNormal );
1788 SubsampleRawLuxel_r()
1789 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1792 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1794 int b, samples, mapped, lighted;
1797 vec3_t deluxel[ 3 ];
1798 vec3_t origin[ 4 ], normal[ 4 ];
1799 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1800 vec3_t color, direction = { 0, 0, 0 }, total;
1804 if( lightLuxel[ 3 ] >= lightSamples )
1808 VectorClear( total );
1812 /* make 2x2 subsample stamp */
1813 for( b = 0; b < 4; b++ )
1816 VectorCopy( sampleOrigin, origin[ b ] );
1818 /* calculate position */
1819 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1826 /* increment sample count */
1827 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1830 trace->cluster = *cluster;
1831 VectorCopy( origin[ b ], trace->origin );
1832 VectorCopy( normal[ b ], trace->normal );
1836 LightContributionToSample( trace );
1837 if(trace->forceSubsampling > 1.0f)
1839 /* alphashadow: we subsample as deep as we can */
1845 /* add to totals (fixme: make contrast function) */
1846 VectorCopy( trace->color, luxel[ b ] );
1849 VectorCopy( trace->directionContribution, deluxel[ b ] );
1851 VectorAdd( total, trace->color, total );
1852 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1856 /* subsample further? */
1857 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1858 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1859 lighted != 0 && lighted != mapped )
1861 for( b = 0; b < 4; b++ )
1863 if( cluster[ b ] < 0 )
1865 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1870 //% VectorClear( color );
1872 VectorCopy( lightLuxel, color );
1875 VectorCopy( lightDeluxel, direction );
1878 for( b = 0; b < 4; b++ )
1880 if( cluster[ b ] < 0 )
1882 VectorAdd( color, luxel[ b ], color );
1885 VectorAdd( direction, deluxel[ b ], direction );
1894 color[ 0 ] /= samples;
1895 color[ 1 ] /= samples;
1896 color[ 2 ] /= samples;
1899 VectorCopy( color, lightLuxel );
1900 lightLuxel[ 3 ] += 1.0f;
1904 direction[ 0 ] /= samples;
1905 direction[ 1 ] /= samples;
1906 direction[ 2 ] /= samples;
1907 VectorCopy( direction, lightDeluxel );
1912 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1913 static void GaussLikeRandom(float sigma, float *x, float *y)
1916 r = Random() * 2 * Q_PI;
1917 *x = sigma * 2.73861278752581783822 * cos(r);
1918 *y = sigma * 2.73861278752581783822 * sin(r);
1925 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1929 vec3_t origin, normal;
1930 vec3_t total, totaldirection;
1933 VectorClear( total );
1934 VectorClear( totaldirection );
1936 for(b = 0; b < lightSamples; ++b)
1939 VectorCopy( sampleOrigin, origin );
1940 GaussLikeRandom(bias, &dx, &dy);
1942 /* calculate position */
1943 if( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) )
1950 trace->cluster = cluster;
1951 VectorCopy( origin, trace->origin );
1952 VectorCopy( normal, trace->normal );
1954 LightContributionToSample( trace );
1955 VectorAdd( total, trace->color, total );
1958 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1966 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1967 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1968 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1972 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1973 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1974 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1982 IlluminateRawLightmap()
1983 illuminates the luxels
1986 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1987 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1988 #define LIGHT_DELUXEL( x, y ) (lightDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
1990 void IlluminateRawLightmap( int rawLightmapNum )
1992 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1993 int *cluster, *cluster2, mapped, lighted, totalLighted;
1994 size_t llSize, ldSize;
1996 surfaceInfo_t *info;
1997 qboolean filterColor, filterDir;
1999 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
2000 unsigned char *flag;
2001 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2002 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2003 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2005 float stackLightLuxels[ STACK_LL_SIZE ];
2008 /* bail if this number exceeds the number of raw lightmaps */
2009 if( rawLightmapNum >= numRawLightmaps )
2013 lm = &rawLightmaps[ rawLightmapNum ];
2016 trace.testOcclusion = !noTrace;
2017 trace.forceSunlight = qfalse;
2018 trace.recvShadows = lm->recvShadows;
2019 trace.numSurfaces = lm->numLightSurfaces;
2020 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2021 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2023 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2024 trace.twoSided = qfalse;
2025 for( i = 0; i < trace.numSurfaces; i++ )
2028 info = &surfaceInfos[ trace.surfaces[ i ] ];
2030 /* check twosidedness */
2031 if( info->si->twoSided )
2033 trace.twoSided = qtrue;
2038 /* create a culled light list for this raw lightmap */
2039 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2041 /* -----------------------------------------------------------------
2043 ----------------------------------------------------------------- */
2046 numLuxelsIlluminated += (lm->sw * lm->sh);
2048 /* test debugging state */
2049 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
2051 /* debug fill the luxels */
2052 for( y = 0; y < lm->sh; y++ )
2054 for( x = 0; x < lm->sw; x++ )
2057 cluster = SUPER_CLUSTER( x, y );
2059 /* only fill mapped luxels */
2063 /* get particulars */
2064 luxel = SUPER_LUXEL( 0, x, y );
2065 origin = SUPER_ORIGIN( x, y );
2066 normal = SUPER_NORMAL( x, y );
2068 /* color the luxel with raw lightmap num? */
2070 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2072 /* color the luxel with lightmap axis? */
2073 else if( debugAxis )
2075 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
2076 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
2077 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
2080 /* color the luxel with luxel cluster? */
2081 else if( debugCluster )
2082 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2084 /* color the luxel with luxel origin? */
2085 else if( debugOrigin )
2087 VectorSubtract( lm->maxs, lm->mins, temp );
2088 VectorScale( temp, (1.0f / 255.0f), temp );
2089 VectorSubtract( origin, lm->mins, temp2 );
2090 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2091 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2092 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2095 /* color the luxel with the normal */
2096 else if( normalmap )
2098 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
2099 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
2100 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
2103 /* otherwise clear it */
2105 VectorClear( luxel );
2114 /* allocate temporary per-light luxel storage */
2115 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2116 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2117 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2118 lightLuxels = stackLightLuxels;
2120 lightLuxels = safe_malloc( llSize );
2122 lightDeluxels = safe_malloc( ldSize );
2124 lightDeluxels = NULL;
2127 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2129 /* set ambient color */
2130 for( y = 0; y < lm->sh; y++ )
2132 for( x = 0; x < lm->sw; x++ )
2135 cluster = SUPER_CLUSTER( x, y );
2136 luxel = SUPER_LUXEL( 0, x, y );
2137 normal = SUPER_NORMAL( x, y );
2138 deluxel = SUPER_DELUXEL( x, y );
2140 /* blacken unmapped clusters */
2142 VectorClear( luxel );
2147 VectorCopy( ambientColor, luxel );
2150 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2152 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2153 if(brightness < 0.00390625f)
2154 brightness = 0.00390625f;
2156 VectorScale( normal, brightness, deluxel );
2163 /* clear styled lightmaps */
2164 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2165 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2167 if( lm->superLuxels[ lightmapNum ] != NULL )
2168 memset( lm->superLuxels[ lightmapNum ], 0, size );
2171 /* debugging code */
2172 //% if( trace.numLights <= 0 )
2173 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2175 /* walk light list */
2176 for( i = 0; i < trace.numLights; i++ )
2179 trace.light = trace.lights[ i ];
2182 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2184 if( lm->styles[ lightmapNum ] == trace.light->style ||
2185 lm->styles[ lightmapNum ] == LS_NONE )
2189 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2190 if( lightmapNum >= MAX_LIGHTMAPS )
2192 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2197 memset( lightLuxels, 0, llSize );
2199 memset( lightDeluxels, 0, ldSize );
2202 /* determine filter radius */
2203 filterRadius = lm->filterRadius > trace.light->filterRadius
2205 : trace.light->filterRadius;
2206 if( filterRadius < 0.0f )
2207 filterRadius = 0.0f;
2209 /* set luxel filter radius */
2210 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2211 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2212 luxelFilterRadius = 1;
2214 /* allocate sampling flags storage */
2215 if((lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2217 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2218 if(lm->superFlags == NULL)
2219 lm->superFlags = safe_malloc( size );
2220 memset( (void *) lm->superFlags, 0, size );
2223 /* initial pass, one sample per luxel */
2224 for( y = 0; y < lm->sh; y++ )
2226 for( x = 0; x < lm->sw; x++ )
2229 cluster = SUPER_CLUSTER( x, y );
2233 /* get particulars */
2234 lightLuxel = LIGHT_LUXEL( x, y );
2235 lightDeluxel = LIGHT_DELUXEL( x, y );
2236 origin = SUPER_ORIGIN( x, y );
2237 normal = SUPER_NORMAL( x, y );
2238 flag = SUPER_FLAG( x, y );
2241 ////////// 27's temp hack for testing edge clipping ////
2242 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2244 lightLuxel[ 1 ] = 255;
2245 lightLuxel[ 3 ] = 1.0f;
2251 /* set contribution count */
2252 lightLuxel[ 3 ] = 1.0f;
2255 trace.cluster = *cluster;
2256 VectorCopy( origin, trace.origin );
2257 VectorCopy( normal, trace.normal );
2259 /* get light for this sample */
2260 LightContributionToSample( &trace );
2261 VectorCopy( trace.color, lightLuxel );
2263 /* add the contribution to the deluxemap */
2266 VectorCopy( trace.directionContribution, lightDeluxel );
2269 /* check for evilness */
2270 if(trace.forceSubsampling > 1.0f && (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2273 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2276 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2282 /* don't even bother with everything else if nothing was lit */
2283 if( totalLighted == 0 )
2286 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2287 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2288 if( (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0 )
2291 for( y = 0; y < (lm->sh - 1); y++ )
2293 for( x = 0; x < (lm->sw - 1); x++ )
2298 VectorClear( total );
2300 /* test 2x2 stamp */
2301 for( t = 0; t < 4; t++ )
2303 /* set sample coords */
2304 sx = x + tests[ t ][ 0 ];
2305 sy = y + tests[ t ][ 1 ];
2308 cluster = SUPER_CLUSTER( sx, sy );
2314 flag = SUPER_FLAG( sx, sy );
2315 if(*flag & FLAG_FORCE_SUBSAMPLING)
2317 /* force a lighted/mapped discrepancy so we subsample */
2322 lightLuxel = LIGHT_LUXEL( sx, sy );
2323 VectorAdd( total, lightLuxel, total );
2324 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2328 /* if total color is under a certain amount, then don't bother subsampling */
2329 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2332 /* if all 4 pixels are either in shadow or light, then don't subsample */
2333 if( lighted != 0 && lighted != mapped )
2335 for( t = 0; t < 4; t++ )
2337 /* set sample coords */
2338 sx = x + tests[ t ][ 0 ];
2339 sy = y + tests[ t ][ 1 ];
2342 cluster = SUPER_CLUSTER( sx, sy );
2345 flag = SUPER_FLAG( sx, sy );
2346 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2348 lightLuxel = LIGHT_LUXEL( sx, sy );
2349 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2350 origin = SUPER_ORIGIN( sx, sy );
2352 /* only subsample shadowed luxels */
2353 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2357 if(lightRandomSamples)
2358 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2360 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2362 *flag |= FLAG_ALREADY_SUBSAMPLED;
2364 /* debug code to colorize subsampled areas to yellow */
2365 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2366 //% VectorSet( luxel, 255, 204, 0 );
2373 /* tertiary pass, apply dirt map (ambient occlusion) */
2377 for( y = 0; y < lm->sh; y++ )
2379 for( x = 0; x < lm->sw; x++ )
2382 cluster = SUPER_CLUSTER( x, y );
2386 /* get particulars */
2387 lightLuxel = LIGHT_LUXEL( x, y );
2388 dirt = SUPER_DIRT( x, y );
2390 /* scale light value */
2391 VectorScale( lightLuxel, *dirt, lightLuxel );
2396 /* allocate sampling lightmap storage */
2397 if( lm->superLuxels[ lightmapNum ] == NULL )
2399 /* allocate sampling lightmap storage */
2400 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2401 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2402 memset( lm->superLuxels[ lightmapNum ], 0, size );
2406 if( lightmapNum > 0 )
2408 lm->styles[ lightmapNum ] = trace.light->style;
2409 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2412 /* copy to permanent luxels */
2413 for( y = 0; y < lm->sh; y++ )
2415 for( x = 0; x < lm->sw; x++ )
2417 /* get cluster and origin */
2418 cluster = SUPER_CLUSTER( x, y );
2421 origin = SUPER_ORIGIN( x, y );
2424 if( luxelFilterRadius )
2427 VectorClear( averageColor );
2428 VectorClear( averageDir );
2431 /* cheaper distance-based filtering */
2432 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2434 if( sy < 0 || sy >= lm->sh )
2437 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2439 if( sx < 0 || sx >= lm->sw )
2442 /* get particulars */
2443 cluster = SUPER_CLUSTER( sx, sy );
2446 lightLuxel = LIGHT_LUXEL( sx, sy );
2447 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2450 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2451 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2453 /* scale luxel by filter weight */
2454 VectorScale( lightLuxel, weight, color );
2455 VectorAdd( averageColor, color, averageColor );
2458 VectorScale( lightDeluxel, weight, direction );
2459 VectorAdd( averageDir, direction, averageDir );
2466 if( samples <= 0.0f )
2469 /* scale into luxel */
2470 luxel = SUPER_LUXEL( lightmapNum, x, y );
2473 /* handle negative light */
2474 if( trace.light->flags & LIGHT_NEGATIVE )
2476 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2477 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2478 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2481 /* handle normal light */
2484 luxel[ 0 ] += averageColor[ 0 ] / samples;
2485 luxel[ 1 ] += averageColor[ 1 ] / samples;
2486 luxel[ 2 ] += averageColor[ 2 ] / samples;
2491 /* scale into luxel */
2492 deluxel = SUPER_DELUXEL( x, y );
2493 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2494 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2495 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2502 /* get particulars */
2503 lightLuxel = LIGHT_LUXEL( x, y );
2504 lightDeluxel = LIGHT_DELUXEL( x, y );
2505 luxel = SUPER_LUXEL( lightmapNum, x, y );
2506 deluxel = SUPER_DELUXEL( x, y );
2508 /* handle negative light */
2509 if( trace.light->flags & LIGHT_NEGATIVE )
2510 VectorScale( averageColor, -1.0f, averageColor );
2515 /* handle negative light */
2516 if( trace.light->flags & LIGHT_NEGATIVE )
2517 VectorSubtract( luxel, lightLuxel, luxel );
2519 /* handle normal light */
2521 VectorAdd( luxel, lightLuxel, luxel );
2525 VectorAdd( deluxel, lightDeluxel, deluxel );
2532 /* free temporary luxels */
2533 if( lightLuxels != stackLightLuxels )
2534 free( lightLuxels );
2537 free( lightDeluxels );
2540 /* free light list */
2541 FreeTraceLights( &trace );
2543 /* floodlight pass */
2545 FloodlightIlluminateLightmap(lm);
2549 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2552 if( lm->superLuxels[ lightmapNum ] == NULL )
2555 for( y = 0; y < lm->sh; y++ )
2557 for( x = 0; x < lm->sw; x++ )
2560 cluster = SUPER_CLUSTER( x, y );
2561 //% if( *cluster < 0 )
2564 /* get particulars */
2565 luxel = SUPER_LUXEL( lightmapNum, x, y );
2566 normal = SUPER_NORMAL ( x, y );
2568 luxel[0]=(normal[0]*127)+127;
2569 luxel[1]=(normal[1]*127)+127;
2570 luxel[2]=(normal[2]*127)+127;
2576 /* -----------------------------------------------------------------
2578 ----------------------------------------------------------------- */
2582 /* walk lightmaps */
2583 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2586 if( lm->superLuxels[ lightmapNum ] == NULL )
2589 /* apply dirt to each luxel */
2590 for( y = 0; y < lm->sh; y++ )
2592 for( x = 0; x < lm->sw; x++ )
2595 cluster = SUPER_CLUSTER( x, y );
2596 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2599 /* get particulars */
2600 luxel = SUPER_LUXEL( lightmapNum, x, y );
2601 dirt = SUPER_DIRT( x, y );
2604 VectorScale( luxel, *dirt, luxel );
2608 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2614 /* -----------------------------------------------------------------
2616 ----------------------------------------------------------------- */
2618 /* walk lightmaps */
2619 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2622 if( lm->superLuxels[ lightmapNum ] == NULL )
2625 /* average occluded luxels from neighbors */
2626 for( y = 0; y < lm->sh; y++ )
2628 for( x = 0; x < lm->sw; x++ )
2630 /* get particulars */
2631 cluster = SUPER_CLUSTER( x, y );
2632 luxel = SUPER_LUXEL( lightmapNum, x, y );
2633 deluxel = SUPER_DELUXEL( x, y );
2634 normal = SUPER_NORMAL( x, y );
2636 /* determine if filtering is necessary */
2637 filterColor = qfalse;
2640 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2641 filterColor = qtrue;
2643 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2646 if( !filterColor && !filterDir )
2649 /* choose seed amount */
2650 VectorClear( averageColor );
2651 VectorClear( averageDir );
2654 /* walk 3x3 matrix */
2655 for( sy = (y - 1); sy <= (y + 1); sy++ )
2657 if( sy < 0 || sy >= lm->sh )
2660 for( sx = (x - 1); sx <= (x + 1); sx++ )
2662 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2665 /* get neighbor's particulars */
2666 cluster2 = SUPER_CLUSTER( sx, sy );
2667 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2668 deluxel2 = SUPER_DELUXEL( sx, sy );
2670 /* ignore unmapped/unlit luxels */
2671 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2672 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2675 /* add its distinctiveness to our own */
2676 VectorAdd( averageColor, luxel2, averageColor );
2677 samples += luxel2[ 3 ];
2679 VectorAdd( averageDir, deluxel2, averageDir );
2684 if( samples <= 0.0f )
2687 /* dark lightmap seams */
2690 if( lightmapNum == 0 )
2691 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2698 VectorDivide( averageColor, samples, luxel );
2702 VectorDivide( averageDir, samples, deluxel );
2704 /* set cluster to -3 */
2706 *cluster = CLUSTER_FLOODED;
2714 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2717 if( lm->superLuxels[ lightmapNum ] == NULL )
2719 for( y = 0; y < lm->sh; y++ )
2720 for( x = 0; x < lm->sw; x++ )
2723 cluster = SUPER_CLUSTER( x, y );
2724 luxel = SUPER_LUXEL( lightmapNum, x, y );
2725 deluxel = SUPER_DELUXEL( x, y );
2726 if(!luxel || !deluxel || !cluster)
2728 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2731 else if(*cluster < 0)
2734 // should have neither deluxemap nor lightmap
2736 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2741 // should have both deluxemap and lightmap
2743 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2753 IlluminateVertexes()
2754 light the surface vertexes
2757 #define VERTEX_NUDGE 4.0f
2759 void IlluminateVertexes( int num )
2761 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2762 int lightmapNum, numAvg;
2763 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2764 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2765 bspDrawSurface_t *ds;
2766 surfaceInfo_t *info;
2768 bspDrawVert_t *verts;
2770 float floodLightAmount;
2774 /* get surface, info, and raw lightmap */
2775 ds = &bspDrawSurfaces[ num ];
2776 info = &surfaceInfos[ num ];
2779 /* -----------------------------------------------------------------
2780 illuminate the vertexes
2781 ----------------------------------------------------------------- */
2783 /* calculate vertex lighting for surfaces without lightmaps */
2784 if( lm == NULL || cpmaHack )
2787 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2788 trace.forceSunlight = info->si->forceSunlight;
2789 trace.recvShadows = info->recvShadows;
2790 trace.numSurfaces = 1;
2791 trace.surfaces = #
2792 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2794 /* twosided lighting */
2795 trace.twoSided = info->si->twoSided;
2797 /* make light list for this surface */
2798 CreateTraceLightsForSurface( num, &trace );
2801 verts = yDrawVerts + ds->firstVert;
2803 memset( avgColors, 0, sizeof( avgColors ) );
2805 /* walk the surface verts */
2806 for( i = 0; i < ds->numVerts; i++ )
2808 /* get vertex luxel */
2809 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2811 /* color the luxel with raw lightmap num? */
2813 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2815 /* color the luxel with luxel origin? */
2816 else if( debugOrigin )
2818 VectorSubtract( info->maxs, info->mins, temp );
2819 VectorScale( temp, (1.0f / 255.0f), temp );
2820 VectorSubtract( origin, lm->mins, temp2 );
2821 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2822 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2823 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2826 /* color the luxel with the normal */
2827 else if( normalmap )
2829 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2830 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2831 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2834 /* illuminate the vertex */
2837 /* clear vertex luxel */
2838 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2840 /* try at initial origin */
2841 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2842 if( trace.cluster >= 0 )
2845 VectorCopy( verts[ i ].xyz, trace.origin );
2846 VectorCopy( verts[ i ].normal, trace.normal );
2849 if( dirty && !bouncing )
2850 dirt = DirtForSample( &trace );
2854 /* jal: floodlight */
2855 floodLightAmount = 0.0f;
2856 VectorClear( floodColor );
2857 if( floodlighty && !bouncing )
2859 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2860 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2864 LightingAtSample( &trace, ds->vertexStyles, colors );
2867 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2870 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2872 /* jal: floodlight */
2873 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2876 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2877 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2878 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2882 /* is this sample bright enough? */
2883 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2884 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2885 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2886 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2888 /* nudge the sample point around a bit */
2889 for( x = 0; x < 5; x++ )
2891 /* two's complement 0, 1, -1, 2, -2, etc */
2892 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2894 for( y = 0; y < 5; y++ )
2896 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2898 for( z = 0; z < 5; z++ )
2900 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2903 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2904 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2905 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2907 /* try at nudged origin */
2908 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2909 if( trace.cluster < 0 )
2913 if( dirty && !bouncing )
2914 dirt = DirtForSample( &trace );
2918 /* jal: floodlight */
2919 floodLightAmount = 0.0f;
2920 VectorClear( floodColor );
2921 if( floodlighty && !bouncing )
2923 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2924 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2928 LightingAtSample( &trace, ds->vertexStyles, colors );
2931 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2934 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2936 /* jal: floodlight */
2937 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2940 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2941 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2944 /* bright enough? */
2945 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2946 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2947 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2948 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2955 /* add to average? */
2956 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2957 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2958 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2959 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2962 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2964 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2965 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2970 /* another happy customer */
2971 numVertsIlluminated++;
2974 /* set average color */
2977 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2978 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2982 VectorCopy( ambientColor, avgColors[ 0 ] );
2985 /* clean up and store vertex color */
2986 for( i = 0; i < ds->numVerts; i++ )
2988 /* get vertex luxel */
2989 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2991 /* store average in occluded vertexes */
2992 if( radVertLuxel[ 0 ] < 0.0f )
2994 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2996 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2997 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
3000 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
3005 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3008 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3009 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3012 if( bouncing || bounce == 0 || !bounceOnly )
3013 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3014 if( !info->si->noVertexLight )
3015 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3019 /* free light list */
3020 FreeTraceLights( &trace );
3022 /* return to sender */
3026 /* -----------------------------------------------------------------
3027 reconstitute vertex lighting from the luxels
3028 ----------------------------------------------------------------- */
3030 /* set styles from lightmap */
3031 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3032 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3034 /* get max search radius */
3036 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3038 /* walk the surface verts */
3039 verts = yDrawVerts + ds->firstVert;
3040 for( i = 0; i < ds->numVerts; i++ )
3042 /* do each lightmap */
3043 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3046 if( lm->superLuxels[ lightmapNum ] == NULL )
3049 /* get luxel coords */
3050 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3051 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3054 else if( x >= lm->sw )
3058 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? */
3068 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
3069 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
3070 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
3073 /* color the luxel with surface num? */
3074 else if( debugSurfaces )
3075 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3077 /* divine color from the superluxels */
3080 /* increasing radius */
3081 VectorClear( radVertLuxel );
3083 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3085 /* sample within radius */
3086 for( sy = (y - radius); sy <= (y + radius); sy++ )
3088 if( sy < 0 || sy >= lm->sh )
3091 for( sx = (x - radius); sx <= (x + radius); sx++ )
3093 if( sx < 0 || sx >= lm->sw )
3096 /* get luxel particulars */
3097 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3098 cluster = SUPER_CLUSTER( sx, sy );
3102 /* testing: must be brigher than ambient color */
3103 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3106 /* add its distinctiveness to our own */
3107 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3108 samples += luxel[ 3 ];
3114 if( samples > 0.0f )
3115 VectorDivide( radVertLuxel, samples, radVertLuxel );
3117 VectorCopy( ambientColor, radVertLuxel );
3120 /* store into floating point storage */
3121 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3122 numVertsIlluminated++;
3124 /* store into bytes (for vertex approximation) */
3125 if( !info->si->noVertexLight )
3126 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3133 /* -------------------------------------------------------------------------------
3135 light optimization (-fast)
3137 creates a list of lights that will affect a surface and stores it in tw
3138 this is to optimize surface lighting by culling out as many of the
3139 lights in the world as possible from further calculation
3141 ------------------------------------------------------------------------------- */
3145 determines opaque brushes in the world and find sky shaders for sunlight calculations
3148 void SetupBrushes( void )
3150 int i, j, b, compileFlags;
3153 bspBrushSide_t *side;
3154 bspShader_t *shader;
3159 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3162 if( opaqueBrushes == NULL )
3163 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3166 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3167 numOpaqueBrushes = 0;
3169 /* walk the list of worldspawn brushes */
3170 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3173 b = bspModels[ 0 ].firstBSPBrush + i;
3174 brush = &bspBrushes[ b ];
3176 /* check all sides */
3179 for( j = 0; j < brush->numSides && inside; j++ )
3181 /* do bsp shader calculations */
3182 side = &bspBrushSides[ brush->firstSide + j ];
3183 shader = &bspShaders[ side->shaderNum ];
3185 /* get shader info */
3186 si = ShaderInfoForShader( shader->shader );
3190 /* or together compile flags */
3191 compileFlags |= si->compileFlags;
3194 /* determine if this brush is opaque to light */
3195 if( !(compileFlags & C_TRANSLUCENT) )
3197 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3203 /* emit some statistics */
3204 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3211 determines if two clusters are visible to each other using the PVS
3214 qboolean ClusterVisible( int a, int b )
3221 if( a < 0 || b < 0 )
3229 if( numBSPVisBytes <=8 )
3233 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3234 leafBytes = ((int*) bspVisBytes)[ 1 ];
3235 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3238 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3247 borrowed from vlight.c
3250 int PointInLeafNum_r( vec3_t point, int nodenum )
3258 while( nodenum >= 0 )
3260 node = &bspNodes[ nodenum ];
3261 plane = &bspPlanes[ node->planeNum ];
3262 dist = DotProduct( point, plane->normal ) - plane->dist;
3264 nodenum = node->children[ 0 ];
3265 else if( dist < -0.1 )
3266 nodenum = node->children[ 1 ];
3269 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3270 if( bspLeafs[ leafnum ].cluster != -1 )
3272 nodenum = node->children[ 1 ];
3276 leafnum = -nodenum - 1;
3284 borrowed from vlight.c
3287 int PointInLeafNum( vec3_t point )
3289 return PointInLeafNum_r( point, 0 );
3295 ClusterVisibleToPoint() - ydnar
3296 returns qtrue if point can "see" cluster
3299 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3304 /* get leafNum for point */
3305 pointCluster = ClusterForPoint( point );
3306 if( pointCluster < 0 )
3310 return ClusterVisible( pointCluster, cluster );
3316 ClusterForPoint() - ydnar
3317 returns the pvs cluster for point
3320 int ClusterForPoint( vec3_t point )
3325 /* get leafNum for point */
3326 leafNum = PointInLeafNum( point );
3330 /* return the cluster */
3331 return bspLeafs[ leafNum ].cluster;
3337 ClusterForPointExt() - ydnar
3338 also takes brushes into account for occlusion testing
3341 int ClusterForPointExt( vec3_t point, float epsilon )
3343 int i, j, b, leafNum, cluster;
3346 int *brushes, numBSPBrushes;
3352 /* get leaf for point */
3353 leafNum = PointInLeafNum( point );
3356 leaf = &bspLeafs[ leafNum ];
3358 /* get the cluster */
3359 cluster = leaf->cluster;
3363 /* transparent leaf, so check point against all brushes in the leaf */
3364 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3365 numBSPBrushes = leaf->numBSPLeafBrushes;
3366 for( i = 0; i < numBSPBrushes; i++ )
3370 if( b > maxOpaqueBrush )
3372 brush = &bspBrushes[ b ];
3373 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3376 /* check point against all planes */
3378 for( j = 0; j < brush->numSides && inside; j++ )
3380 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3381 dot = DotProduct( point, plane->normal );
3387 /* if inside, return bogus cluster */
3392 /* if the point made it this far, it's not inside any opaque brushes */
3399 ClusterForPointExtFilter() - ydnar
3400 adds cluster checking against a list of known valid clusters
3403 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3408 /* get cluster for point */
3409 cluster = ClusterForPointExt( point, epsilon );
3411 /* check if filtering is necessary */
3412 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3416 for( i = 0; i < numClusters; i++ )
3418 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3429 ShaderForPointInLeaf() - ydnar
3430 checks a point against all brushes in a leaf, returning the shader of the brush
3431 also sets the cumulative surface and content flags for the brush hit
3434 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3439 int *brushes, numBSPBrushes;
3442 bspBrushSide_t *side;
3444 bspShader_t *shader;
3445 int allSurfaceFlags, allContentFlags;
3448 /* clear things out first */
3455 leaf = &bspLeafs[ leafNum ];
3457 /* transparent leaf, so check point against all brushes in the leaf */
3458 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3459 numBSPBrushes = leaf->numBSPLeafBrushes;
3460 for( i = 0; i < numBSPBrushes; i++ )
3463 brush = &bspBrushes[ brushes[ i ] ];
3465 /* check point against all planes */
3467 allSurfaceFlags = 0;
3468 allContentFlags = 0;
3469 for( j = 0; j < brush->numSides && inside; j++ )
3471 side = &bspBrushSides[ brush->firstSide + j ];
3472 plane = &bspPlanes[ side->planeNum ];
3473 dot = DotProduct( point, plane->normal );
3479 shader = &bspShaders[ side->shaderNum ];
3480 allSurfaceFlags |= shader->surfaceFlags;
3481 allContentFlags |= shader->contentFlags;
3485 /* handle if inside */
3488 /* if there are desired flags, check for same and continue if they aren't matched */
3489 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3491 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3494 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3495 *surfaceFlags = allSurfaceFlags;
3496 *contentFlags = allContentFlags;
3497 return brush->shaderNum;
3501 /* if the point made it this far, it's not inside any brushes */
3509 chops a bounding box by the plane defined by origin and normal
3510 returns qfalse if the bounds is entirely clipped away
3512 this is not exactly the fastest way to do this...
3515 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3517 /* FIXME: rewrite this so it doesn't use bloody brushes */
3525 calculates each light's effective envelope,
3526 taking into account brightness, type, and pvs.
3529 #define LIGHT_EPSILON 0.125f
3530 #define LIGHT_NUDGE 2.0f
3532 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3534 int i, x, y, z, x1, y1, z1;
3535 light_t *light, *light2, **owner;
3537 vec3_t origin, dir, mins, maxs;
3538 float radius, intensity;
3539 light_t *buckets[ 256 ];
3542 /* early out for weird cases where there are no lights */
3543 if( lights == NULL )
3547 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3551 numCulledLights = 0;
3553 while( *owner != NULL )
3558 /* handle negative lights */
3559 if( light->photons < 0.0f || light->add < 0.0f )
3561 light->photons *= -1.0f;
3562 light->add *= -1.0f;
3563 light->flags |= LIGHT_NEGATIVE;
3567 if( light->type == EMIT_SUN )
3571 light->envelope = MAX_WORLD_COORD * 8.0f;
3572 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3573 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3576 /* everything else */
3579 /* get pvs cluster for light */
3580 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3582 /* invalid cluster? */
3583 if( light->cluster < 0 )
3585 /* nudge the sample point around a bit */
3586 for( x = 0; x < 4; x++ )
3588 /* two's complement 0, 1, -1, 2, -2, etc */
3589 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3591 for( y = 0; y < 4; y++ )
3593 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3595 for( z = 0; z < 4; z++ )
3597 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3600 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3601 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3602 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3604 /* try at nudged origin */
3605 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3606 if( light->cluster < 0 )
3610 VectorCopy( origin, light->origin );
3616 /* only calculate for lights in pvs and outside of opaque brushes */
3617 if( light->cluster >= 0 )
3619 /* set light fast flag */
3621 light->flags |= LIGHT_FAST_TEMP;
3623 light->flags &= ~LIGHT_FAST_TEMP;
3624 if( light->si && light->si->noFast )
3625 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3627 /* clear light envelope */
3628 light->envelope = 0;
3630 /* handle area lights */
3631 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3633 /* ugly hack to calculate extent for area lights, but only done once */
3634 VectorScale( light->normal, -1.0f, dir );
3635 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3639 VectorMA( light->origin, radius, light->normal, origin );
3640 factor = PointToPolygonFormFactor( origin, dir, light->w );
3643 if( (factor * light->add) <= light->falloffTolerance )
3644 light->envelope = radius;
3647 /* check for fast mode */
3648 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3649 light->envelope = MAX_WORLD_COORD * 8.0f;
3650 intensity = light->photons; /* hopefully not used */
3655 intensity = light->photons;
3659 if( light->envelope <= 0.0f )
3661 /* solve distance for non-distance lights */
3662 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3663 light->envelope = MAX_WORLD_COORD * 8.0f;
3665 /* solve distance for linear lights */
3666 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3667 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3668 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3671 add = angle * light->photons * linearScale - (dist * light->fade);
3672 T = (light->photons * linearScale) - (dist * light->fade);
3673 T + (dist * light->fade) = (light->photons * linearScale);
3674 dist * light->fade = (light->photons * linearScale) - T;
3675 dist = ((light->photons * linearScale) - T) / light->fade;
3678 /* solve for inverse square falloff */
3680 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3683 add = light->photons / (dist * dist);
3684 T = light->photons / (dist * dist);
3685 T * (dist * dist) = light->photons;
3686 dist = sqrt( light->photons / T );
3690 /* chop radius against pvs */
3693 ClearBounds( mins, maxs );
3695 /* check all leaves */
3696 for( i = 0; i < numBSPLeafs; i++ )
3699 leaf = &bspLeafs[ i ];
3702 if( leaf->cluster < 0 )
3704 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3707 /* add this leafs bbox to the bounds */
3708 VectorCopy( leaf->mins, origin );
3709 AddPointToBounds( origin, mins, maxs );
3710 VectorCopy( leaf->maxs, origin );
3711 AddPointToBounds( origin, mins, maxs );
3714 /* test to see if bounds encompass light */
3715 for( i = 0; i < 3; i++ )
3717 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3719 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3720 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3721 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3722 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3723 AddPointToBounds( light->origin, mins, maxs );
3727 /* chop the bounds by a plane for area lights and spotlights */
3728 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3729 ChopBounds( mins, maxs, light->origin, light->normal );
3732 VectorCopy( mins, light->mins );
3733 VectorCopy( maxs, light->maxs );
3735 /* reflect bounds around light origin */
3736 //% VectorMA( light->origin, -1.0f, origin, origin );
3737 VectorScale( light->origin, 2, origin );
3738 VectorSubtract( origin, maxs, origin );
3739 AddPointToBounds( origin, mins, maxs );
3740 //% VectorMA( light->origin, -1.0f, mins, origin );
3741 VectorScale( light->origin, 2, origin );
3742 VectorSubtract( origin, mins, origin );
3743 AddPointToBounds( origin, mins, maxs );
3745 /* calculate spherical bounds */
3746 VectorSubtract( maxs, light->origin, dir );
3747 radius = (float) VectorLength( dir );
3749 /* if this radius is smaller than the envelope, then set the envelope to it */
3750 if( radius < light->envelope )
3752 light->envelope = radius;
3753 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3756 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3759 /* add grid/surface only check */
3762 if( !(light->flags & LIGHT_GRID) )
3763 light->envelope = 0.0f;
3767 if( !(light->flags & LIGHT_SURFACES) )
3768 light->envelope = 0.0f;
3773 if( light->cluster < 0 || light->envelope <= 0.0f )
3776 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3778 /* delete the light */
3780 *owner = light->next;
3781 if( light->w != NULL )
3788 /* square envelope */
3789 light->envelope2 = (light->envelope * light->envelope);
3791 /* increment light count */
3794 /* set next light */
3795 owner = &((**owner).next);
3798 /* bucket sort lights by style */
3799 memset( buckets, 0, sizeof( buckets ) );
3801 for( light = lights; light != NULL; light = light2 )
3803 /* get next light */
3804 light2 = light->next;
3806 /* filter into correct bucket */
3807 light->next = buckets[ light->style ];
3808 buckets[ light->style ] = light;
3810 /* if any styled light is present, automatically set nocollapse */
3811 if( light->style != LS_NORMAL )
3815 /* filter back into light list */
3817 for( i = 255; i >= 0; i-- )
3820 for( light = buckets[ i ]; light != NULL; light = light2 )
3822 light2 = light->next;
3823 light->next = lights;
3828 /* emit some statistics */
3829 Sys_Printf( "%9d total lights\n", numLights );
3830 Sys_Printf( "%9d culled lights\n", numCulledLights );
3836 CreateTraceLightsForBounds()
3837 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3840 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3844 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3845 float radius, dist, length;
3848 /* potential pre-setup */
3849 if( numLights == 0 )
3850 SetupEnvelopes( qfalse, fast );
3853 //% 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 ] );
3855 /* allocate the light list */
3856 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3857 trace->numLights = 0;
3859 /* calculate spherical bounds */
3860 VectorAdd( mins, maxs, origin );
3861 VectorScale( origin, 0.5f, origin );
3862 VectorSubtract( maxs, origin, dir );
3863 radius = (float) VectorLength( dir );
3865 /* get length of normal vector */
3866 if( normal != NULL )
3867 length = VectorLength( normal );
3870 normal = nullVector;
3874 /* test each light and see if it reaches the sphere */
3875 /* note: the attenuation code MUST match LightingAtSample() */
3876 for( light = lights; light; light = light->next )
3878 /* check zero sized envelope */
3879 if( light->envelope <= 0 )
3881 lightsEnvelopeCulled++;
3886 if( !(light->flags & flags) )
3889 /* sunlight skips all this nonsense */
3890 if( light->type != EMIT_SUN )
3896 /* check against pvs cluster */
3897 if( numClusters > 0 && clusters != NULL )
3899 for( i = 0; i < numClusters; i++ )
3901 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3906 if( i == numClusters )
3908 lightsClusterCulled++;
3913 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3914 VectorSubtract( light->origin, origin, dir );
3915 dist = VectorLength( dir );
3916 dist -= light->envelope;
3920 lightsEnvelopeCulled++;
3924 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3927 for( i = 0; i < 3; i++ )
3929 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3934 lightsBoundsCulled++;
3940 /* planar surfaces (except twosided surfaces) have a couple more checks */
3941 if( length > 0.0f && trace->twoSided == qfalse )
3943 /* lights coplanar with a surface won't light it */
3944 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3946 lightsPlaneCulled++;
3950 /* check to see if light is behind the plane */
3951 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3953 lightsPlaneCulled++;
3958 /* add this light */
3959 trace->lights[ trace->numLights++ ] = light;
3962 /* make last night null */
3963 trace->lights[ trace->numLights ] = NULL;
3968 void FreeTraceLights( trace_t *trace )
3970 if( trace->lights != NULL )
3971 free( trace->lights );
3977 CreateTraceLightsForSurface()
3978 creates a list of lights that can potentially affect a drawsurface
3981 void CreateTraceLightsForSurface( int num, trace_t *trace )
3984 vec3_t mins, maxs, normal;
3986 bspDrawSurface_t *ds;
3987 surfaceInfo_t *info;
3994 /* get drawsurface and info */
3995 ds = &bspDrawSurfaces[ num ];
3996 info = &surfaceInfos[ num ];
3998 /* get the mins/maxs for the dsurf */
3999 ClearBounds( mins, maxs );
4000 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4001 for( i = 0; i < ds->numVerts; i++ )
4003 dv = &yDrawVerts[ ds->firstVert + i ];
4004 AddPointToBounds( dv->xyz, mins, maxs );
4005 if( !VectorCompare( dv->normal, normal ) )
4006 VectorClear( normal );
4009 /* create the lights for the bounding box */
4010 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4013 /////////////////////////////////////////////////////////////
4015 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4016 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4017 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4018 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
4020 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4021 static int numFloodVectors = 0;
4023 void SetupFloodLight( void )
4026 float angle, elevation, angleStep, elevationStep;
4028 double v1,v2,v3,v4,v5,v6;
4031 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4033 /* calculate angular steps */
4034 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4035 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4039 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4041 /* iterate elevation */
4042 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4044 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4045 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4046 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4051 /* emit some statistics */
4052 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4055 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4057 if( value[ 0 ] != '\0' )
4060 v4=floodlightDistance;
4061 v5=floodlightIntensity;
4062 v6=floodlightDirectionScale;
4064 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6);
4066 floodlightRGB[0]=v1;
4067 floodlightRGB[1]=v2;
4068 floodlightRGB[2]=v3;
4070 if (VectorLength(floodlightRGB)==0)
4072 VectorSet(floodlightRGB,240,240,255);
4079 floodlightDistance=v4;
4080 floodlightIntensity=v5;
4081 floodlightDirectionScale=v6;
4083 floodlighty = qtrue;
4084 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4088 VectorSet(floodlightRGB,240,240,255);
4089 //floodlighty = qtrue;
4090 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4092 VectorNormalize(floodlightRGB,floodlightRGB);
4096 FloodLightForSample()
4097 calculates floodlight value for a given sample
4098 once again, kudos to the dirtmapping coder
4101 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
4107 float gatherLight, outLight;
4108 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4116 if( trace == NULL || trace->cluster < 0 )
4121 dd = floodLightDistance;
4122 VectorCopy( trace->normal, normal );
4124 /* check if the normal is aligned to the world-up */
4125 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4127 if( normal[ 2 ] == 1.0f )
4129 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4130 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4132 else if( normal[ 2 ] == -1.0f )
4134 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4135 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4140 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4141 CrossProduct( normal, worldUp, myRt );
4142 VectorNormalize( myRt, myRt );
4143 CrossProduct( myRt, normal, myUp );
4144 VectorNormalize( myUp, myUp );
4147 /* vortex: optimise floodLightLowQuality a bit */
4148 if ( floodLightLowQuality == qtrue )
4150 /* iterate through ordered vectors */
4151 for( i = 0; i < numFloodVectors; i++ )
4152 if (rand()%10 != 0 ) continue;
4156 /* iterate through ordered vectors */
4157 for( i = 0; i < numFloodVectors; i++ )
4161 /* transform vector into tangent space */
4162 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4163 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4164 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4167 VectorMA( trace->origin, dd, direction, trace->end );
4169 //VectorMA( trace->origin, 1, direction, trace->origin );
4171 SetupTrace( trace );
4176 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT )
4180 else if ( trace->opaque )
4182 VectorSubtract( trace->hit, trace->origin, displacement );
4183 d=VectorLength( displacement );
4185 // d=trace->distance;
4186 //if (d>256) gatherDirt+=1;
4188 if (contribution>1) contribution=1.0f;
4190 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4193 gatherLight+=contribution;
4198 if( gatherLight <= 0.0f )
4206 outLight=gatherLight;
4207 if( outLight > 1.0f )
4210 /* return to sender */
4215 FloodLightRawLightmap
4216 lighttracer style ambient occlusion light hack.
4217 Kudos to the dirtmapping author for most of this source.
4218 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4219 VorteX: fixed problems with deluxemapping
4222 // floodlight pass on a lightmap
4223 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4225 int i, x, y, *cluster;
4226 float *origin, *normal, *floodlight, floodLightAmount;
4227 surfaceInfo_t *info;
4230 // float samples, average, *floodlight2;
4232 memset(&trace,0,sizeof(trace_t));
4235 trace.testOcclusion = qtrue;
4236 trace.forceSunlight = qfalse;
4237 trace.twoSided = qtrue;
4238 trace.recvShadows = lm->recvShadows;
4239 trace.numSurfaces = lm->numLightSurfaces;
4240 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4241 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4242 trace.testAll = qfalse;
4243 trace.distance = 1024;
4245 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4246 //trace.twoSided = qfalse;
4247 for( i = 0; i < trace.numSurfaces; i++ )
4250 info = &surfaceInfos[ trace.surfaces[ i ] ];
4252 /* check twosidedness */
4253 if( info->si->twoSided )
4255 trace.twoSided = qtrue;
4260 /* gather floodlight */
4261 for( y = 0; y < lm->sh; y++ )
4263 for( x = 0; x < lm->sw; x++ )
4266 cluster = SUPER_CLUSTER( x, y );
4267 origin = SUPER_ORIGIN( x, y );
4268 normal = SUPER_NORMAL( x, y );
4269 floodlight = SUPER_FLOODLIGHT( x, y );
4271 /* set default dirt */
4274 /* only look at mapped luxels */
4279 trace.cluster = *cluster;
4280 VectorCopy( origin, trace.origin );
4281 VectorCopy( normal, trace.normal );
4283 /* get floodlight */
4284 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4286 /* add floodlight */
4287 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4288 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4289 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4290 floodlight[3] += floodlightDirectionScale;
4294 /* testing no filtering */
4300 for( y = 0; y < lm->sh; y++ )
4302 for( x = 0; x < lm->sw; x++ )
4305 cluster = SUPER_CLUSTER( x, y );
4306 floodlight = SUPER_FLOODLIGHT(x, y );
4308 /* filter dirt by adjacency to unmapped luxels */
4309 average = *floodlight;
4311 for( sy = (y - 1); sy <= (y + 1); sy++ )
4313 if( sy < 0 || sy >= lm->sh )
4316 for( sx = (x - 1); sx <= (x + 1); sx++ )
4318 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4321 /* get neighboring luxel */
4322 cluster = SUPER_CLUSTER( sx, sy );
4323 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4324 if( *cluster < 0 || *floodlight2 <= 0.0f )
4328 average += *floodlight2;
4333 if( samples <= 0.0f )
4338 if( samples <= 0.0f )
4342 *floodlight = average / samples;
4348 void FloodLightRawLightmap( int rawLightmapNum )
4352 /* bail if this number exceeds the number of raw lightmaps */
4353 if( rawLightmapNum >= numRawLightmaps )
4356 lm = &rawLightmaps[ rawLightmapNum ];
4359 if (floodlighty && floodlightIntensity)
4360 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale);
4363 if (lm->floodlightIntensity)
4365 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4366 numSurfacesFloodlighten += 1;
4370 void FloodlightRawLightmaps()
4372 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4373 numSurfacesFloodlighten = 0;
4374 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4375 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4379 FloodLightIlluminate()
4380 illuminate floodlight into lightmap luxels
4383 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4385 float *luxel, *floodlight, *deluxel, *normal;
4388 int x, y, lightmapNum;
4390 /* walk lightmaps */
4391 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4394 if( lm->superLuxels[ lightmapNum ] == NULL )
4397 /* apply floodlight to each luxel */
4398 for( y = 0; y < lm->sh; y++ )
4400 for( x = 0; x < lm->sw; x++ )
4402 /* get floodlight */
4403 floodlight = SUPER_FLOODLIGHT( x, y );
4404 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4408 cluster = SUPER_CLUSTER( x, y );
4410 /* only process mapped luxels */
4414 /* get particulars */
4415 luxel = SUPER_LUXEL( lightmapNum, x, y );
4416 deluxel = SUPER_DELUXEL( x, y );
4418 /* add to lightmap */
4419 luxel[0]+=floodlight[0];
4420 luxel[1]+=floodlight[1];
4421 luxel[2]+=floodlight[2];
4423 if (luxel[3]==0) luxel[3]=1;
4425 /* add to deluxemap */
4426 if (deluxemap && floodlight[3] > 0)
4430 normal = SUPER_NORMAL( x, y );
4431 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4433 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4434 if(brightness < 0.00390625f)
4435 brightness = 0.00390625f;
4437 VectorScale( normal, brightness, lightvector );
4438 VectorAdd( deluxel, lightvector, deluxel );