1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale ){
54 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
55 if ( scale <= 0.0f ) {
59 scale *= lightmapBrightness;
61 /* make a local copy */
62 VectorScale( color, scale, sample );
65 gamma = 1.0f / lightmapGamma;
66 for ( i = 0; i < 3; i++ )
68 /* handle negative light */
69 if ( sample[ i ] < 0.0f ) {
75 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
78 if ( lightmapExposure == 0 ) {
79 /* clamp with color normalization */
81 if ( sample[ 1 ] > max ) {
84 if ( sample[ 2 ] > max ) {
88 VectorScale( sample, ( 255.0f / max ), sample );
93 inv = 1.f / lightmapExposure;
97 if ( sample[ 1 ] > max ) {
100 if ( sample[ 2 ] > max ) {
104 dif = ( 1 - exp( -max * inv ) ) * 255;
114 for ( i = 0; i < 3; i++ )
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );
125 if ( lightmapContrast != 1.0f ){
126 for ( i = 0; i < 3; i++ ){
127 sample[i] = lightmapContrast * ( sample[i] - 128 ) + 128;
128 if ( sample[i] < 0 ){
132 if ( ( sample[0] > 255 ) || ( sample[1] > 255 ) || ( sample[2] > 255 ) ) {
133 max = sample[0] > sample[1] ? sample[0] : sample[1];
134 max = max > sample[2] ? max : sample[2];
135 sample[0] = sample[0] * 255 / max;
136 sample[1] = sample[1] * 255 / max;
137 sample[2] = sample[2] * 255 / max;
142 if ( lightmapsRGB ) {
143 sample[0] = floor( Image_sRGBFloatFromLinearFloat( sample[0] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
144 sample[1] = floor( Image_sRGBFloatFromLinearFloat( sample[1] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
145 sample[2] = floor( Image_sRGBFloatFromLinearFloat( sample[2] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
149 colorBytes[ 0 ] = sample[ 0 ];
150 colorBytes[ 1 ] = sample[ 1 ];
151 colorBytes[ 2 ] = sample[ 2 ];
156 /* -------------------------------------------------------------------------------
158 this section deals with phong shading (normal interpolation across brush faces)
160 ------------------------------------------------------------------------------- */
164 smooths together coincident vertex normals across the bsp
167 #define MAX_SAMPLES 256
168 #define THETA_EPSILON 0.000001
169 #define EQUAL_NORMAL_EPSILON 0.01
171 void SmoothNormals( void ){
172 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
173 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
174 bspDrawSurface_t *ds;
178 vec3_t average, diff;
179 int indexes[ MAX_SAMPLES ];
180 vec3_t votes[ MAX_SAMPLES ];
183 /* allocate shade angle table */
184 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
185 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
187 /* allocate smoothed table */
188 cs = ( numBSPDrawVerts / 8 ) + 1;
189 smoothed = safe_malloc( cs );
190 memset( smoothed, 0, cs );
192 /* set default shade angle */
193 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
196 /* run through every surface and flag verts belonging to non-lightmapped surfaces
197 and set per-vertex smoothing angle */
198 for ( i = 0; i < numBSPDrawSurfaces; i++ )
201 ds = &bspDrawSurfaces[ i ];
203 /* get shader for shade angle */
204 si = surfaceInfos[ i ].si;
205 if ( si->shadeAngleDegrees ) {
206 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
209 shadeAngle = defaultShadeAngle;
211 if ( shadeAngle > maxShadeAngle ) {
212 maxShadeAngle = shadeAngle;
216 for ( j = 0; j < ds->numVerts; j++ )
218 f = ds->firstVert + j;
219 shadeAngles[ f ] = shadeAngle;
220 if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
221 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
225 /* ydnar: optional force-to-trisoup */
226 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
227 ds->surfaceType = MST_TRIANGLE_SOUP;
228 ds->lightmapNum[ 0 ] = -3;
232 /* bail if no surfaces have a shade angle */
233 if ( maxShadeAngle == 0 ) {
241 start = I_FloatTime();
243 /* go through the list of vertexes */
244 for ( i = 0; i < numBSPDrawVerts; i++ )
247 f = 10 * i / numBSPDrawVerts;
250 Sys_Printf( "%i...", f );
253 /* already smoothed? */
254 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
259 VectorClear( average );
263 /* build a table of coincident vertexes */
264 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
266 /* already smoothed? */
267 if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
272 if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
276 /* use smallest shade angle */
277 shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
279 /* check shade angle */
280 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
284 else if ( dot < -1.0 ) {
287 testAngle = acos( dot ) + THETA_EPSILON;
288 if ( testAngle >= shadeAngle ) {
289 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
292 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
294 /* add to the list */
295 indexes[ numVerts++ ] = j;
298 smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
300 /* see if this normal has already been voted */
301 for ( k = 0; k < numVotes; k++ )
303 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
304 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
305 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
306 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
311 /* add a new vote? */
312 if ( k == numVotes && numVotes < MAX_SAMPLES ) {
313 VectorAdd( average, bspDrawVerts[ j ].normal, average );
314 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
319 /* don't average for less than 2 verts */
320 if ( numVerts < 2 ) {
325 if ( VectorNormalize( average, average ) > 0 ) {
327 for ( j = 0; j < numVerts; j++ )
328 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
332 /* free the tables */
337 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
342 /* -------------------------------------------------------------------------------
344 this section deals with phong shaded lightmap tracing
346 ------------------------------------------------------------------------------- */
348 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
352 calculates the st tangent vectors for normalmapping
355 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
361 /* calculate barycentric basis for the triangle */
362 bb = ( dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ] ) * ( dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ] ) - ( dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ] ) * ( dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ] );
363 if ( fabs( bb ) < 0.00000001f ) {
368 for ( i = 0; i < numVerts; i++ )
370 /* calculate s tangent vector */
371 s = dv[ i ]->st[ 0 ] + 10.0f;
372 t = dv[ i ]->st[ 1 ];
373 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
374 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
375 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
377 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
378 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
379 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
381 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
382 VectorNormalize( stv[ i ], stv[ i ] );
384 /* calculate t tangent vector */
385 s = dv[ i ]->st[ 0 ];
386 t = dv[ i ]->st[ 1 ] + 10.0f;
387 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
388 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
389 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
391 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
392 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
393 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
395 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
396 VectorNormalize( ttv[ i ], ttv[ i ] );
399 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
400 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
403 /* return to caller */
412 perterbs the normal by the shader's normalmap in tangent space
415 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
421 VectorCopy( dv->normal, pNormal );
423 /* sample normalmap */
424 if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
428 /* remap sampled normal from [0,255] to [-1,-1] */
429 for ( i = 0; i < 3; i++ )
430 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
432 /* scale tangent vectors and add to original normal */
433 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
434 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
435 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
437 /* renormalize and return */
438 VectorNormalize( pNormal, pNormal );
445 maps a luxel for triangle bv at
449 #define BOGUS_NUDGE -99999.0f
451 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] ){
452 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
453 float *luxel, *origin, *normal, d, lightmapSampleOffset;
460 vec4_t sideplane, hostplane;
465 static float nudges[][ 2 ] =
467 //%{ 0, 0 }, /* try center first */
468 { -NUDGE, 0 }, /* left */
469 { NUDGE, 0 }, /* right */
470 { 0, NUDGE }, /* up */
471 { 0, -NUDGE }, /* down */
472 { -NUDGE, NUDGE }, /* left/up */
473 { NUDGE, -NUDGE }, /* right/down */
474 { NUDGE, NUDGE }, /* right/up */
475 { -NUDGE, -NUDGE }, /* left/down */
476 { BOGUS_NUDGE, BOGUS_NUDGE }
480 /* find luxel xy coords (fixme: subtract 0.5?) */
481 x = dv->lightmap[ 0 ][ 0 ];
482 y = dv->lightmap[ 0 ][ 1 ];
486 else if ( x >= lm->sw ) {
492 else if ( y >= lm->sh ) {
496 /* set shader and cluster list */
497 if ( info != NULL ) {
499 numClusters = info->numSurfaceClusters;
500 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
509 /* get luxel, origin, cluster, and normal */
510 luxel = SUPER_LUXEL( 0, x, y );
511 origin = SUPER_ORIGIN( x, y );
512 normal = SUPER_NORMAL( x, y );
513 cluster = SUPER_CLUSTER( x, y );
515 /* don't attempt to remap occluded luxels for planar surfaces */
516 if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
520 /* only average the normal for premapped luxels */
521 else if ( ( *cluster ) >= 0 ) {
522 /* do bumpmap calculations */
524 PerturbNormal( dv, si, pNormal, stv, ttv );
527 VectorCopy( dv->normal, pNormal );
530 /* add the additional normal data */
531 VectorAdd( normal, pNormal, normal );
536 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
540 /* axial lightmap projection */
541 if ( lm->vecs != NULL ) {
542 /* calculate an origin for the sample from the lightmap vectors */
543 VectorCopy( lm->origin, origin );
544 for ( i = 0; i < 3; i++ )
546 /* add unless it's the axis, which is taken care of later */
547 if ( i == lm->axisNum ) {
550 origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
553 /* project the origin onto the plane */
554 d = DotProduct( origin, plane ) - plane[ 3 ];
555 d /= plane[ lm->axisNum ];
556 origin[ lm->axisNum ] -= d;
559 /* non axial lightmap projection (explicit xyz) */
561 VectorCopy( dv->xyz, origin );
564 //////////////////////
565 //27's test to make sure samples stay within the triangle boundaries
566 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
567 //2) if it does, nudge it onto the correct side.
569 if ( worldverts != NULL && lightmapTriangleCheck ) {
570 for ( j = 0; j < 3; j++ )
572 VectorCopy( worldverts[j],cverts[j] );
574 PlaneFromPoints( hostplane,cverts[0],cverts[1],cverts[2] );
576 for ( j = 0; j < 3; j++ )
578 for ( i = 0; i < 3; i++ )
580 //build plane using 2 edges and a normal
581 next = ( i + 1 ) % 3;
583 VectorCopy( cverts[next],temp );
584 VectorAdd( temp,hostplane,temp );
585 PlaneFromPoints( sideplane,cverts[i],cverts[ next ], temp );
587 //planetest sample point
588 e = DotProduct( origin,sideplane );
589 e = e - sideplane[3];
592 //VectorClear(origin);
593 //Move the sample point back inside triangle bounds
594 origin[0] -= sideplane[0] * ( e + 1 );
595 origin[1] -= sideplane[1] * ( e + 1 );
596 origin[2] -= sideplane[2] * ( e + 1 );
598 VectorClear( origin );
605 ////////////////////////
607 /* planar surfaces have precalculated lightmap vectors for nudging */
608 if ( lm->plane != NULL ) {
609 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
610 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
611 VectorCopy( lm->plane, vecs[ 2 ] );
614 /* non-planar surfaces must calculate them */
617 if ( plane != NULL ) {
618 VectorCopy( plane, vecs[ 2 ] );
621 VectorCopy( dv->normal, vecs[ 2 ] );
623 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
626 /* push the origin off the surface a bit */
628 lightmapSampleOffset = si->lightmapSampleOffset;
631 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
633 if ( lm->axisNum < 0 ) {
634 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
636 else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
637 origin[ lm->axisNum ] -= lightmapSampleOffset;
640 origin[ lm->axisNum ] += lightmapSampleOffset;
643 VectorCopy( origin,origintwo );
644 if ( lightmapExtraVisClusterNudge ) {
645 origintwo[0] += vecs[2][0];
646 origintwo[1] += vecs[2][1];
647 origintwo[2] += vecs[2][2];
651 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
653 /* another retarded hack, storing nudge count in luxel[ 1 ] */
656 /* point in solid? (except in dark mode) */
657 if ( pointCluster < 0 && dark == qfalse ) {
658 /* nudge the the location around */
660 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
662 /* nudge the vector around a bit */
663 for ( i = 0; i < 3; i++ )
665 /* set nudged point*/
666 nudged[ i ] = origintwo[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
670 /* get pvs cluster */
671 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
672 if ( pointCluster >= 0 ) {
673 VectorCopy( nudged, origin );
679 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
680 if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
681 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
682 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
683 if ( pointCluster >= 0 ) {
684 VectorCopy( nudged, origin );
690 if ( pointCluster < 0 ) {
691 ( *cluster ) = CLUSTER_OCCLUDED;
692 VectorClear( origin );
693 VectorClear( normal );
699 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
701 /* do bumpmap calculations */
703 PerturbNormal( dv, si, pNormal, stv, ttv );
706 VectorCopy( dv->normal, pNormal );
709 /* store the cluster and normal */
710 ( *cluster ) = pointCluster;
711 VectorCopy( pNormal, normal );
713 /* store explicit mapping pass and implicit mapping pass */
728 recursively subdivides a triangle until its edges are shorter
729 than the distance between two luxels (thanks jc :)
732 static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] ){
733 bspDrawVert_t mid, *dv2[ 3 ];
737 /* map the vertexes */
739 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
740 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
741 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
747 float *a, *b, dx, dy, dist, maxDist;
750 /* find the longest edge and split it */
753 for ( i = 0; i < 3; i++ )
756 a = dv[ i ]->lightmap[ 0 ];
757 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
760 dx = a[ 0 ] - b[ 0 ];
761 dy = a[ 1 ] - b[ 1 ];
762 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
765 if ( dist > maxDist ) {
771 /* try to early out */
772 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
777 /* split the longest edge and map it */
778 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
779 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
781 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
782 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
784 /* recurse to first triangle */
785 VectorCopy( dv, dv2 );
787 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
789 /* recurse to second triangle */
790 VectorCopy( dv, dv2 );
791 dv2[ ( max + 1 ) % 3 ] = ∣
792 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
799 seed function for MapTriangle_r()
800 requires a cw ordered triangle
803 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
806 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
807 vec3_t worldverts[ 3 ];
810 /* get plane if possible */
811 if ( lm->plane != NULL ) {
812 VectorCopy( lm->plane, plane );
813 plane[ 3 ] = lm->plane[ 3 ];
816 /* otherwise make one from the points */
817 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
821 /* check to see if we need to calculate texture->world tangent vectors */
822 if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
832 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
833 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
834 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
836 /* map the vertexes */
837 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
838 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
839 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
841 /* 2002-11-20: prefer axial triangle edges */
843 /* subdivide the triangle */
844 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
848 for ( i = 0; i < 3; i++ )
851 bspDrawVert_t *dv2[ 3 ];
855 a = dv[ i ]->lightmap[ 0 ];
856 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
858 /* make degenerate triangles for mapping edges */
859 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
861 dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
862 dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
864 /* map the degenerate triangle */
865 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
876 recursively subdivides a quad until its edges are shorter
877 than the distance between two luxels
880 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] ){
881 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
888 float *a, *b, dx, dy, dist, maxDist;
891 /* find the longest edge and split it */
894 for ( i = 0; i < 4; i++ )
897 a = dv[ i ]->lightmap[ 0 ];
898 b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
901 dx = a[ 0 ] - b[ 0 ];
902 dy = a[ 1 ] - b[ 1 ];
903 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
906 if ( dist > maxDist ) {
912 /* try to early out */
913 if ( max < 0 || maxDist <= subdivideThreshold ) {
918 /* we only care about even/odd edges */
921 /* split the longest edges */
922 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
923 LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
925 /* map the vertexes */
926 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
927 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
931 /* recurse to first quad */
933 dv2[ 1 ] = &mid[ 0 ];
934 dv2[ 2 ] = &mid[ 1 ];
936 MapQuad_r( lm, info, dv2, plane, stv, ttv );
938 /* recurse to second quad */
939 dv2[ 0 ] = &mid[ 0 ];
942 dv2[ 3 ] = &mid[ 1 ];
943 MapQuad_r( lm, info, dv2, plane, stv, ttv );
949 /* recurse to first quad */
952 dv2[ 2 ] = &mid[ 0 ];
953 dv2[ 3 ] = &mid[ 1 ];
954 MapQuad_r( lm, info, dv2, plane, stv, ttv );
956 /* recurse to second quad */
957 dv2[ 0 ] = &mid[ 1 ];
958 dv2[ 1 ] = &mid[ 0 ];
961 MapQuad_r( lm, info, dv2, plane, stv, ttv );
969 seed function for MapQuad_r()
970 requires a cw ordered triangle quad
973 #define QUAD_PLANAR_EPSILON 0.5f
975 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
978 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
981 /* get plane if possible */
982 if ( lm->plane != NULL ) {
983 VectorCopy( lm->plane, plane );
984 plane[ 3 ] = lm->plane[ 3 ];
987 /* otherwise make one from the points */
988 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
992 /* 4th point must fall on the plane */
993 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
994 if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
998 /* check to see if we need to calculate texture->world tangent vectors */
999 if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
1009 /* map the vertexes */
1010 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
1011 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
1012 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
1013 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
1015 /* subdivide the quad */
1016 MapQuad_r( lm, info, dv, plane, stv, ttv );
1024 maps the locations, normals, and pvs clusters for a raw lightmap
1027 #define VectorDivide( in, d, out ) VectorScale( in, ( 1.0f / ( d ) ), out ) //% (out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)
1029 void MapRawLightmap( int rawLightmapNum ){
1030 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1031 float *luxel, *origin, *normal, samples, radius, pass;
1033 bspDrawSurface_t *ds;
1034 surfaceInfo_t *info;
1035 mesh_t src, *subdivided, *mesh;
1036 bspDrawVert_t *verts, *dv[ 4 ], fake;
1039 /* bail if this number exceeds the number of raw lightmaps */
1040 if ( rawLightmapNum >= numRawLightmaps ) {
1045 lm = &rawLightmaps[ rawLightmapNum ];
1047 /* -----------------------------------------------------------------
1048 map referenced surfaces onto the raw lightmap
1049 ----------------------------------------------------------------- */
1051 /* walk the list of surfaces on this raw lightmap */
1052 for ( n = 0; n < lm->numLightSurfaces; n++ )
1054 /* with > 1 surface per raw lightmap, clear occluded */
1056 for ( y = 0; y < lm->sh; y++ )
1058 for ( x = 0; x < lm->sw; x++ )
1061 cluster = SUPER_CLUSTER( x, y );
1062 if ( *cluster < 0 ) {
1063 *cluster = CLUSTER_UNMAPPED;
1070 num = lightSurfaces[ lm->firstLightSurface + n ];
1071 ds = &bspDrawSurfaces[ num ];
1072 info = &surfaceInfos[ num ];
1074 /* bail if no lightmap to calculate */
1075 if ( info->lm != lm ) {
1080 /* map the surface onto the lightmap origin/cluster/normal buffers */
1081 switch ( ds->surfaceType )
1085 verts = yDrawVerts + ds->firstVert;
1087 /* map the triangles */
1088 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1090 for ( i = 0; i < ds->numIndexes; i += 3 )
1092 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1093 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1094 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1095 MapTriangle( lm, info, dv, mapNonAxial );
1101 /* make a mesh from the drawsurf */
1102 src.width = ds->patchWidth;
1103 src.height = ds->patchHeight;
1104 src.verts = &yDrawVerts[ ds->firstVert ];
1105 //% subdivided = SubdivideMesh( src, 8, 512 );
1106 subdivided = SubdivideMesh2( src, info->patchIterations );
1108 /* fit it to the curve and remove colinear verts on rows/columns */
1109 PutMeshOnCurve( *subdivided );
1110 mesh = RemoveLinearMeshColumnsRows( subdivided );
1111 FreeMesh( subdivided );
1114 verts = mesh->verts;
1119 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1120 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1121 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1122 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1126 /* map the mesh quads */
1129 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1131 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1133 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1136 pw[ 0 ] = x + ( y * mesh->width );
1137 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1138 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1139 pw[ 3 ] = x + 1 + ( y * mesh->width );
1140 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1145 /* get drawverts and map first triangle */
1146 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1147 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1148 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1149 MapTriangle( lm, info, dv, mapNonAxial );
1151 /* get drawverts and map second triangle */
1152 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1153 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1154 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1155 MapTriangle( lm, info, dv, mapNonAxial );
1162 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1164 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1167 pw[ 0 ] = x + ( y * mesh->width );
1168 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1169 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1170 pw[ 3 ] = x + 1 + ( y * mesh->width );
1176 /* attempt to map quad first */
1177 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1178 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1179 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1180 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1181 if ( MapQuad( lm, info, dv ) ) {
1185 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1187 /* get drawverts and map first triangle */
1188 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1189 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1190 MapTriangle( lm, info, dv, mapNonAxial );
1192 /* get drawverts and map second triangle */
1193 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1194 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1195 MapTriangle( lm, info, dv, mapNonAxial );
1211 /* -----------------------------------------------------------------
1212 average and clean up luxel normals
1213 ----------------------------------------------------------------- */
1215 /* walk the luxels */
1216 for ( y = 0; y < lm->sh; y++ )
1218 for ( x = 0; x < lm->sw; x++ )
1221 luxel = SUPER_LUXEL( 0, x, y );
1222 normal = SUPER_NORMAL( x, y );
1223 cluster = SUPER_CLUSTER( x, y );
1225 /* only look at mapped luxels */
1226 if ( *cluster < 0 ) {
1230 /* the normal data could be the sum of multiple samples */
1231 if ( luxel[ 3 ] > 1.0f ) {
1232 VectorNormalize( normal, normal );
1235 /* mark this luxel as having only one normal */
1240 /* non-planar surfaces stop here */
1241 if ( lm->plane == NULL ) {
1245 /* -----------------------------------------------------------------
1246 map occluded or unuxed luxels
1247 ----------------------------------------------------------------- */
1249 /* walk the luxels */
1250 radius = floor( superSample / 2 );
1251 radius = radius > 0 ? radius : 1.0f;
1253 for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1255 for ( y = 0; y < lm->sh; y++ )
1257 for ( x = 0; x < lm->sw; x++ )
1260 luxel = SUPER_LUXEL( 0, x, y );
1261 normal = SUPER_NORMAL( x, y );
1262 cluster = SUPER_CLUSTER( x, y );
1264 /* only look at unmapped luxels */
1265 if ( *cluster != CLUSTER_UNMAPPED ) {
1269 /* divine a normal and origin from neighboring luxels */
1270 VectorClear( fake.xyz );
1271 VectorClear( fake.normal );
1272 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1273 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1275 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1277 if ( sy < 0 || sy >= lm->sh ) {
1281 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1283 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1287 /* get neighboring luxel */
1288 luxel = SUPER_LUXEL( 0, sx, sy );
1289 origin = SUPER_ORIGIN( sx, sy );
1290 normal = SUPER_NORMAL( sx, sy );
1291 cluster = SUPER_CLUSTER( sx, sy );
1293 /* only consider luxels mapped in previous passes */
1294 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1298 /* add its distinctiveness to our own */
1299 VectorAdd( fake.xyz, origin, fake.xyz );
1300 VectorAdd( fake.normal, normal, fake.normal );
1301 samples += luxel[ 3 ];
1306 if ( samples == 0.0f ) {
1311 VectorDivide( fake.xyz, samples, fake.xyz );
1312 //% VectorDivide( fake.normal, samples, fake.normal );
1313 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1317 /* map the fake vert */
1318 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1323 /* -----------------------------------------------------------------
1324 average and clean up luxel normals
1325 ----------------------------------------------------------------- */
1327 /* walk the luxels */
1328 for ( y = 0; y < lm->sh; y++ )
1330 for ( x = 0; x < lm->sw; x++ )
1333 luxel = SUPER_LUXEL( 0, x, y );
1334 normal = SUPER_NORMAL( x, y );
1335 cluster = SUPER_CLUSTER( x, y );
1337 /* only look at mapped luxels */
1338 if ( *cluster < 0 ) {
1342 /* the normal data could be the sum of multiple samples */
1343 if ( luxel[ 3 ] > 1.0f ) {
1344 VectorNormalize( normal, normal );
1347 /* mark this luxel as having only one normal */
1355 for ( y = 0; y < lm->sh; y++ )
1357 for ( x = 0; x < lm->sw; x++ )
1362 cluster = SUPER_CLUSTER( x, y );
1363 origin = SUPER_ORIGIN( x, y );
1364 normal = SUPER_NORMAL( x, y );
1365 luxel = SUPER_LUXEL( x, y );
1367 if ( *cluster < 0 ) {
1371 /* check if within the bounding boxes of all surfaces referenced */
1372 ClearBounds( mins, maxs );
1373 for ( n = 0; n < lm->numLightSurfaces; n++ )
1376 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1377 TOL = info->sampleSize + 2;
1378 AddPointToBounds( info->mins, mins, maxs );
1379 AddPointToBounds( info->maxs, mins, maxs );
1380 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1381 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1382 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1388 if ( n < lm->numLightSurfaces ) {
1392 /* report bogus origin */
1393 Sys_Printf( "%6d [%2d,%2d] (%4d): XYZ(%+4.1f %+4.1f %+4.1f) LO(%+4.1f %+4.1f %+4.1f) HI(%+4.1f %+4.1f %+4.1f) <%3.0f>\n",
1394 rawLightmapNum, x, y, *cluster,
1395 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1396 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1397 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1408 sets up dirtmap (ambient occlusion)
1411 #define DIRT_CONE_ANGLE 88 /* degrees */
1412 #define DIRT_NUM_ANGLE_STEPS 16
1413 #define DIRT_NUM_ELEVATION_STEPS 3
1414 #define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1416 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1417 static int numDirtVectors = 0;
1419 void SetupDirt( void ){
1421 float angle, elevation, angleStep, elevationStep;
1425 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1427 /* calculate angular steps */
1428 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1429 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1433 for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1435 /* iterate elevation */
1436 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1438 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1439 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1440 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1445 /* emit some statistics */
1446 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1452 calculates dirt value for a given sample
1455 float DirtForSample( trace_t *trace ){
1457 float gatherDirt, outDirt, angle, elevation, ooDepth;
1458 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1465 if ( trace == NULL || trace->cluster < 0 ) {
1471 ooDepth = 1.0f / dirtDepth;
1472 VectorCopy( trace->normal, normal );
1474 /* check if the normal is aligned to the world-up */
1475 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
1476 if ( normal[ 2 ] == 1.0f ) {
1477 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1478 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1480 else if ( normal[ 2 ] == -1.0f ) {
1481 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1482 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1487 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1488 CrossProduct( normal, worldUp, myRt );
1489 VectorNormalize( myRt, myRt );
1490 CrossProduct( myRt, normal, myUp );
1491 VectorNormalize( myUp, myUp );
1494 /* 1 = random mode, 0 (well everything else) = non-random mode */
1495 if ( dirtMode == 1 ) {
1497 for ( i = 0; i < numDirtVectors; i++ )
1499 /* get random vector */
1500 angle = Random() * DEG2RAD( 360.0f );
1501 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1502 temp[ 0 ] = cos( angle ) * sin( elevation );
1503 temp[ 1 ] = sin( angle ) * sin( elevation );
1504 temp[ 2 ] = cos( elevation );
1506 /* transform into tangent space */
1507 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1508 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1509 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1512 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1513 SetupTrace( trace );
1514 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1518 if ( trace->opaque && !( trace->compileFlags & C_SKY ) ) {
1519 VectorSubtract( trace->hit, trace->origin, displacement );
1520 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1526 /* iterate through ordered vectors */
1527 for ( i = 0; i < numDirtVectors; i++ )
1529 /* transform vector into tangent space */
1530 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1531 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1532 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1535 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1536 SetupTrace( trace );
1537 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1541 if ( trace->opaque ) {
1542 VectorSubtract( trace->hit, trace->origin, displacement );
1543 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1549 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1550 SetupTrace( trace );
1551 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1555 if ( trace->opaque ) {
1556 VectorSubtract( trace->hit, trace->origin, displacement );
1557 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1561 if ( gatherDirt <= 0.0f ) {
1565 /* apply gain (does this even do much? heh) */
1566 outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1567 if ( outDirt > 1.0f ) {
1572 outDirt *= dirtScale;
1573 if ( outDirt > 1.0f ) {
1577 /* return to sender */
1578 return 1.0f - outDirt;
1585 calculates dirty fraction for each luxel
1588 void DirtyRawLightmap( int rawLightmapNum ){
1589 int i, x, y, sx, sy, *cluster;
1590 float *origin, *normal, *dirt, *dirt2, average, samples;
1592 surfaceInfo_t *info;
1597 /* bail if this number exceeds the number of raw lightmaps */
1598 if ( rawLightmapNum >= numRawLightmaps ) {
1603 lm = &rawLightmaps[ rawLightmapNum ];
1606 trace.testOcclusion = qtrue;
1607 trace.forceSunlight = qfalse;
1608 trace.recvShadows = lm->recvShadows;
1609 trace.numSurfaces = lm->numLightSurfaces;
1610 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1611 trace.inhibitRadius = 0.0f;
1612 trace.testAll = qfalse;
1614 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1615 trace.twoSided = qfalse;
1616 for ( i = 0; i < trace.numSurfaces; i++ )
1619 info = &surfaceInfos[ trace.surfaces[ i ] ];
1621 /* check twosidedness */
1622 if ( info->si->twoSided ) {
1623 trace.twoSided = qtrue;
1629 for ( i = 0; i < trace.numSurfaces; i++ )
1632 info = &surfaceInfos[ trace.surfaces[ i ] ];
1634 /* check twosidedness */
1635 if ( info->si->noDirty ) {
1642 for ( y = 0; y < lm->sh; y++ )
1644 for ( x = 0; x < lm->sw; x++ )
1647 cluster = SUPER_CLUSTER( x, y );
1648 origin = SUPER_ORIGIN( x, y );
1649 normal = SUPER_NORMAL( x, y );
1650 dirt = SUPER_DIRT( x, y );
1652 /* set default dirt */
1655 /* only look at mapped luxels */
1656 if ( *cluster < 0 ) {
1660 /* don't apply dirty on this surface */
1667 trace.cluster = *cluster;
1668 VectorCopy( origin, trace.origin );
1669 VectorCopy( normal, trace.normal );
1672 *dirt = DirtForSample( &trace );
1676 /* testing no filtering */
1680 for ( y = 0; y < lm->sh; y++ )
1682 for ( x = 0; x < lm->sw; x++ )
1685 cluster = SUPER_CLUSTER( x, y );
1686 dirt = SUPER_DIRT( x, y );
1688 /* filter dirt by adjacency to unmapped luxels */
1691 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1693 if ( sy < 0 || sy >= lm->sh ) {
1697 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1699 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1703 /* get neighboring luxel */
1704 cluster = SUPER_CLUSTER( sx, sy );
1705 dirt2 = SUPER_DIRT( sx, sy );
1706 if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1716 if ( samples <= 0.0f ) {
1722 if ( samples <= 0.0f ) {
1727 *dirt = average / samples;
1736 calculates the pvs cluster, origin, normal of a sub-luxel
1739 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1740 int i, *cluster, *cluster2;
1741 float *origin, *origin2, *normal; //% , *normal2;
1742 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1745 /* calulate x vector */
1746 if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1747 cluster = SUPER_CLUSTER( x, y );
1748 origin = SUPER_ORIGIN( x, y );
1749 //% normal = SUPER_NORMAL( x, y );
1750 cluster2 = SUPER_CLUSTER( x + 1, y );
1751 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1752 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1754 else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1755 cluster = SUPER_CLUSTER( x - 1, y );
1756 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1757 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1758 cluster2 = SUPER_CLUSTER( x, y );
1759 origin2 = SUPER_ORIGIN( x, y );
1760 //% normal2 = SUPER_NORMAL( x, y );
1764 Error( "Spurious lightmap S vector\n" );
1767 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1768 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1770 /* calulate y vector */
1771 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1772 cluster = SUPER_CLUSTER( x, y );
1773 origin = SUPER_ORIGIN( x, y );
1774 //% normal = SUPER_NORMAL( x, y );
1775 cluster2 = SUPER_CLUSTER( x, y + 1 );
1776 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1777 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1779 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1780 cluster = SUPER_CLUSTER( x, y - 1 );
1781 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1782 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1783 cluster2 = SUPER_CLUSTER( x, y );
1784 origin2 = SUPER_ORIGIN( x, y );
1785 //% normal2 = SUPER_NORMAL( x, y );
1788 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1791 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1792 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1794 /* calculate new origin */
1795 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1796 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1797 for ( i = 0; i < 3; i++ )
1798 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1801 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1802 if ( *sampleCluster < 0 ) {
1806 /* calculate new normal */
1807 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1808 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1809 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1811 normal = SUPER_NORMAL( x, y );
1812 VectorCopy( normal, sampleNormal );
1820 SubsampleRawLuxel_r()
1821 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1824 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1825 int b, samples, mapped, lighted;
1828 vec3_t deluxel[ 3 ];
1829 vec3_t origin[ 4 ], normal[ 4 ];
1830 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1831 vec3_t color, direction = { 0, 0, 0 }, total;
1835 if ( lightLuxel[ 3 ] >= lightSamples ) {
1840 VectorClear( total );
1844 /* make 2x2 subsample stamp */
1845 for ( b = 0; b < 4; b++ )
1848 VectorCopy( sampleOrigin, origin[ b ] );
1850 /* calculate position */
1851 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1857 /* increment sample count */
1858 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1861 trace->cluster = *cluster;
1862 VectorCopy( origin[ b ], trace->origin );
1863 VectorCopy( normal[ b ], trace->normal );
1867 LightContributionToSample( trace );
1868 if ( trace->forceSubsampling > 1.0f ) {
1869 /* alphashadow: we subsample as deep as we can */
1875 /* add to totals (fixme: make contrast function) */
1876 VectorCopy( trace->color, luxel[ b ] );
1877 if ( lightDeluxel ) {
1878 VectorCopy( trace->directionContribution, deluxel[ b ] );
1880 VectorAdd( total, trace->color, total );
1881 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1886 /* subsample further? */
1887 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1888 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1889 lighted != 0 && lighted != mapped ) {
1890 for ( b = 0; b < 4; b++ )
1892 if ( cluster[ b ] < 0 ) {
1895 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1900 //% VectorClear( color );
1902 VectorCopy( lightLuxel, color );
1903 if ( lightDeluxel ) {
1904 VectorCopy( lightDeluxel, direction );
1907 for ( b = 0; b < 4; b++ )
1909 if ( cluster[ b ] < 0 ) {
1912 VectorAdd( color, luxel[ b ], color );
1913 if ( lightDeluxel ) {
1914 VectorAdd( direction, deluxel[ b ], direction );
1920 if ( samples > 0 ) {
1922 color[ 0 ] /= samples;
1923 color[ 1 ] /= samples;
1924 color[ 2 ] /= samples;
1927 VectorCopy( color, lightLuxel );
1928 lightLuxel[ 3 ] += 1.0f;
1930 if ( lightDeluxel ) {
1931 direction[ 0 ] /= samples;
1932 direction[ 1 ] /= samples;
1933 direction[ 2 ] /= samples;
1934 VectorCopy( direction, lightDeluxel );
1939 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1940 static void GaussLikeRandom( float sigma, float *x, float *y ){
1942 r = Random() * 2 * Q_PI;
1943 *x = sigma * 2.73861278752581783822 * cos( r );
1944 *y = sigma * 2.73861278752581783822 * sin( r );
1951 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1954 vec3_t origin, normal;
1955 vec3_t total, totaldirection;
1958 VectorClear( total );
1959 VectorClear( totaldirection );
1961 for ( b = 0; b < lightSamples; ++b )
1964 VectorCopy( sampleOrigin, origin );
1965 GaussLikeRandom( bias, &dx, &dy );
1967 /* calculate position */
1968 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1974 trace->cluster = cluster;
1975 VectorCopy( origin, trace->origin );
1976 VectorCopy( normal, trace->normal );
1978 LightContributionToSample( trace );
1979 VectorAdd( total, trace->color, total );
1980 if ( lightDeluxel ) {
1981 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1988 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1989 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1990 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1992 if ( lightDeluxel ) {
1993 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1994 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1995 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
2003 IlluminateRawLightmap()
2004 illuminates the luxels
2007 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
2008 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
2009 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
2011 void IlluminateRawLightmap( int rawLightmapNum ){
2012 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
2013 int *cluster, *cluster2, mapped, lighted, totalLighted;
2014 size_t llSize, ldSize;
2016 surfaceInfo_t *info;
2017 qboolean filterColor, filterDir;
2019 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
2020 unsigned char *flag;
2021 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2022 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2023 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2025 float stackLightLuxels[ STACK_LL_SIZE ];
2028 /* bail if this number exceeds the number of raw lightmaps */
2029 if ( rawLightmapNum >= numRawLightmaps ) {
2034 lm = &rawLightmaps[ rawLightmapNum ];
2037 trace.testOcclusion = !noTrace;
2038 trace.forceSunlight = qfalse;
2039 trace.recvShadows = lm->recvShadows;
2040 trace.numSurfaces = lm->numLightSurfaces;
2041 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2042 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2044 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2045 trace.twoSided = qfalse;
2046 for ( i = 0; i < trace.numSurfaces; i++ )
2049 info = &surfaceInfos[ trace.surfaces[ i ] ];
2051 /* check twosidedness */
2052 if ( info->si->twoSided ) {
2053 trace.twoSided = qtrue;
2058 /* create a culled light list for this raw lightmap */
2059 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2061 /* -----------------------------------------------------------------
2063 ----------------------------------------------------------------- */
2066 numLuxelsIlluminated += ( lm->sw * lm->sh );
2068 /* test debugging state */
2069 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2070 /* debug fill the luxels */
2071 for ( y = 0; y < lm->sh; y++ )
2073 for ( x = 0; x < lm->sw; x++ )
2076 cluster = SUPER_CLUSTER( x, y );
2078 /* only fill mapped luxels */
2079 if ( *cluster < 0 ) {
2083 /* get particulars */
2084 luxel = SUPER_LUXEL( 0, x, y );
2085 origin = SUPER_ORIGIN( x, y );
2086 normal = SUPER_NORMAL( x, y );
2088 /* color the luxel with raw lightmap num? */
2089 if ( debugSurfaces ) {
2090 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2093 /* color the luxel with lightmap axis? */
2094 else if ( debugAxis ) {
2095 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2096 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2097 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2100 /* color the luxel with luxel cluster? */
2101 else if ( debugCluster ) {
2102 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2105 /* color the luxel with luxel origin? */
2106 else if ( debugOrigin ) {
2107 VectorSubtract( lm->maxs, lm->mins, temp );
2108 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2109 VectorSubtract( origin, lm->mins, temp2 );
2110 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2111 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2112 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2115 /* color the luxel with the normal */
2116 else if ( normalmap ) {
2117 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2118 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2119 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2122 /* otherwise clear it */
2124 VectorClear( luxel );
2134 /* allocate temporary per-light luxel storage */
2135 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2136 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2137 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2138 lightLuxels = stackLightLuxels;
2141 lightLuxels = safe_malloc( llSize );
2144 lightDeluxels = safe_malloc( ldSize );
2147 lightDeluxels = NULL;
2151 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2153 /* set ambient color */
2154 for ( y = 0; y < lm->sh; y++ )
2156 for ( x = 0; x < lm->sw; x++ )
2159 cluster = SUPER_CLUSTER( x, y );
2160 luxel = SUPER_LUXEL( 0, x, y );
2161 normal = SUPER_NORMAL( x, y );
2162 deluxel = SUPER_DELUXEL( x, y );
2164 /* blacken unmapped clusters */
2165 if ( *cluster < 0 ) {
2166 VectorClear( luxel );
2172 VectorCopy( ambientColor, luxel );
2174 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2176 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2177 if ( brightness < 0.00390625f ) {
2178 brightness = 0.00390625f;
2181 VectorScale( normal, brightness, deluxel );
2188 /* clear styled lightmaps */
2189 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2190 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2192 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2193 memset( lm->superLuxels[ lightmapNum ], 0, size );
2197 /* debugging code */
2198 //% if( trace.numLights <= 0 )
2199 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2201 /* walk light list */
2202 for ( i = 0; i < trace.numLights; i++ )
2205 trace.light = trace.lights[ i ];
2208 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2210 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2211 lm->styles[ lightmapNum ] == LS_NONE ) {
2216 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2217 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2218 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2223 memset( lightLuxels, 0, llSize );
2225 memset( lightDeluxels, 0, ldSize );
2229 /* determine filter radius */
2230 filterRadius = lm->filterRadius > trace.light->filterRadius
2232 : trace.light->filterRadius;
2233 if ( filterRadius < 0.0f ) {
2234 filterRadius = 0.0f;
2237 /* set luxel filter radius */
2238 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2239 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2240 luxelFilterRadius = 1;
2243 /* allocate sampling flags storage */
2244 if ( lightSamples > 1 || lightRandomSamples ) {
2245 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2246 if ( lm->superFlags == NULL ) {
2247 lm->superFlags = safe_malloc( size );
2249 memset( (void *) lm->superFlags, 0, size );
2252 /* initial pass, one sample per luxel */
2253 for ( y = 0; y < lm->sh; y++ )
2255 for ( x = 0; x < lm->sw; x++ )
2258 cluster = SUPER_CLUSTER( x, y );
2259 if ( *cluster < 0 ) {
2263 /* get particulars */
2264 lightLuxel = LIGHT_LUXEL( x, y );
2265 lightDeluxel = LIGHT_DELUXEL( x, y );
2266 origin = SUPER_ORIGIN( x, y );
2267 normal = SUPER_NORMAL( x, y );
2268 flag = SUPER_FLAG( x, y );
2271 ////////// 27's temp hack for testing edge clipping ////
2272 if ( origin[0] == 0 && origin[1] == 0 && origin[2] == 0 ) {
2273 lightLuxel[ 1 ] = 255;
2274 lightLuxel[ 3 ] = 1.0f;
2280 /* set contribution count */
2281 lightLuxel[ 3 ] = 1.0f;
2284 trace.cluster = *cluster;
2285 VectorCopy( origin, trace.origin );
2286 VectorCopy( normal, trace.normal );
2288 /* get light for this sample */
2289 LightContributionToSample( &trace );
2290 VectorCopy( trace.color, lightLuxel );
2292 /* add the contribution to the deluxemap */
2294 VectorCopy( trace.directionContribution, lightDeluxel );
2297 /* check for evilness */
2298 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) ) {
2300 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2303 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2310 /* don't even bother with everything else if nothing was lit */
2311 if ( totalLighted == 0 ) {
2315 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2316 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2317 if ( lightSamples > 1 || lightRandomSamples ) {
2319 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2321 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2326 VectorClear( total );
2328 /* test 2x2 stamp */
2329 for ( t = 0; t < 4; t++ )
2331 /* set sample coords */
2332 sx = x + tests[ t ][ 0 ];
2333 sy = y + tests[ t ][ 1 ];
2336 cluster = SUPER_CLUSTER( sx, sy );
2337 if ( *cluster < 0 ) {
2343 flag = SUPER_FLAG( sx, sy );
2344 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2345 /* force a lighted/mapped discrepancy so we subsample */
2350 lightLuxel = LIGHT_LUXEL( sx, sy );
2351 VectorAdd( total, lightLuxel, total );
2352 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2357 /* if total color is under a certain amount, then don't bother subsampling */
2358 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2362 /* if all 4 pixels are either in shadow or light, then don't subsample */
2363 if ( lighted != 0 && lighted != mapped ) {
2364 for ( t = 0; t < 4; t++ )
2366 /* set sample coords */
2367 sx = x + tests[ t ][ 0 ];
2368 sy = y + tests[ t ][ 1 ];
2371 cluster = SUPER_CLUSTER( sx, sy );
2372 if ( *cluster < 0 ) {
2375 flag = SUPER_FLAG( sx, sy );
2376 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2379 lightLuxel = LIGHT_LUXEL( sx, sy );
2380 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2381 origin = SUPER_ORIGIN( sx, sy );
2383 /* only subsample shadowed luxels */
2384 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2388 if ( lightRandomSamples ) {
2389 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2392 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2395 *flag |= FLAG_ALREADY_SUBSAMPLED;
2397 /* debug code to colorize subsampled areas to yellow */
2398 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2399 //% VectorSet( luxel, 255, 204, 0 );
2406 /* tertiary pass, apply dirt map (ambient occlusion) */
2409 for ( y = 0; y < lm->sh; y++ )
2411 for ( x = 0; x < lm->sw; x++ )
2414 cluster = SUPER_CLUSTER( x, y );
2415 if ( *cluster < 0 ) {
2419 /* get particulars */
2420 lightLuxel = LIGHT_LUXEL( x, y );
2421 dirt = SUPER_DIRT( x, y );
2423 /* scale light value */
2424 VectorScale( lightLuxel, *dirt, lightLuxel );
2429 /* allocate sampling lightmap storage */
2430 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2431 /* allocate sampling lightmap storage */
2432 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2433 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2434 memset( lm->superLuxels[ lightmapNum ], 0, size );
2438 if ( lightmapNum > 0 ) {
2439 lm->styles[ lightmapNum ] = trace.light->style;
2440 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2443 /* copy to permanent luxels */
2444 for ( y = 0; y < lm->sh; y++ )
2446 for ( x = 0; x < lm->sw; x++ )
2448 /* get cluster and origin */
2449 cluster = SUPER_CLUSTER( x, y );
2450 if ( *cluster < 0 ) {
2453 origin = SUPER_ORIGIN( x, y );
2456 if ( luxelFilterRadius ) {
2458 VectorClear( averageColor );
2459 VectorClear( averageDir );
2462 /* cheaper distance-based filtering */
2463 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2465 if ( sy < 0 || sy >= lm->sh ) {
2469 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2471 if ( sx < 0 || sx >= lm->sw ) {
2475 /* get particulars */
2476 cluster = SUPER_CLUSTER( sx, sy );
2477 if ( *cluster < 0 ) {
2480 lightLuxel = LIGHT_LUXEL( sx, sy );
2481 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2484 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2485 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2487 /* scale luxel by filter weight */
2488 VectorScale( lightLuxel, weight, color );
2489 VectorAdd( averageColor, color, averageColor );
2491 VectorScale( lightDeluxel, weight, direction );
2492 VectorAdd( averageDir, direction, averageDir );
2499 if ( samples <= 0.0f ) {
2503 /* scale into luxel */
2504 luxel = SUPER_LUXEL( lightmapNum, x, y );
2507 /* handle negative light */
2508 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2509 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2510 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2511 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2514 /* handle normal light */
2517 luxel[ 0 ] += averageColor[ 0 ] / samples;
2518 luxel[ 1 ] += averageColor[ 1 ] / samples;
2519 luxel[ 2 ] += averageColor[ 2 ] / samples;
2523 /* scale into luxel */
2524 deluxel = SUPER_DELUXEL( x, y );
2525 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2526 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2527 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2534 /* get particulars */
2535 lightLuxel = LIGHT_LUXEL( x, y );
2536 lightDeluxel = LIGHT_DELUXEL( x, y );
2537 luxel = SUPER_LUXEL( lightmapNum, x, y );
2538 deluxel = SUPER_DELUXEL( x, y );
2540 /* handle negative light */
2541 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2542 VectorScale( averageColor, -1.0f, averageColor );
2548 /* handle negative light */
2549 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2550 VectorSubtract( luxel, lightLuxel, luxel );
2553 /* handle normal light */
2555 VectorAdd( luxel, lightLuxel, luxel );
2559 VectorAdd( deluxel, lightDeluxel, deluxel );
2566 /* free temporary luxels */
2567 if ( lightLuxels != stackLightLuxels ) {
2568 free( lightLuxels );
2572 free( lightDeluxels );
2576 /* free light list */
2577 FreeTraceLights( &trace );
2579 /* floodlight pass */
2580 if ( floodlighty ) {
2581 FloodlightIlluminateLightmap( lm );
2584 if ( debugnormals ) {
2585 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2588 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2592 for ( y = 0; y < lm->sh; y++ )
2594 for ( x = 0; x < lm->sw; x++ )
2597 cluster = SUPER_CLUSTER( x, y );
2598 //% if( *cluster < 0 )
2601 /* get particulars */
2602 luxel = SUPER_LUXEL( lightmapNum, x, y );
2603 normal = SUPER_NORMAL( x, y );
2605 luxel[0] = ( normal[0] * 127 ) + 127;
2606 luxel[1] = ( normal[1] * 127 ) + 127;
2607 luxel[2] = ( normal[2] * 127 ) + 127;
2613 /* -----------------------------------------------------------------
2615 ----------------------------------------------------------------- */
2618 /* walk lightmaps */
2619 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2622 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2626 /* apply dirt to each luxel */
2627 for ( y = 0; y < lm->sh; y++ )
2629 for ( x = 0; x < lm->sw; x++ )
2632 cluster = SUPER_CLUSTER( x, y );
2633 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2636 /* get particulars */
2637 luxel = SUPER_LUXEL( lightmapNum, x, y );
2638 dirt = SUPER_DIRT( x, y );
2641 VectorScale( luxel, *dirt, luxel );
2645 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2652 /* -----------------------------------------------------------------
2654 ----------------------------------------------------------------- */
2656 /* walk lightmaps */
2657 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2660 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2664 /* average occluded luxels from neighbors */
2665 for ( y = 0; y < lm->sh; y++ )
2667 for ( x = 0; x < lm->sw; x++ )
2669 /* get particulars */
2670 cluster = SUPER_CLUSTER( x, y );
2671 luxel = SUPER_LUXEL( lightmapNum, x, y );
2672 deluxel = SUPER_DELUXEL( x, y );
2673 normal = SUPER_NORMAL( x, y );
2675 /* determine if filtering is necessary */
2676 filterColor = qfalse;
2678 if ( *cluster < 0 ||
2679 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2680 filterColor = qtrue;
2683 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2687 if ( !filterColor && !filterDir ) {
2691 /* choose seed amount */
2692 VectorClear( averageColor );
2693 VectorClear( averageDir );
2696 /* walk 3x3 matrix */
2697 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2699 if ( sy < 0 || sy >= lm->sh ) {
2703 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2705 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2709 /* get neighbor's particulars */
2710 cluster2 = SUPER_CLUSTER( sx, sy );
2711 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2712 deluxel2 = SUPER_DELUXEL( sx, sy );
2714 /* ignore unmapped/unlit luxels */
2715 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2716 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2720 /* add its distinctiveness to our own */
2721 VectorAdd( averageColor, luxel2, averageColor );
2722 samples += luxel2[ 3 ];
2724 VectorAdd( averageDir, deluxel2, averageDir );
2730 if ( samples <= 0.0f ) {
2734 /* dark lightmap seams */
2736 if ( lightmapNum == 0 ) {
2737 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2743 if ( filterColor ) {
2744 VectorDivide( averageColor, samples, luxel );
2748 VectorDivide( averageDir, samples, deluxel );
2751 /* set cluster to -3 */
2752 if ( *cluster < 0 ) {
2753 *cluster = CLUSTER_FLOODED;
2762 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2765 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2768 for ( y = 0; y < lm->sh; y++ )
2769 for ( x = 0; x < lm->sw; x++ )
2772 cluster = SUPER_CLUSTER( x, y );
2773 luxel = SUPER_LUXEL( lightmapNum, x, y );
2774 deluxel = SUPER_DELUXEL( x, y );
2775 if ( !luxel || !deluxel || !cluster ) {
2776 Sys_FPrintf( SYS_VRB, "WARNING: I got NULL'd.\n" );
2779 else if ( *cluster < 0 ) {
2781 // should have neither deluxemap nor lightmap
2783 Sys_FPrintf( SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n" );
2789 // should have both deluxemap and lightmap
2791 Sys_FPrintf( SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n" );
2802 IlluminateVertexes()
2803 light the surface vertexes
2806 #define VERTEX_NUDGE 4.0f
2808 void IlluminateVertexes( int num ){
2809 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2810 int lightmapNum, numAvg;
2811 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2812 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2813 bspDrawSurface_t *ds;
2814 surfaceInfo_t *info;
2816 bspDrawVert_t *verts;
2818 float floodLightAmount;
2822 /* get surface, info, and raw lightmap */
2823 ds = &bspDrawSurfaces[ num ];
2824 info = &surfaceInfos[ num ];
2827 /* -----------------------------------------------------------------
2828 illuminate the vertexes
2829 ----------------------------------------------------------------- */
2831 /* calculate vertex lighting for surfaces without lightmaps */
2832 if ( lm == NULL || cpmaHack ) {
2834 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2835 trace.forceSunlight = info->si->forceSunlight;
2836 trace.recvShadows = info->recvShadows;
2837 trace.numSurfaces = 1;
2838 trace.surfaces = #
2839 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2841 /* twosided lighting */
2842 trace.twoSided = info->si->twoSided;
2844 /* make light list for this surface */
2845 CreateTraceLightsForSurface( num, &trace );
2848 verts = yDrawVerts + ds->firstVert;
2850 memset( avgColors, 0, sizeof( avgColors ) );
2852 /* walk the surface verts */
2853 for ( i = 0; i < ds->numVerts; i++ )
2855 /* get vertex luxel */
2856 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2858 /* color the luxel with raw lightmap num? */
2859 if ( debugSurfaces ) {
2860 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2863 /* color the luxel with luxel origin? */
2864 else if ( debugOrigin ) {
2865 VectorSubtract( info->maxs, info->mins, temp );
2866 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2867 VectorSubtract( origin, lm->mins, temp2 );
2868 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2869 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2870 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2873 /* color the luxel with the normal */
2874 else if ( normalmap ) {
2875 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2876 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2877 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2880 else if ( info->si->noVertexLight ) {
2881 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
2884 else if ( noVertexLighting > 0 ) {
2885 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
2888 /* illuminate the vertex */
2891 /* clear vertex luxel */
2892 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2894 /* try at initial origin */
2895 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2896 if ( trace.cluster >= 0 ) {
2898 VectorCopy( verts[ i ].xyz, trace.origin );
2899 VectorCopy( verts[ i ].normal, trace.normal );
2902 if ( dirty && !bouncing ) {
2903 dirt = DirtForSample( &trace );
2909 /* jal: floodlight */
2910 floodLightAmount = 0.0f;
2911 VectorClear( floodColor );
2912 if ( floodlighty && !bouncing ) {
2913 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2914 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2918 LightingAtSample( &trace, ds->vertexStyles, colors );
2921 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2924 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2926 /* jal: floodlight */
2927 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2930 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2931 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2932 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2936 /* is this sample bright enough? */
2937 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2938 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2939 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2940 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2941 /* nudge the sample point around a bit */
2942 for ( x = 0; x < 5; x++ )
2944 /* two's complement 0, 1, -1, 2, -2, etc */
2945 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2947 for ( y = 0; y < 5; y++ )
2949 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2951 for ( z = 0; z < 5; z++ )
2953 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2956 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2957 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2958 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2960 /* try at nudged origin */
2961 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2962 if ( trace.cluster < 0 ) {
2967 if ( dirty && !bouncing ) {
2968 dirt = DirtForSample( &trace );
2974 /* jal: floodlight */
2975 floodLightAmount = 0.0f;
2976 VectorClear( floodColor );
2977 if ( floodlighty && !bouncing ) {
2978 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2979 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2983 LightingAtSample( &trace, ds->vertexStyles, colors );
2986 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2989 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2991 /* jal: floodlight */
2992 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2995 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2996 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2999 /* bright enough? */
3000 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
3001 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
3002 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
3003 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
3011 /* add to average? */
3012 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
3013 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
3014 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
3015 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
3017 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3019 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3020 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
3025 /* another happy customer */
3026 numVertsIlluminated++;
3029 /* set average color */
3031 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3032 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
3036 VectorCopy( ambientColor, avgColors[ 0 ] );
3039 /* clean up and store vertex color */
3040 for ( i = 0; i < ds->numVerts; i++ )
3042 /* get vertex luxel */
3043 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
3045 /* store average in occluded vertexes */
3046 if ( radVertLuxel[ 0 ] < 0.0f ) {
3047 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3049 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3050 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
3053 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
3058 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3061 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3062 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3065 if ( bouncing || bounce == 0 || !bounceOnly ) {
3066 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3068 if ( !info->si->noVertexLight ) {
3069 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3074 /* free light list */
3075 FreeTraceLights( &trace );
3077 /* return to sender */
3081 /* -----------------------------------------------------------------
3082 reconstitute vertex lighting from the luxels
3083 ----------------------------------------------------------------- */
3085 /* set styles from lightmap */
3086 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3087 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3089 /* get max search radius */
3091 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3093 /* walk the surface verts */
3094 verts = yDrawVerts + ds->firstVert;
3095 for ( i = 0; i < ds->numVerts; i++ )
3097 /* do each lightmap */
3098 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3101 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3105 /* get luxel coords */
3106 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3107 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3111 else if ( x >= lm->sw ) {
3117 else if ( y >= lm->sh ) {
3121 /* get vertex luxels */
3122 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3123 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3125 /* color the luxel with the normal? */
3127 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3128 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3129 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3132 /* color the luxel with surface num? */
3133 else if ( debugSurfaces ) {
3134 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3137 else if ( info->si->noVertexLight ) {
3138 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
3141 else if ( noVertexLighting > 0 ) {
3142 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
3145 /* divine color from the superluxels */
3148 /* increasing radius */
3149 VectorClear( radVertLuxel );
3151 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3153 /* sample within radius */
3154 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3156 if ( sy < 0 || sy >= lm->sh ) {
3160 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3162 if ( sx < 0 || sx >= lm->sw ) {
3166 /* get luxel particulars */
3167 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3168 cluster = SUPER_CLUSTER( sx, sy );
3169 if ( *cluster < 0 ) {
3173 /* testing: must be brigher than ambient color */
3174 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3177 /* add its distinctiveness to our own */
3178 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3179 samples += luxel[ 3 ];
3185 if ( samples > 0.0f ) {
3186 VectorDivide( radVertLuxel, samples, radVertLuxel );
3189 VectorCopy( ambientColor, radVertLuxel );
3193 /* store into floating point storage */
3194 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3195 numVertsIlluminated++;
3197 /* store into bytes (for vertex approximation) */
3198 if ( !info->si->noVertexLight ) {
3199 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3207 /* -------------------------------------------------------------------------------
3209 light optimization (-fast)
3211 creates a list of lights that will affect a surface and stores it in tw
3212 this is to optimize surface lighting by culling out as many of the
3213 lights in the world as possible from further calculation
3215 ------------------------------------------------------------------------------- */
3219 determines opaque brushes in the world and find sky shaders for sunlight calculations
3222 void SetupBrushesFlags( int mask_any, int test_any, int mask_all, int test_all ){
3224 unsigned int compileFlags, allCompileFlags;
3227 bspBrushSide_t *side;
3228 bspShader_t *shader;
3233 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3236 if ( opaqueBrushes == NULL ) {
3237 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3241 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3242 numOpaqueBrushes = 0;
3244 /* walk the list of worldspawn brushes */
3245 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3248 b = bspModels[ 0 ].firstBSPBrush + i;
3249 brush = &bspBrushes[ b ];
3251 /* check all sides */
3254 allCompileFlags = ~( 0u );
3255 for ( j = 0; j < brush->numSides && inside; j++ )
3257 /* do bsp shader calculations */
3258 side = &bspBrushSides[ brush->firstSide + j ];
3259 shader = &bspShaders[ side->shaderNum ];
3261 /* get shader info */
3262 si = ShaderInfoForShaderNull( shader->shader );
3267 /* or together compile flags */
3268 compileFlags |= si->compileFlags;
3269 allCompileFlags &= si->compileFlags;
3272 /* determine if this brush is opaque to light */
3273 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3274 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3280 /* emit some statistics */
3281 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3283 void SetupBrushes( void ){
3284 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3291 determines if two clusters are visible to each other using the PVS
3294 qboolean ClusterVisible( int a, int b ){
3300 if ( a < 0 || b < 0 ) {
3310 if ( numBSPVisBytes <= 8 ) {
3315 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3316 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3317 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3320 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3330 borrowed from vlight.c
3333 int PointInLeafNum_r( vec3_t point, int nodenum ){
3340 while ( nodenum >= 0 )
3342 node = &bspNodes[ nodenum ];
3343 plane = &bspPlanes[ node->planeNum ];
3344 dist = DotProduct( point, plane->normal ) - plane->dist;
3346 nodenum = node->children[ 0 ];
3348 else if ( dist < -0.1 ) {
3349 nodenum = node->children[ 1 ];
3353 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3354 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3357 nodenum = node->children[ 1 ];
3361 leafnum = -nodenum - 1;
3369 borrowed from vlight.c
3372 int PointInLeafNum( vec3_t point ){
3373 return PointInLeafNum_r( point, 0 );
3379 ClusterVisibleToPoint() - ydnar
3380 returns qtrue if point can "see" cluster
3383 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3387 /* get leafNum for point */
3388 pointCluster = ClusterForPoint( point );
3389 if ( pointCluster < 0 ) {
3394 return ClusterVisible( pointCluster, cluster );
3400 ClusterForPoint() - ydnar
3401 returns the pvs cluster for point
3404 int ClusterForPoint( vec3_t point ){
3408 /* get leafNum for point */
3409 leafNum = PointInLeafNum( point );
3410 if ( leafNum < 0 ) {
3414 /* return the cluster */
3415 return bspLeafs[ leafNum ].cluster;
3421 ClusterForPointExt() - ydnar
3422 also takes brushes into account for occlusion testing
3425 int ClusterForPointExt( vec3_t point, float epsilon ){
3426 int i, j, b, leafNum, cluster;
3429 int *brushes, numBSPBrushes;
3435 /* get leaf for point */
3436 leafNum = PointInLeafNum( point );
3437 if ( leafNum < 0 ) {
3440 leaf = &bspLeafs[ leafNum ];
3442 /* get the cluster */
3443 cluster = leaf->cluster;
3444 if ( cluster < 0 ) {
3448 /* transparent leaf, so check point against all brushes in the leaf */
3449 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3450 numBSPBrushes = leaf->numBSPLeafBrushes;
3451 for ( i = 0; i < numBSPBrushes; i++ )
3455 if ( b > maxOpaqueBrush ) {
3458 brush = &bspBrushes[ b ];
3459 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3463 /* check point against all planes */
3465 for ( j = 0; j < brush->numSides && inside; j++ )
3467 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3468 dot = DotProduct( point, plane->normal );
3470 if ( dot > epsilon ) {
3475 /* if inside, return bogus cluster */
3481 /* if the point made it this far, it's not inside any opaque brushes */
3488 ClusterForPointExtFilter() - ydnar
3489 adds cluster checking against a list of known valid clusters
3492 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3496 /* get cluster for point */
3497 cluster = ClusterForPointExt( point, epsilon );
3499 /* check if filtering is necessary */
3500 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3505 for ( i = 0; i < numClusters; i++ )
3507 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3519 ShaderForPointInLeaf() - ydnar
3520 checks a point against all brushes in a leaf, returning the shader of the brush
3521 also sets the cumulative surface and content flags for the brush hit
3524 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3528 int *brushes, numBSPBrushes;
3531 bspBrushSide_t *side;
3533 bspShader_t *shader;
3534 int allSurfaceFlags, allContentFlags;
3537 /* clear things out first */
3542 if ( leafNum < 0 ) {
3545 leaf = &bspLeafs[ leafNum ];
3547 /* transparent leaf, so check point against all brushes in the leaf */
3548 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3549 numBSPBrushes = leaf->numBSPLeafBrushes;
3550 for ( i = 0; i < numBSPBrushes; i++ )
3553 brush = &bspBrushes[ brushes[ i ] ];
3555 /* check point against all planes */
3557 allSurfaceFlags = 0;
3558 allContentFlags = 0;
3559 for ( j = 0; j < brush->numSides && inside; j++ )
3561 side = &bspBrushSides[ brush->firstSide + j ];
3562 plane = &bspPlanes[ side->planeNum ];
3563 dot = DotProduct( point, plane->normal );
3565 if ( dot > epsilon ) {
3570 shader = &bspShaders[ side->shaderNum ];
3571 allSurfaceFlags |= shader->surfaceFlags;
3572 allContentFlags |= shader->contentFlags;
3576 /* handle if inside */
3578 /* if there are desired flags, check for same and continue if they aren't matched */
3579 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3582 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3586 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3587 *surfaceFlags = allSurfaceFlags;
3588 *contentFlags = allContentFlags;
3589 return brush->shaderNum;
3593 /* if the point made it this far, it's not inside any brushes */
3601 chops a bounding box by the plane defined by origin and normal
3602 returns qfalse if the bounds is entirely clipped away
3604 this is not exactly the fastest way to do this...
3607 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3608 /* FIXME: rewrite this so it doesn't use bloody brushes */
3616 calculates each light's effective envelope,
3617 taking into account brightness, type, and pvs.
3620 #define LIGHT_EPSILON 0.125f
3621 #define LIGHT_NUDGE 2.0f
3623 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3624 int i, x, y, z, x1, y1, z1;
3625 light_t *light, *light2, **owner;
3627 vec3_t origin, dir, mins, maxs;
3628 float radius, intensity;
3629 light_t *buckets[ 256 ];
3632 /* early out for weird cases where there are no lights */
3633 if ( lights == NULL ) {
3638 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3642 numCulledLights = 0;
3644 while ( *owner != NULL )
3649 /* handle negative lights */
3650 if ( light->photons < 0.0f || light->add < 0.0f ) {
3651 light->photons *= -1.0f;
3652 light->add *= -1.0f;
3653 light->flags |= LIGHT_NEGATIVE;
3657 if ( light->type == EMIT_SUN ) {
3660 light->envelope = MAX_WORLD_COORD * 8.0f;
3661 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3662 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3665 /* everything else */
3668 /* get pvs cluster for light */
3669 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3671 /* invalid cluster? */
3672 if ( light->cluster < 0 ) {
3673 /* nudge the sample point around a bit */
3674 for ( x = 0; x < 4; x++ )
3676 /* two's complement 0, 1, -1, 2, -2, etc */
3677 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3679 for ( y = 0; y < 4; y++ )
3681 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3683 for ( z = 0; z < 4; z++ )
3685 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3688 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3689 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3690 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3692 /* try at nudged origin */
3693 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3694 if ( light->cluster < 0 ) {
3699 VectorCopy( origin, light->origin );
3705 /* only calculate for lights in pvs and outside of opaque brushes */
3706 if ( light->cluster >= 0 ) {
3707 /* set light fast flag */
3709 light->flags |= LIGHT_FAST_TEMP;
3712 light->flags &= ~LIGHT_FAST_TEMP;
3714 if ( fastpoint && ( light->flags != EMIT_AREA ) ) {
3715 light->flags |= LIGHT_FAST_TEMP;
3717 if ( light->si && light->si->noFast ) {
3718 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3721 /* clear light envelope */
3722 light->envelope = 0;
3724 /* handle area lights */
3725 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3726 light->envelope = MAX_WORLD_COORD * 8.0f;
3728 /* check for fast mode */
3729 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3730 /* ugly hack to calculate extent for area lights, but only done once */
3731 VectorScale( light->normal, -1.0f, dir );
3732 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3736 VectorMA( light->origin, radius, light->normal, origin );
3737 factor = PointToPolygonFormFactor( origin, dir, light->w );
3738 if ( factor < 0.0f ) {
3741 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3742 light->envelope = radius;
3748 intensity = light->photons; /* hopefully not used */
3753 intensity = light->photons;
3757 if ( light->envelope <= 0.0f ) {
3758 /* solve distance for non-distance lights */
3759 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3760 light->envelope = MAX_WORLD_COORD * 8.0f;
3763 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3764 /* solve distance for linear lights */
3765 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3766 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3770 add = angle * light->photons * linearScale - (dist * light->fade);
3771 T = (light->photons * linearScale) - (dist * light->fade);
3772 T + (dist * light->fade) = (light->photons * linearScale);
3773 dist * light->fade = (light->photons * linearScale) - T;
3774 dist = ((light->photons * linearScale) - T) / light->fade;
3777 /* solve for inverse square falloff */
3779 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3783 add = light->photons / (dist * dist);
3784 T = light->photons / (dist * dist);
3785 T * (dist * dist) = light->photons;
3786 dist = sqrt( light->photons / T );
3791 /* solve distance for linear lights */
3792 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3793 light->envelope = ( intensity * linearScale ) / light->fade;
3796 /* can't cull these */
3798 light->envelope = MAX_WORLD_COORD * 8.0f;
3803 /* chop radius against pvs */
3806 ClearBounds( mins, maxs );
3808 /* check all leaves */
3809 for ( i = 0; i < numBSPLeafs; i++ )
3812 leaf = &bspLeafs[ i ];
3815 if ( leaf->cluster < 0 ) {
3818 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3822 /* add this leafs bbox to the bounds */
3823 VectorCopy( leaf->mins, origin );
3824 AddPointToBounds( origin, mins, maxs );
3825 VectorCopy( leaf->maxs, origin );
3826 AddPointToBounds( origin, mins, maxs );
3829 /* test to see if bounds encompass light */
3830 for ( i = 0; i < 3; i++ )
3832 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3833 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3834 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3835 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3836 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3837 AddPointToBounds( light->origin, mins, maxs );
3841 /* chop the bounds by a plane for area lights and spotlights */
3842 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3843 ChopBounds( mins, maxs, light->origin, light->normal );
3847 VectorCopy( mins, light->mins );
3848 VectorCopy( maxs, light->maxs );
3850 /* reflect bounds around light origin */
3851 //% VectorMA( light->origin, -1.0f, origin, origin );
3852 VectorScale( light->origin, 2, origin );
3853 VectorSubtract( origin, maxs, origin );
3854 AddPointToBounds( origin, mins, maxs );
3855 //% VectorMA( light->origin, -1.0f, mins, origin );
3856 VectorScale( light->origin, 2, origin );
3857 VectorSubtract( origin, mins, origin );
3858 AddPointToBounds( origin, mins, maxs );
3860 /* calculate spherical bounds */
3861 VectorSubtract( maxs, light->origin, dir );
3862 radius = (float) VectorLength( dir );
3864 /* if this radius is smaller than the envelope, then set the envelope to it */
3865 if ( radius < light->envelope ) {
3866 light->envelope = radius;
3867 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3870 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3873 /* add grid/surface only check */
3875 if ( !( light->flags & LIGHT_GRID ) ) {
3876 light->envelope = 0.0f;
3881 if ( !( light->flags & LIGHT_SURFACES ) ) {
3882 light->envelope = 0.0f;
3888 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3890 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3892 /* delete the light */
3894 *owner = light->next;
3895 if ( light->w != NULL ) {
3903 /* square envelope */
3904 light->envelope2 = ( light->envelope * light->envelope );
3906 /* increment light count */
3909 /* set next light */
3910 owner = &( ( **owner ).next );
3913 /* bucket sort lights by style */
3914 memset( buckets, 0, sizeof( buckets ) );
3916 for ( light = lights; light != NULL; light = light2 )
3918 /* get next light */
3919 light2 = light->next;
3921 /* filter into correct bucket */
3922 light->next = buckets[ light->style ];
3923 buckets[ light->style ] = light;
3925 /* if any styled light is present, automatically set nocollapse */
3926 if ( light->style != LS_NORMAL ) {
3931 /* filter back into light list */
3933 for ( i = 255; i >= 0; i-- )
3936 for ( light = buckets[ i ]; light != NULL; light = light2 )
3938 light2 = light->next;
3939 light->next = lights;
3944 /* emit some statistics */
3945 Sys_Printf( "%9d total lights\n", numLights );
3946 Sys_Printf( "%9d culled lights\n", numCulledLights );
3952 CreateTraceLightsForBounds()
3953 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3956 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3959 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3960 float radius, dist, length;
3963 /* potential pre-setup */
3964 if ( numLights == 0 ) {
3965 SetupEnvelopes( qfalse, fast );
3969 //% 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 ] );
3971 /* allocate the light list */
3972 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3973 trace->numLights = 0;
3975 /* calculate spherical bounds */
3976 VectorAdd( mins, maxs, origin );
3977 VectorScale( origin, 0.5f, origin );
3978 VectorSubtract( maxs, origin, dir );
3979 radius = (float) VectorLength( dir );
3981 /* get length of normal vector */
3982 if ( normal != NULL ) {
3983 length = VectorLength( normal );
3987 normal = nullVector;
3991 /* test each light and see if it reaches the sphere */
3992 /* note: the attenuation code MUST match LightingAtSample() */
3993 for ( light = lights; light; light = light->next )
3995 /* check zero sized envelope */
3996 if ( light->envelope <= 0 ) {
3997 lightsEnvelopeCulled++;
4002 if ( !( light->flags & flags ) ) {
4006 /* sunlight skips all this nonsense */
4007 if ( light->type != EMIT_SUN ) {
4013 /* check against pvs cluster */
4014 if ( numClusters > 0 && clusters != NULL ) {
4015 for ( i = 0; i < numClusters; i++ )
4017 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
4023 if ( i == numClusters ) {
4024 lightsClusterCulled++;
4029 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
4030 VectorSubtract( light->origin, origin, dir );
4031 dist = VectorLength( dir );
4032 dist -= light->envelope;
4035 lightsEnvelopeCulled++;
4039 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
4042 for ( i = 0; i < 3; i++ )
4044 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
4049 lightsBoundsCulled++;
4055 /* planar surfaces (except twosided surfaces) have a couple more checks */
4056 if ( length > 0.0f && trace->twoSided == qfalse ) {
4057 /* lights coplanar with a surface won't light it */
4058 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
4059 lightsPlaneCulled++;
4063 /* check to see if light is behind the plane */
4064 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
4065 lightsPlaneCulled++;
4070 /* add this light */
4071 trace->lights[ trace->numLights++ ] = light;
4074 /* make last night null */
4075 trace->lights[ trace->numLights ] = NULL;
4080 void FreeTraceLights( trace_t *trace ){
4081 if ( trace->lights != NULL ) {
4082 free( trace->lights );
4089 CreateTraceLightsForSurface()
4090 creates a list of lights that can potentially affect a drawsurface
4093 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4095 vec3_t mins, maxs, normal;
4097 bspDrawSurface_t *ds;
4098 surfaceInfo_t *info;
4106 /* get drawsurface and info */
4107 ds = &bspDrawSurfaces[ num ];
4108 info = &surfaceInfos[ num ];
4110 /* get the mins/maxs for the dsurf */
4111 ClearBounds( mins, maxs );
4112 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4113 for ( i = 0; i < ds->numVerts; i++ )
4115 dv = &yDrawVerts[ ds->firstVert + i ];
4116 AddPointToBounds( dv->xyz, mins, maxs );
4117 if ( !VectorCompare( dv->normal, normal ) ) {
4118 VectorClear( normal );
4122 /* create the lights for the bounding box */
4123 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4126 /////////////////////////////////////////////////////////////
4128 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4129 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4130 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4131 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4133 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4134 static int numFloodVectors = 0;
4136 void SetupFloodLight( void ){
4138 float angle, elevation, angleStep, elevationStep;
4140 double v1,v2,v3,v4,v5,v6;
4143 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4145 /* calculate angular steps */
4146 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4147 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4151 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4153 /* iterate elevation */
4154 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4156 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4157 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4158 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4163 /* emit some statistics */
4164 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4167 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4169 if ( value[ 0 ] != '\0' ) {
4171 v4 = floodlightDistance;
4172 v5 = floodlightIntensity;
4173 v6 = floodlightDirectionScale;
4175 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4177 floodlightRGB[0] = v1;
4178 floodlightRGB[1] = v2;
4179 floodlightRGB[2] = v3;
4181 if ( VectorLength( floodlightRGB ) == 0 ) {
4182 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4195 floodlightDistance = v4;
4196 floodlightIntensity = v5;
4197 floodlightDirectionScale = v6;
4199 floodlighty = qtrue;
4200 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4204 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4207 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4208 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4209 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4211 ColorNormalize( floodlightRGB,floodlightRGB );
4215 FloodLightForSample()
4216 calculates floodlight value for a given sample
4217 once again, kudos to the dirtmapping coder
4220 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4225 float gatherLight, outLight;
4226 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4234 if ( trace == NULL || trace->cluster < 0 ) {
4240 dd = floodLightDistance;
4241 VectorCopy( trace->normal, normal );
4243 /* check if the normal is aligned to the world-up */
4244 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4245 if ( normal[ 2 ] == 1.0f ) {
4246 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4247 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4249 else if ( normal[ 2 ] == -1.0f ) {
4250 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4251 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4256 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4257 CrossProduct( normal, worldUp, myRt );
4258 VectorNormalize( myRt, myRt );
4259 CrossProduct( myRt, normal, myUp );
4260 VectorNormalize( myUp, myUp );
4263 /* vortex: optimise floodLightLowQuality a bit */
4264 if ( floodLightLowQuality == qtrue ) {
4265 /* iterate through ordered vectors */
4266 for ( i = 0; i < numFloodVectors; i++ )
4267 if ( rand() % 10 != 0 ) {
4273 /* iterate through ordered vectors */
4274 for ( i = 0; i < numFloodVectors; i++ )
4278 /* transform vector into tangent space */
4279 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4280 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4281 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4284 VectorMA( trace->origin, dd, direction, trace->end );
4286 //VectorMA( trace->origin, 1, direction, trace->origin );
4288 SetupTrace( trace );
4289 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4294 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4295 contribution = 1.0f;
4297 else if ( trace->opaque ) {
4298 VectorSubtract( trace->hit, trace->origin, displacement );
4299 d = VectorLength( displacement );
4301 // d=trace->distance;
4302 //if (d>256) gatherDirt+=1;
4303 contribution = d / dd;
4304 if ( contribution > 1 ) {
4305 contribution = 1.0f;
4308 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4311 gatherLight += contribution;
4316 if ( gatherLight <= 0.0f ) {
4325 gatherLight /= ( sub );
4327 outLight = gatherLight;
4328 if ( outLight > 1.0f ) {
4332 /* return to sender */
4337 FloodLightRawLightmap
4338 lighttracer style ambient occlusion light hack.
4339 Kudos to the dirtmapping author for most of this source.
4340 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4341 VorteX: fixed problems with deluxemapping
4344 // floodlight pass on a lightmap
4345 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4346 int i, x, y, *cluster;
4347 float *origin, *normal, *floodlight, floodLightAmount;
4348 surfaceInfo_t *info;
4351 // float samples, average, *floodlight2;
4353 memset( &trace,0,sizeof( trace_t ) );
4356 trace.testOcclusion = qtrue;
4357 trace.forceSunlight = qfalse;
4358 trace.twoSided = qtrue;
4359 trace.recvShadows = lm->recvShadows;
4360 trace.numSurfaces = lm->numLightSurfaces;
4361 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4362 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4363 trace.testAll = qfalse;
4364 trace.distance = 1024;
4366 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4367 //trace.twoSided = qfalse;
4368 for ( i = 0; i < trace.numSurfaces; i++ )
4371 info = &surfaceInfos[ trace.surfaces[ i ] ];
4373 /* check twosidedness */
4374 if ( info->si->twoSided ) {
4375 trace.twoSided = qtrue;
4380 /* gather floodlight */
4381 for ( y = 0; y < lm->sh; y++ )
4383 for ( x = 0; x < lm->sw; x++ )
4386 cluster = SUPER_CLUSTER( x, y );
4387 origin = SUPER_ORIGIN( x, y );
4388 normal = SUPER_NORMAL( x, y );
4389 floodlight = SUPER_FLOODLIGHT( x, y );
4391 /* set default dirt */
4394 /* only look at mapped luxels */
4395 if ( *cluster < 0 ) {
4400 trace.cluster = *cluster;
4401 VectorCopy( origin, trace.origin );
4402 VectorCopy( normal, trace.normal );
4404 /* get floodlight */
4405 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4407 /* add floodlight */
4408 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4409 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4410 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4411 floodlight[3] += floodlightDirectionScale;
4415 /* testing no filtering */
4421 for ( y = 0; y < lm->sh; y++ )
4423 for ( x = 0; x < lm->sw; x++ )
4426 cluster = SUPER_CLUSTER( x, y );
4427 floodlight = SUPER_FLOODLIGHT( x, y );
4429 /* filter dirt by adjacency to unmapped luxels */
4430 average = *floodlight;
4432 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4434 if ( sy < 0 || sy >= lm->sh ) {
4438 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4440 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4444 /* get neighboring luxel */
4445 cluster = SUPER_CLUSTER( sx, sy );
4446 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4447 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4452 average += *floodlight2;
4457 if ( samples <= 0.0f ) {
4463 if ( samples <= 0.0f ) {
4468 *floodlight = average / samples;
4474 void FloodLightRawLightmap( int rawLightmapNum ){
4477 /* bail if this number exceeds the number of raw lightmaps */
4478 if ( rawLightmapNum >= numRawLightmaps ) {
4482 lm = &rawLightmaps[ rawLightmapNum ];
4485 if ( floodlighty && floodlightIntensity ) {
4486 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4490 if ( lm->floodlightIntensity ) {
4491 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4492 numSurfacesFloodlighten += 1;
4496 void FloodlightRawLightmaps(){
4497 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4498 numSurfacesFloodlighten = 0;
4499 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4500 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4504 FloodLightIlluminate()
4505 illuminate floodlight into lightmap luxels
4508 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4509 float *luxel, *floodlight, *deluxel, *normal;
4512 int x, y, lightmapNum;
4514 /* walk lightmaps */
4515 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4518 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4522 /* apply floodlight to each luxel */
4523 for ( y = 0; y < lm->sh; y++ )
4525 for ( x = 0; x < lm->sw; x++ )
4527 /* get floodlight */
4528 floodlight = SUPER_FLOODLIGHT( x, y );
4529 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4534 cluster = SUPER_CLUSTER( x, y );
4536 /* only process mapped luxels */
4537 if ( *cluster < 0 ) {
4541 /* get particulars */
4542 luxel = SUPER_LUXEL( lightmapNum, x, y );
4543 deluxel = SUPER_DELUXEL( x, y );
4545 /* add to lightmap */
4546 luxel[0] += floodlight[0];
4547 luxel[1] += floodlight[1];
4548 luxel[2] += floodlight[2];
4550 if ( luxel[3] == 0 ) {
4554 /* add to deluxemap */
4555 if ( deluxemap && floodlight[3] > 0 ) {
4558 normal = SUPER_NORMAL( x, y );
4559 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4561 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4562 if ( brightness < 0.00390625f ) {
4563 brightness = 0.00390625f;
4566 VectorScale( normal, brightness, lightvector );
4567 VectorAdd( deluxel, lightvector, deluxel );