1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale )
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if( sample[ i ] < 0.0f )
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if (lightmapExposure == 1)
79 /* clamp with color normalization */
81 if( sample[ 1 ] > max )
83 if( sample[ 2 ] > max )
86 VectorScale( sample, (255.0f / max), sample );
90 if (lightmapExposure==0)
92 lightmapExposure=1.0f;
94 inv=1.f/lightmapExposure;
98 if( sample[ 1 ] > max )
100 if( sample[ 2 ] > max )
103 dif = (1- exp(-max * inv) ) * 255;
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, (1.0f / lightmapCompensate), sample );
125 colorBytes[ 0 ] = sample[ 0 ];
126 colorBytes[ 1 ] = sample[ 1 ];
127 colorBytes[ 2 ] = sample[ 2 ];
132 /* -------------------------------------------------------------------------------
134 this section deals with phong shading (normal interpolation across brush faces)
136 ------------------------------------------------------------------------------- */
140 smooths together coincident vertex normals across the bsp
143 #define MAX_SAMPLES 256
144 #define THETA_EPSILON 0.000001
145 #define EQUAL_NORMAL_EPSILON 0.01
147 void SmoothNormals( void )
149 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
150 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
151 bspDrawSurface_t *ds;
155 vec3_t average, diff;
156 int indexes[ MAX_SAMPLES ];
157 vec3_t votes[ MAX_SAMPLES ];
160 /* allocate shade angle table */
161 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
162 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
164 /* allocate smoothed table */
165 cs = (numBSPDrawVerts / 8) + 1;
166 smoothed = safe_malloc( cs );
167 memset( smoothed, 0, cs );
169 /* set default shade angle */
170 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
173 /* run through every surface and flag verts belonging to non-lightmapped surfaces
174 and set per-vertex smoothing angle */
175 for( i = 0; i < numBSPDrawSurfaces; i++ )
178 ds = &bspDrawSurfaces[ i ];
180 /* get shader for shade angle */
181 si = surfaceInfos[ i ].si;
182 if( si->shadeAngleDegrees )
183 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
185 shadeAngle = defaultShadeAngle;
186 if( shadeAngle > maxShadeAngle )
187 maxShadeAngle = shadeAngle;
190 for( j = 0; j < ds->numVerts; j++ )
192 f = ds->firstVert + j;
193 shadeAngles[ f ] = shadeAngle;
194 if( ds->surfaceType == MST_TRIANGLE_SOUP )
195 smoothed[ f >> 3 ] |= (1 << (f & 7));
198 /* ydnar: optional force-to-trisoup */
199 if( trisoup && ds->surfaceType == MST_PLANAR )
201 ds->surfaceType = MST_TRIANGLE_SOUP;
202 ds->lightmapNum[ 0 ] = -3;
206 /* bail if no surfaces have a shade angle */
207 if( maxShadeAngle == 0 )
216 start = I_FloatTime();
218 /* go through the list of vertexes */
219 for( i = 0; i < numBSPDrawVerts; i++ )
222 f = 10 * i / numBSPDrawVerts;
226 Sys_Printf( "%i...", f );
229 /* already smoothed? */
230 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
234 VectorClear( average );
238 /* build a table of coincident vertexes */
239 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
241 /* already smoothed? */
242 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
246 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
249 /* use smallest shade angle */
250 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
252 /* check shade angle */
253 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
256 else if( dot < -1.0 )
258 testAngle = acos( dot ) + THETA_EPSILON;
259 if( testAngle >= shadeAngle )
261 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
264 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
266 /* add to the list */
267 indexes[ numVerts++ ] = j;
270 smoothed[ j >> 3 ] |= (1 << (j & 7));
272 /* see if this normal has already been voted */
273 for( k = 0; k < numVotes; k++ )
275 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
276 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
277 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
278 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
282 /* add a new vote? */
283 if( k == numVotes && numVotes < MAX_SAMPLES )
285 VectorAdd( average, bspDrawVerts[ j ].normal, average );
286 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
291 /* don't average for less than 2 verts */
296 if( VectorNormalize( average, average ) > 0 )
299 for( j = 0; j < numVerts; j++ )
300 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
304 /* free the tables */
309 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
314 /* -------------------------------------------------------------------------------
316 this section deals with phong shaded lightmap tracing
318 ------------------------------------------------------------------------------- */
320 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
324 calculates the st tangent vectors for normalmapping
327 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
334 /* calculate barycentric basis for the triangle */
335 bb = (dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]) - (dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]);
336 if( fabs( bb ) < 0.00000001f )
340 for( i = 0; i < numVerts; i++ )
342 /* calculate s tangent vector */
343 s = dv[ i ]->st[ 0 ] + 10.0f;
344 t = dv[ i ]->st[ 1 ];
345 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
346 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
347 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
349 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
350 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
351 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
353 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
354 VectorNormalize( stv[ i ], stv[ i ] );
356 /* calculate t tangent vector */
357 s = dv[ i ]->st[ 0 ];
358 t = dv[ i ]->st[ 1 ] + 10.0f;
359 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
360 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
361 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
363 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
367 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
368 VectorNormalize( ttv[ i ], ttv[ i ] );
371 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
372 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
375 /* return to caller */
384 perterbs the normal by the shader's normalmap in tangent space
387 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
394 VectorCopy( dv->normal, pNormal );
396 /* sample normalmap */
397 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
400 /* remap sampled normal from [0,255] to [-1,-1] */
401 for( i = 0; i < 3; i++ )
402 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
404 /* scale tangent vectors and add to original normal */
405 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
406 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
407 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
409 /* renormalize and return */
410 VectorNormalize( pNormal, pNormal );
417 maps a luxel for triangle bv at
421 #define BOGUS_NUDGE -99999.0f
423 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
425 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
426 float *luxel, *origin, *normal, d, lightmapSampleOffset;
433 vec4_t sideplane, hostplane;
438 static float nudges[][ 2 ] =
440 //%{ 0, 0 }, /* try center first */
441 { -NUDGE, 0 }, /* left */
442 { NUDGE, 0 }, /* right */
443 { 0, NUDGE }, /* up */
444 { 0, -NUDGE }, /* down */
445 { -NUDGE, NUDGE }, /* left/up */
446 { NUDGE, -NUDGE }, /* right/down */
447 { NUDGE, NUDGE }, /* right/up */
448 { -NUDGE, -NUDGE }, /* left/down */
449 { BOGUS_NUDGE, BOGUS_NUDGE }
453 /* find luxel xy coords (fixme: subtract 0.5?) */
454 x = dv->lightmap[ 0 ][ 0 ];
455 y = dv->lightmap[ 0 ][ 1 ];
458 else if( x >= lm->sw )
462 else if( y >= lm->sh )
465 /* set shader and cluster list */
469 numClusters = info->numSurfaceClusters;
470 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
479 /* get luxel, origin, cluster, and normal */
480 luxel = SUPER_LUXEL( 0, x, y );
481 origin = SUPER_ORIGIN( x, y );
482 normal = SUPER_NORMAL( x, y );
483 cluster = SUPER_CLUSTER( x, y );
485 /* don't attempt to remap occluded luxels for planar surfaces */
486 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
489 /* only average the normal for premapped luxels */
490 else if( (*cluster) >= 0 )
492 /* do bumpmap calculations */
494 PerturbNormal( dv, si, pNormal, stv, ttv );
496 VectorCopy( dv->normal, pNormal );
498 /* add the additional normal data */
499 VectorAdd( normal, pNormal, normal );
504 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
508 /* axial lightmap projection */
509 if( lm->vecs != NULL )
511 /* calculate an origin for the sample from the lightmap vectors */
512 VectorCopy( lm->origin, origin );
513 for( i = 0; i < 3; i++ )
515 /* add unless it's the axis, which is taken care of later */
516 if( i == lm->axisNum )
518 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
521 /* project the origin onto the plane */
522 d = DotProduct( origin, plane ) - plane[ 3 ];
523 d /= plane[ lm->axisNum ];
524 origin[ lm->axisNum ] -= d;
527 /* non axial lightmap projection (explicit xyz) */
529 VectorCopy( dv->xyz, origin );
531 //////////////////////
532 //27's test to make sure samples stay within the triangle boundaries
533 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
534 //2) if it does, nudge it onto the correct side.
536 if (worldverts!=NULL)
540 VectorCopy(worldverts[j],cverts[j]);
542 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
548 //build plane using 2 edges and a normal
551 VectorCopy(cverts[next],temp);
552 VectorAdd(temp,hostplane,temp);
553 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
555 //planetest sample point
556 e=DotProduct(origin,sideplane);
561 //VectorClear(origin);
562 //Move the sample point back inside triangle bounds
563 origin[0]-=sideplane[0]*(e+1);
564 origin[1]-=sideplane[1]*(e+1);
565 origin[2]-=sideplane[2]*(e+1);
574 ////////////////////////
576 /* planar surfaces have precalculated lightmap vectors for nudging */
577 if( lm->plane != NULL )
579 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
580 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
581 VectorCopy( lm->plane, vecs[ 2 ] );
584 /* non-planar surfaces must calculate them */
588 VectorCopy( plane, vecs[ 2 ] );
590 VectorCopy( dv->normal, vecs[ 2 ] );
591 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
594 /* push the origin off the surface a bit */
596 lightmapSampleOffset = si->lightmapSampleOffset;
598 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
599 if( lm->axisNum < 0 )
600 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
601 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
602 origin[ lm->axisNum ] -= lightmapSampleOffset;
604 origin[ lm->axisNum ] += lightmapSampleOffset;
606 VectorCopy(origin,origintwo);
607 origintwo[0]+=vecs[2][0];
608 origintwo[1]+=vecs[2][1];
609 origintwo[2]+=vecs[2][2];
612 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
614 /* another retarded hack, storing nudge count in luxel[ 1 ] */
617 /* point in solid? (except in dark mode) */
618 if( pointCluster < 0 && dark == qfalse )
620 /* nudge the the location around */
622 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
624 /* nudge the vector around a bit */
625 for( i = 0; i < 3; i++ )
627 /* set nudged point*/
628 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
632 /* get pvs cluster */
633 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
634 //if( pointCluster >= 0 )
635 // VectorCopy( nudged, origin );
640 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
641 if( pointCluster < 0 && si != NULL && dark == qfalse )
643 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
644 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
645 //if( pointCluster >= 0 )
646 // VectorCopy( nudged, origin );
651 if( pointCluster < 0 )
653 (*cluster) = CLUSTER_OCCLUDED;
654 VectorClear( origin );
655 VectorClear( normal );
661 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
663 /* do bumpmap calculations */
665 PerturbNormal( dv, si, pNormal, stv, ttv );
667 VectorCopy( dv->normal, pNormal );
669 /* store the cluster and normal */
670 (*cluster) = pointCluster;
671 VectorCopy( pNormal, normal );
673 /* store explicit mapping pass and implicit mapping pass */
688 recursively subdivides a triangle until its edges are shorter
689 than the distance between two luxels (thanks jc :)
692 static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
694 bspDrawVert_t mid, *dv2[ 3 ];
698 /* map the vertexes */
700 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
701 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
702 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
708 float *a, *b, dx, dy, dist, maxDist;
711 /* find the longest edge and split it */
714 for( i = 0; i < 3; i++ )
717 a = dv[ i ]->lightmap[ 0 ];
718 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
721 dx = a[ 0 ] - b[ 0 ];
722 dy = a[ 1 ] - b[ 1 ];
723 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
733 /* try to early out */
734 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
738 /* split the longest edge and map it */
739 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
740 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
742 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
743 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
745 /* recurse to first triangle */
746 VectorCopy( dv, dv2 );
748 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
750 /* recurse to second triangle */
751 VectorCopy( dv, dv2 );
752 dv2[ (max + 1) % 3 ] = ∣
753 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
760 seed function for MapTriangle_r()
761 requires a cw ordered triangle
764 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
768 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
769 vec3_t worldverts[ 3 ];
772 /* get plane if possible */
773 if( lm->plane != NULL )
775 VectorCopy( lm->plane, plane );
776 plane[ 3 ] = lm->plane[ 3 ];
779 /* otherwise make one from the points */
780 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
783 /* check to see if we need to calculate texture->world tangent vectors */
784 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
795 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
796 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
797 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
799 /* map the vertexes */
800 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
801 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
802 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
804 /* 2002-11-20: prefer axial triangle edges */
807 /* subdivide the triangle */
808 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
812 for( i = 0; i < 3; i++ )
815 bspDrawVert_t *dv2[ 3 ];
819 a = dv[ i ]->lightmap[ 0 ];
820 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
822 /* make degenerate triangles for mapping edges */
823 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
826 dv2[ 1 ] = dv[ (i + 1) % 3 ];
827 dv2[ 2 ] = dv[ (i + 1) % 3 ];
829 /* map the degenerate triangle */
830 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
841 recursively subdivides a quad until its edges are shorter
842 than the distance between two luxels
845 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] )
847 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
854 float *a, *b, dx, dy, dist, maxDist;
857 /* find the longest edge and split it */
860 for( i = 0; i < 4; i++ )
863 a = dv[ i ]->lightmap[ 0 ];
864 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
867 dx = a[ 0 ] - b[ 0 ];
868 dy = a[ 1 ] - b[ 1 ];
869 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
879 /* try to early out */
880 if( max < 0 || maxDist <= subdivideThreshold )
884 /* we only care about even/odd edges */
887 /* split the longest edges */
888 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
889 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
891 /* map the vertexes */
892 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
893 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
898 /* recurse to first quad */
900 dv2[ 1 ] = &mid[ 0 ];
901 dv2[ 2 ] = &mid[ 1 ];
903 MapQuad_r( lm, info, dv2, plane, stv, ttv );
905 /* recurse to second quad */
906 dv2[ 0 ] = &mid[ 0 ];
909 dv2[ 3 ] = &mid[ 1 ];
910 MapQuad_r( lm, info, dv2, plane, stv, ttv );
916 /* recurse to first quad */
919 dv2[ 2 ] = &mid[ 0 ];
920 dv2[ 3 ] = &mid[ 1 ];
921 MapQuad_r( lm, info, dv2, plane, stv, ttv );
923 /* recurse to second quad */
924 dv2[ 0 ] = &mid[ 1 ];
925 dv2[ 1 ] = &mid[ 0 ];
928 MapQuad_r( lm, info, dv2, plane, stv, ttv );
936 seed function for MapQuad_r()
937 requires a cw ordered triangle quad
940 #define QUAD_PLANAR_EPSILON 0.5f
942 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
946 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
949 /* get plane if possible */
950 if( lm->plane != NULL )
952 VectorCopy( lm->plane, plane );
953 plane[ 3 ] = lm->plane[ 3 ];
956 /* otherwise make one from the points */
957 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
960 /* 4th point must fall on the plane */
961 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
962 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
965 /* check to see if we need to calculate texture->world tangent vectors */
966 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
977 /* map the vertexes */
978 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
979 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
980 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
981 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
983 /* subdivide the quad */
984 MapQuad_r( lm, info, dv, plane, stv, ttv );
992 maps the locations, normals, and pvs clusters for a raw lightmap
995 #define VectorDivide( in, d, out ) VectorScale( in, (1.0f / (d)), out ) //% (out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)
997 void MapRawLightmap( int rawLightmapNum )
999 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1000 float *luxel, *origin, *normal, samples, radius, pass;
1002 bspDrawSurface_t *ds;
1003 surfaceInfo_t *info;
1004 mesh_t src, *subdivided, *mesh;
1005 bspDrawVert_t *verts, *dv[ 4 ], fake;
1008 /* bail if this number exceeds the number of raw lightmaps */
1009 if( rawLightmapNum >= numRawLightmaps )
1013 lm = &rawLightmaps[ rawLightmapNum ];
1015 /* -----------------------------------------------------------------
1016 map referenced surfaces onto the raw lightmap
1017 ----------------------------------------------------------------- */
1019 /* walk the list of surfaces on this raw lightmap */
1020 for( n = 0; n < lm->numLightSurfaces; n++ )
1022 /* with > 1 surface per raw lightmap, clear occluded */
1025 for( y = 0; y < lm->sh; y++ )
1027 for( x = 0; x < lm->sw; x++ )
1030 cluster = SUPER_CLUSTER( x, y );
1032 *cluster = CLUSTER_UNMAPPED;
1038 num = lightSurfaces[ lm->firstLightSurface + n ];
1039 ds = &bspDrawSurfaces[ num ];
1040 info = &surfaceInfos[ num ];
1042 /* bail if no lightmap to calculate */
1043 if( info->lm != lm )
1049 /* map the surface onto the lightmap origin/cluster/normal buffers */
1050 switch( ds->surfaceType )
1054 verts = yDrawVerts + ds->firstVert;
1056 /* map the triangles */
1057 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1059 for( i = 0; i < ds->numIndexes; i += 3 )
1061 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1062 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1063 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1064 MapTriangle( lm, info, dv, mapNonAxial );
1070 /* make a mesh from the drawsurf */
1071 src.width = ds->patchWidth;
1072 src.height = ds->patchHeight;
1073 src.verts = &yDrawVerts[ ds->firstVert ];
1074 //% subdivided = SubdivideMesh( src, 8, 512 );
1075 subdivided = SubdivideMesh2( src, info->patchIterations );
1077 /* fit it to the curve and remove colinear verts on rows/columns */
1078 PutMeshOnCurve( *subdivided );
1079 mesh = RemoveLinearMeshColumnsRows( subdivided );
1080 FreeMesh( subdivided );
1083 verts = mesh->verts;
1089 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1090 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1091 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1092 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1096 /* map the mesh quads */
1099 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1101 for( y = 0; y < (mesh->height - 1); y++ )
1103 for( x = 0; x < (mesh->width - 1); x++ )
1106 pw[ 0 ] = x + (y * mesh->width);
1107 pw[ 1 ] = x + ((y + 1) * mesh->width);
1108 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1109 pw[ 3 ] = x + 1 + (y * mesh->width);
1110 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1115 /* get drawverts and map first triangle */
1116 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1117 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1118 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1119 MapTriangle( lm, info, dv, mapNonAxial );
1121 /* get drawverts and map second triangle */
1122 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1123 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1124 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1125 MapTriangle( lm, info, dv, mapNonAxial );
1132 for( y = 0; y < (mesh->height - 1); y++ )
1134 for( x = 0; x < (mesh->width - 1); x++ )
1137 pw[ 0 ] = x + (y * mesh->width);
1138 pw[ 1 ] = x + ((y + 1) * mesh->width);
1139 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1140 pw[ 3 ] = x + 1 + (y * mesh->width);
1146 /* attempt to map quad first */
1147 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1148 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1149 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1150 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1151 if( MapQuad( lm, info, dv ) )
1154 /* get drawverts and map first triangle */
1155 MapTriangle( lm, info, dv, mapNonAxial );
1157 /* get drawverts and map second triangle */
1158 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1159 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1160 MapTriangle( lm, info, dv, mapNonAxial );
1175 /* -----------------------------------------------------------------
1176 average and clean up luxel normals
1177 ----------------------------------------------------------------- */
1179 /* walk the luxels */
1180 for( y = 0; y < lm->sh; y++ )
1182 for( x = 0; x < lm->sw; x++ )
1185 luxel = SUPER_LUXEL( 0, x, y );
1186 normal = SUPER_NORMAL( x, y );
1187 cluster = SUPER_CLUSTER( x, y );
1189 /* only look at mapped luxels */
1193 /* the normal data could be the sum of multiple samples */
1194 if( luxel[ 3 ] > 1.0f )
1195 VectorNormalize( normal, normal );
1197 /* mark this luxel as having only one normal */
1202 /* non-planar surfaces stop here */
1203 if( lm->plane == NULL )
1206 /* -----------------------------------------------------------------
1207 map occluded or unuxed luxels
1208 ----------------------------------------------------------------- */
1210 /* walk the luxels */
1211 radius = floor( superSample / 2 );
1212 radius = radius > 0 ? radius : 1.0f;
1214 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1216 for( y = 0; y < lm->sh; y++ )
1218 for( x = 0; x < lm->sw; x++ )
1221 luxel = SUPER_LUXEL( 0, x, y );
1222 normal = SUPER_NORMAL( x, y );
1223 cluster = SUPER_CLUSTER( x, y );
1225 /* only look at unmapped luxels */
1226 if( *cluster != CLUSTER_UNMAPPED )
1229 /* divine a normal and origin from neighboring luxels */
1230 VectorClear( fake.xyz );
1231 VectorClear( fake.normal );
1232 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1233 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1235 for( sy = (y - 1); sy <= (y + 1); sy++ )
1237 if( sy < 0 || sy >= lm->sh )
1240 for( sx = (x - 1); sx <= (x + 1); sx++ )
1242 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1245 /* get neighboring luxel */
1246 luxel = SUPER_LUXEL( 0, sx, sy );
1247 origin = SUPER_ORIGIN( sx, sy );
1248 normal = SUPER_NORMAL( sx, sy );
1249 cluster = SUPER_CLUSTER( sx, sy );
1251 /* only consider luxels mapped in previous passes */
1252 if( *cluster < 0 || luxel[ 0 ] >= pass )
1255 /* add its distinctiveness to our own */
1256 VectorAdd( fake.xyz, origin, fake.xyz );
1257 VectorAdd( fake.normal, normal, fake.normal );
1258 samples += luxel[ 3 ];
1263 if( samples == 0.0f )
1267 VectorDivide( fake.xyz, samples, fake.xyz );
1268 //% VectorDivide( fake.normal, samples, fake.normal );
1269 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1272 /* map the fake vert */
1273 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1278 /* -----------------------------------------------------------------
1279 average and clean up luxel normals
1280 ----------------------------------------------------------------- */
1282 /* walk the luxels */
1283 for( y = 0; y < lm->sh; y++ )
1285 for( x = 0; x < lm->sw; x++ )
1288 luxel = SUPER_LUXEL( 0, x, y );
1289 normal = SUPER_NORMAL( x, y );
1290 cluster = SUPER_CLUSTER( x, y );
1292 /* only look at mapped luxels */
1296 /* the normal data could be the sum of multiple samples */
1297 if( luxel[ 3 ] > 1.0f )
1298 VectorNormalize( normal, normal );
1300 /* mark this luxel as having only one normal */
1308 for( y = 0; y < lm->sh; y++ )
1310 for( x = 0; x < lm->sw; x++ )
1315 cluster = SUPER_CLUSTER( x, y );
1316 origin = SUPER_ORIGIN( x, y );
1317 normal = SUPER_NORMAL( x, y );
1318 luxel = SUPER_LUXEL( x, y );
1323 /* check if within the bounding boxes of all surfaces referenced */
1324 ClearBounds( mins, maxs );
1325 for( n = 0; n < lm->numLightSurfaces; n++ )
1328 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1329 TOL = info->sampleSize + 2;
1330 AddPointToBounds( info->mins, mins, maxs );
1331 AddPointToBounds( info->maxs, mins, maxs );
1332 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1333 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1334 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1339 if( n < lm->numLightSurfaces )
1342 /* report bogus origin */
1343 Sys_Printf( "%6d [%2d,%2d] (%4d): XYZ(%+4.1f %+4.1f %+4.1f) LO(%+4.1f %+4.1f %+4.1f) HI(%+4.1f %+4.1f %+4.1f) <%3.0f>\n",
1344 rawLightmapNum, x, y, *cluster,
1345 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1346 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1347 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1358 sets up dirtmap (ambient occlusion)
1361 #define DIRT_CONE_ANGLE 88 /* degrees */
1362 #define DIRT_NUM_ANGLE_STEPS 16
1363 #define DIRT_NUM_ELEVATION_STEPS 3
1364 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1366 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1367 static int numDirtVectors = 0;
1369 void SetupDirt( void )
1372 float angle, elevation, angleStep, elevationStep;
1376 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1378 /* calculate angular steps */
1379 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1380 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1384 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1386 /* iterate elevation */
1387 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1389 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1390 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1391 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1396 /* emit some statistics */
1397 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1403 calculates dirt value for a given sample
1406 float DirtForSample( trace_t *trace )
1409 float gatherDirt, outDirt, angle, elevation, ooDepth;
1410 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1416 if( trace == NULL || trace->cluster < 0 )
1421 ooDepth = 1.0f / dirtDepth;
1422 VectorCopy( trace->normal, normal );
1424 /* check if the normal is aligned to the world-up */
1425 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f )
1427 if( normal[ 2 ] == 1.0f )
1429 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1430 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1432 else if( normal[ 2 ] == -1.0f )
1434 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1435 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1440 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1441 CrossProduct( normal, worldUp, myRt );
1442 VectorNormalize( myRt, myRt );
1443 CrossProduct( myRt, normal, myUp );
1444 VectorNormalize( myUp, myUp );
1447 /* 1 = random mode, 0 (well everything else) = non-random mode */
1451 for( i = 0; i < numDirtVectors; i++ )
1453 /* get random vector */
1454 angle = Random() * DEG2RAD( 360.0f );
1455 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1456 temp[ 0 ] = cos( angle ) * sin( elevation );
1457 temp[ 1 ] = sin( angle ) * sin( elevation );
1458 temp[ 2 ] = cos( elevation );
1460 /* transform into tangent space */
1461 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1462 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1463 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1466 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1467 SetupTrace( trace );
1473 VectorSubtract( trace->hit, trace->origin, displacement );
1474 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1480 /* iterate through ordered vectors */
1481 for( i = 0; i < numDirtVectors; i++ )
1483 /* transform vector into tangent space */
1484 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1485 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1486 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1489 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1490 SetupTrace( trace );
1496 VectorSubtract( trace->hit, trace->origin, displacement );
1497 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1503 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1504 SetupTrace( trace );
1510 VectorSubtract( trace->hit, trace->origin, displacement );
1511 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1515 if( gatherDirt <= 0.0f )
1518 /* apply gain (does this even do much? heh) */
1519 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1520 if( outDirt > 1.0f )
1524 outDirt *= dirtScale;
1525 if( outDirt > 1.0f )
1528 /* return to sender */
1529 return 1.0f - outDirt;
1536 calculates dirty fraction for each luxel
1539 void DirtyRawLightmap( int rawLightmapNum )
1541 int i, x, y, sx, sy, *cluster;
1542 float *origin, *normal, *dirt, *dirt2, average, samples;
1544 surfaceInfo_t *info;
1548 /* bail if this number exceeds the number of raw lightmaps */
1549 if( rawLightmapNum >= numRawLightmaps )
1553 lm = &rawLightmaps[ rawLightmapNum ];
1556 trace.testOcclusion = qtrue;
1557 trace.forceSunlight = qfalse;
1558 trace.recvShadows = lm->recvShadows;
1559 trace.numSurfaces = lm->numLightSurfaces;
1560 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1561 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1562 trace.testAll = qfalse;
1564 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1565 trace.twoSided = qfalse;
1566 for( i = 0; i < trace.numSurfaces; i++ )
1569 info = &surfaceInfos[ trace.surfaces[ i ] ];
1571 /* check twosidedness */
1572 if( info->si->twoSided )
1574 trace.twoSided = qtrue;
1580 for( y = 0; y < lm->sh; y++ )
1582 for( x = 0; x < lm->sw; x++ )
1585 cluster = SUPER_CLUSTER( x, y );
1586 origin = SUPER_ORIGIN( x, y );
1587 normal = SUPER_NORMAL( x, y );
1588 dirt = SUPER_DIRT( x, y );
1590 /* set default dirt */
1593 /* only look at mapped luxels */
1598 trace.cluster = *cluster;
1599 VectorCopy( origin, trace.origin );
1600 VectorCopy( normal, trace.normal );
1603 *dirt = DirtForSample( &trace );
1607 /* testing no filtering */
1611 for( y = 0; y < lm->sh; y++ )
1613 for( x = 0; x < lm->sw; x++ )
1616 cluster = SUPER_CLUSTER( x, y );
1617 dirt = SUPER_DIRT( x, y );
1619 /* filter dirt by adjacency to unmapped luxels */
1622 for( sy = (y - 1); sy <= (y + 1); sy++ )
1624 if( sy < 0 || sy >= lm->sh )
1627 for( sx = (x - 1); sx <= (x + 1); sx++ )
1629 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1632 /* get neighboring luxel */
1633 cluster = SUPER_CLUSTER( sx, sy );
1634 dirt2 = SUPER_DIRT( sx, sy );
1635 if( *cluster < 0 || *dirt2 <= 0.0f )
1644 if( samples <= 0.0f )
1649 if( samples <= 0.0f )
1653 *dirt = average / samples;
1662 calculates the pvs cluster, origin, normal of a sub-luxel
1665 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1667 int i, *cluster, *cluster2;
1668 float *origin, *origin2, *normal; //% , *normal2;
1669 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1672 /* calulate x vector */
1673 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1675 cluster = SUPER_CLUSTER( x, y );
1676 origin = SUPER_ORIGIN( x, y );
1677 //% normal = SUPER_NORMAL( x, y );
1678 cluster2 = SUPER_CLUSTER( x + 1, y );
1679 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1680 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1682 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1684 cluster = SUPER_CLUSTER( x - 1, y );
1685 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1686 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1687 cluster2 = SUPER_CLUSTER( x, y );
1688 origin2 = SUPER_ORIGIN( x, y );
1689 //% normal2 = SUPER_NORMAL( x, y );
1692 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1694 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1695 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1697 /* calulate y vector */
1698 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1700 cluster = SUPER_CLUSTER( x, y );
1701 origin = SUPER_ORIGIN( x, y );
1702 //% normal = SUPER_NORMAL( x, y );
1703 cluster2 = SUPER_CLUSTER( x, y + 1 );
1704 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1705 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1707 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1709 cluster = SUPER_CLUSTER( x, y - 1 );
1710 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1711 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1712 cluster2 = SUPER_CLUSTER( x, y );
1713 origin2 = SUPER_ORIGIN( x, y );
1714 //% normal2 = SUPER_NORMAL( x, y );
1717 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1719 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1720 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1722 /* calculate new origin */
1723 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1724 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1725 for( i = 0; i < 3; i++ )
1726 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1729 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1730 if( *sampleCluster < 0 )
1733 /* calculate new normal */
1734 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1735 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1736 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1738 normal = SUPER_NORMAL( x, y );
1739 VectorCopy( normal, sampleNormal );
1747 SubsampleRawLuxel_r()
1748 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1751 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )
1753 int b, samples, mapped, lighted;
1756 vec3_t origin[ 4 ], normal[ 4 ];
1757 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1758 vec3_t color, total;
1762 if( lightLuxel[ 3 ] >= lightSamples )
1766 VectorClear( total );
1770 /* make 2x2 subsample stamp */
1771 for( b = 0; b < 4; b++ )
1774 VectorCopy( sampleOrigin, origin[ b ] );
1776 /* calculate position */
1777 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1784 /* increment sample count */
1785 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1788 trace->cluster = *cluster;
1789 VectorCopy( origin[ b ], trace->origin );
1790 VectorCopy( normal[ b ], trace->normal );
1794 LightContributionToSample( trace );
1796 /* add to totals (fixme: make contrast function) */
1797 VectorCopy( trace->color, luxel[ b ] );
1798 VectorAdd( total, trace->color, total );
1799 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1803 /* subsample further? */
1804 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1805 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1806 lighted != 0 && lighted != mapped )
1808 for( b = 0; b < 4; b++ )
1810 if( cluster[ b ] < 0 )
1812 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.25f), luxel[ b ] );
1817 //% VectorClear( color );
1819 VectorCopy( lightLuxel, color );
1821 for( b = 0; b < 4; b++ )
1823 if( cluster[ b ] < 0 )
1825 VectorAdd( color, luxel[ b ], color );
1833 color[ 0 ] /= samples;
1834 color[ 1 ] /= samples;
1835 color[ 2 ] /= samples;
1838 VectorCopy( color, lightLuxel );
1839 lightLuxel[ 3 ] += 1.0f;
1846 IlluminateRawLightmap()
1847 illuminates the luxels
1850 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1851 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1853 void IlluminateRawLightmap( int rawLightmapNum )
1855 int i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
1856 int *cluster, *cluster2, mapped, lighted, totalLighted;
1858 surfaceInfo_t *info;
1859 qboolean filterColor, filterDir;
1861 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1862 float *lightLuxels, *lightLuxel, samples, filterRadius, weight;
1863 vec3_t color, averageColor, averageDir, total, temp, temp2;
1864 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1866 float stackLightLuxels[ STACK_LL_SIZE ];
1871 /* bail if this number exceeds the number of raw lightmaps */
1872 if( rawLightmapNum >= numRawLightmaps )
1876 lm = &rawLightmaps[ rawLightmapNum ];
1879 trace.testOcclusion = !noTrace;
1880 trace.forceSunlight = qfalse;
1881 trace.recvShadows = lm->recvShadows;
1882 trace.numSurfaces = lm->numLightSurfaces;
1883 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1884 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1886 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1887 trace.twoSided = qfalse;
1888 for( i = 0; i < trace.numSurfaces; i++ )
1891 info = &surfaceInfos[ trace.surfaces[ i ] ];
1893 /* check twosidedness */
1894 if( info->si->twoSided )
1896 trace.twoSided = qtrue;
1901 /* create a culled light list for this raw lightmap */
1902 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
1904 /* -----------------------------------------------------------------
1906 ----------------------------------------------------------------- */
1909 numLuxelsIlluminated += (lm->sw * lm->sh);
1911 /* test debugging state */
1912 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
1914 /* debug fill the luxels */
1915 for( y = 0; y < lm->sh; y++ )
1917 for( x = 0; x < lm->sw; x++ )
1920 cluster = SUPER_CLUSTER( x, y );
1922 /* only fill mapped luxels */
1926 /* get particulars */
1927 luxel = SUPER_LUXEL( 0, x, y );
1928 origin = SUPER_ORIGIN( x, y );
1929 normal = SUPER_NORMAL( x, y );
1931 /* color the luxel with raw lightmap num? */
1933 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
1935 /* color the luxel with lightmap axis? */
1936 else if( debugAxis )
1938 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
1939 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
1940 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
1943 /* color the luxel with luxel cluster? */
1944 else if( debugCluster )
1945 VectorCopy( debugColors[ *cluster % 12 ], luxel );
1947 /* color the luxel with luxel origin? */
1948 else if( debugOrigin )
1950 VectorSubtract( lm->maxs, lm->mins, temp );
1951 VectorScale( temp, (1.0f / 255.0f), temp );
1952 VectorSubtract( origin, lm->mins, temp2 );
1953 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
1954 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
1955 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
1958 /* color the luxel with the normal */
1959 else if( normalmap )
1961 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
1962 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
1963 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
1966 /* otherwise clear it */
1968 VectorClear( luxel );
1977 /* allocate temporary per-light luxel storage */
1978 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
1979 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
1980 lightLuxels = stackLightLuxels;
1982 lightLuxels = safe_malloc( llSize );
1985 //% memset( lm->superLuxels[ 0 ], 0, llSize );
1987 /* set ambient color */
1988 for( y = 0; y < lm->sh; y++ )
1990 for( x = 0; x < lm->sw; x++ )
1993 cluster = SUPER_CLUSTER( x, y );
1994 luxel = SUPER_LUXEL( 0, x, y );
1995 normal = SUPER_NORMAL( x, y );
1996 deluxel = SUPER_DELUXEL( x, y );
1998 /* blacken unmapped clusters */
2000 VectorClear( luxel );
2005 VectorCopy( ambientColor, luxel );
2007 VectorScale( normal, 0.00390625f, deluxel );
2013 /* clear styled lightmaps */
2014 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2015 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2017 if( lm->superLuxels[ lightmapNum ] != NULL )
2018 memset( lm->superLuxels[ lightmapNum ], 0, size );
2021 /* debugging code */
2022 //% if( trace.numLights <= 0 )
2023 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2025 /* walk light list */
2026 for( i = 0; i < trace.numLights; i++ )
2029 trace.light = trace.lights[ i ];
2032 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2034 if( lm->styles[ lightmapNum ] == trace.light->style ||
2035 lm->styles[ lightmapNum ] == LS_NONE )
2039 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2040 if( lightmapNum >= MAX_LIGHTMAPS )
2042 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2047 memset( lightLuxels, 0, llSize );
2050 /* initial pass, one sample per luxel */
2051 for( y = 0; y < lm->sh; y++ )
2053 for( x = 0; x < lm->sw; x++ )
2056 cluster = SUPER_CLUSTER( x, y );
2060 /* get particulars */
2061 lightLuxel = LIGHT_LUXEL( x, y );
2062 deluxel = SUPER_DELUXEL( x, y );
2063 origin = SUPER_ORIGIN( x, y );
2064 normal = SUPER_NORMAL( x, y );
2067 ////////// 27's temp hack for testing edge clipping ////
2068 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2070 lightLuxel[ 1 ] = 255;
2071 lightLuxel[ 3 ] = 1.0f;
2077 /* set contribution count */
2078 lightLuxel[ 3 ] = 1.0f;
2081 trace.cluster = *cluster;
2082 VectorCopy( origin, trace.origin );
2083 VectorCopy( normal, trace.normal );
2085 /* get light for this sample */
2086 LightContributionToSample( &trace );
2087 VectorCopy( trace.color, lightLuxel );
2090 if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2094 /* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */
2097 /* color to grayscale (photoshop rgb weighting) */
2098 brightness = trace.color[ 0 ] * 0.3f + trace.color[ 1 ] * 0.59f + trace.color[ 2 ] * 0.11f;
2099 brightness *= (1.0 / 255.0);
2100 VectorScale( trace.direction, brightness, trace.direction );
2101 VectorAdd( deluxel, trace.direction, deluxel );
2106 /* don't even bother with everything else if nothing was lit */
2107 if( totalLighted == 0 )
2110 /* determine filter radius */
2111 filterRadius = lm->filterRadius > trace.light->filterRadius
2113 : trace.light->filterRadius;
2114 if( filterRadius < 0.0f )
2115 filterRadius = 0.0f;
2117 /* set luxel filter radius */
2118 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2119 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2120 luxelFilterRadius = 1;
2122 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2123 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2124 if( lightSamples > 1 && luxelFilterRadius == 0 )
2127 for( y = 0; y < (lm->sh - 1); y++ )
2129 for( x = 0; x < (lm->sw - 1); x++ )
2134 VectorClear( total );
2136 /* test 2x2 stamp */
2137 for( t = 0; t < 4; t++ )
2139 /* set sample coords */
2140 sx = x + tests[ t ][ 0 ];
2141 sy = y + tests[ t ][ 1 ];
2144 cluster = SUPER_CLUSTER( sx, sy );
2150 lightLuxel = LIGHT_LUXEL( sx, sy );
2151 VectorAdd( total, lightLuxel, total );
2152 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2156 /* if total color is under a certain amount, then don't bother subsampling */
2157 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2160 /* if all 4 pixels are either in shadow or light, then don't subsample */
2161 if( lighted != 0 && lighted != mapped )
2163 for( t = 0; t < 4; t++ )
2165 /* set sample coords */
2166 sx = x + tests[ t ][ 0 ];
2167 sy = y + tests[ t ][ 1 ];
2170 cluster = SUPER_CLUSTER( sx, sy );
2173 lightLuxel = LIGHT_LUXEL( sx, sy );
2174 origin = SUPER_ORIGIN( sx, sy );
2176 /* only subsample shadowed luxels */
2177 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2181 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f, lightLuxel );
2183 /* debug code to colorize subsampled areas to yellow */
2184 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2185 //% VectorSet( luxel, 255, 204, 0 );
2192 /* tertiary pass, apply dirt map (ambient occlusion) */
2196 for( y = 0; y < lm->sh; y++ )
2198 for( x = 0; x < lm->sw; x++ )
2201 cluster = SUPER_CLUSTER( x, y );
2205 /* get particulars */
2206 lightLuxel = LIGHT_LUXEL( x, y );
2207 dirt = SUPER_DIRT( x, y );
2209 /* scale light value */
2210 VectorScale( lightLuxel, *dirt, lightLuxel );
2215 /* allocate sampling lightmap storage */
2216 if( lm->superLuxels[ lightmapNum ] == NULL )
2218 /* allocate sampling lightmap storage */
2219 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2220 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2221 memset( lm->superLuxels[ lightmapNum ], 0, size );
2225 if( lightmapNum > 0 )
2227 lm->styles[ lightmapNum ] = trace.light->style;
2228 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2231 /* copy to permanent luxels */
2232 for( y = 0; y < lm->sh; y++ )
2234 for( x = 0; x < lm->sw; x++ )
2236 /* get cluster and origin */
2237 cluster = SUPER_CLUSTER( x, y );
2240 origin = SUPER_ORIGIN( x, y );
2243 if( luxelFilterRadius )
2246 VectorClear( averageColor );
2249 /* cheaper distance-based filtering */
2250 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2252 if( sy < 0 || sy >= lm->sh )
2255 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2257 if( sx < 0 || sx >= lm->sw )
2260 /* get particulars */
2261 cluster = SUPER_CLUSTER( sx, sy );
2264 lightLuxel = LIGHT_LUXEL( sx, sy );
2267 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2268 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2270 /* scale luxel by filter weight */
2271 VectorScale( lightLuxel, weight, color );
2272 VectorAdd( averageColor, color, averageColor );
2278 if( samples <= 0.0f )
2281 /* scale into luxel */
2282 luxel = SUPER_LUXEL( lightmapNum, x, y );
2285 /* handle negative light */
2286 if( trace.light->flags & LIGHT_NEGATIVE )
2288 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2289 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2290 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2293 /* handle normal light */
2296 luxel[ 0 ] += averageColor[ 0 ] / samples;
2297 luxel[ 1 ] += averageColor[ 1 ] / samples;
2298 luxel[ 2 ] += averageColor[ 2 ] / samples;
2305 /* get particulars */
2306 lightLuxel = LIGHT_LUXEL( x, y );
2307 luxel = SUPER_LUXEL( lightmapNum, x, y );
2309 /* handle negative light */
2310 if( trace.light->flags & LIGHT_NEGATIVE )
2311 VectorScale( averageColor, -1.0f, averageColor );
2316 /* handle negative light */
2317 if( trace.light->flags & LIGHT_NEGATIVE )
2318 VectorSubtract( luxel, lightLuxel, luxel );
2320 /* handle normal light */
2322 VectorAdd( luxel, lightLuxel, luxel );
2328 /* free temporary luxels */
2329 if( lightLuxels != stackLightLuxels )
2330 free( lightLuxels );
2333 /* free light list */
2334 FreeTraceLights( &trace );
2336 /* -----------------------------------------------------------------
2338 ----------------------------------------------------------------- */
2342 /* walk lightmaps */
2343 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2346 if( lm->superLuxels[ lightmapNum ] == NULL )
2349 /* apply floodlight to each luxel */
2350 for( y = 0; y < lm->sh; y++ )
2352 for( x = 0; x < lm->sw; x++ )
2355 cluster = SUPER_CLUSTER( x, y );
2356 //% if( *cluster < 0 )
2359 /* get particulars */
2360 luxel = SUPER_LUXEL( lightmapNum, x, y );
2361 floodlight = SUPER_FLOODLIGHT( x, y );
2363 flood[0]=floodlightRGB[0]*floodlightIntensity;
2364 flood[1]=floodlightRGB[1]*floodlightIntensity;
2365 flood[2]=floodlightRGB[2]*floodlightIntensity;
2367 /* scale light value */
2368 VectorScale( flood, *floodlight, flood );
2373 if (luxel[3]==0) luxel[3]=1;
2381 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2384 if( lm->superLuxels[ lightmapNum ] == NULL )
2387 for( y = 0; y < lm->sh; y++ )
2389 for( x = 0; x < lm->sw; x++ )
2392 cluster = SUPER_CLUSTER( x, y );
2393 //% if( *cluster < 0 )
2396 /* get particulars */
2397 luxel = SUPER_LUXEL( lightmapNum, x, y );
2398 normal = SUPER_NORMAL ( x, y );
2400 luxel[0]=(normal[0]*127)+127;
2401 luxel[1]=(normal[1]*127)+127;
2402 luxel[2]=(normal[2]*127)+127;
2408 /* -----------------------------------------------------------------
2410 ----------------------------------------------------------------- */
2414 /* walk lightmaps */
2415 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2418 if( lm->superLuxels[ lightmapNum ] == NULL )
2421 /* apply dirt to each luxel */
2422 for( y = 0; y < lm->sh; y++ )
2424 for( x = 0; x < lm->sw; x++ )
2427 cluster = SUPER_CLUSTER( x, y );
2428 //% if( *cluster < 0 )
2431 /* get particulars */
2432 luxel = SUPER_LUXEL( lightmapNum, x, y );
2433 dirt = SUPER_DIRT( x, y );
2436 VectorScale( luxel, *dirt, luxel );
2440 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2446 /* -----------------------------------------------------------------
2448 ----------------------------------------------------------------- */
2450 /* walk lightmaps */
2451 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2454 if( lm->superLuxels[ lightmapNum ] == NULL )
2457 /* average occluded luxels from neighbors */
2458 for( y = 0; y < lm->sh; y++ )
2460 for( x = 0; x < lm->sw; x++ )
2462 /* get particulars */
2463 cluster = SUPER_CLUSTER( x, y );
2464 luxel = SUPER_LUXEL( lightmapNum, x, y );
2465 deluxel = SUPER_DELUXEL( x, y );
2466 normal = SUPER_NORMAL( x, y );
2468 /* determine if filtering is necessary */
2469 filterColor = qfalse;
2472 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2473 filterColor = qtrue;
2474 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2477 if( !filterColor && !filterDir )
2480 /* choose seed amount */
2481 VectorClear( averageColor );
2482 VectorClear( averageDir );
2485 /* walk 3x3 matrix */
2486 for( sy = (y - 1); sy <= (y + 1); sy++ )
2488 if( sy < 0 || sy >= lm->sh )
2491 for( sx = (x - 1); sx <= (x + 1); sx++ )
2493 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2496 /* get neighbor's particulars */
2497 cluster2 = SUPER_CLUSTER( sx, sy );
2498 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2499 deluxel2 = SUPER_DELUXEL( sx, sy );
2501 /* ignore unmapped/unlit luxels */
2502 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2503 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2506 /* add its distinctiveness to our own */
2507 VectorAdd( averageColor, luxel2, averageColor );
2508 samples += luxel2[ 3 ];
2510 VectorAdd( averageDir, deluxel2, averageDir );
2515 if( samples <= 0.0f )
2518 /* dark lightmap seams */
2521 if( lightmapNum == 0 )
2522 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2529 VectorDivide( averageColor, samples, luxel );
2533 VectorDivide( averageDir, samples, deluxel );
2535 /* set cluster to -3 */
2537 *cluster = CLUSTER_FLOODED;
2546 IlluminateVertexes()
2547 light the surface vertexes
2550 #define VERTEX_NUDGE 4.0f
2552 void IlluminateVertexes( int num )
2554 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2555 int lightmapNum, numAvg;
2556 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2557 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2558 bspDrawSurface_t *ds;
2559 surfaceInfo_t *info;
2561 bspDrawVert_t *verts;
2565 /* get surface, info, and raw lightmap */
2566 ds = &bspDrawSurfaces[ num ];
2567 info = &surfaceInfos[ num ];
2570 /* -----------------------------------------------------------------
2571 illuminate the vertexes
2572 ----------------------------------------------------------------- */
2574 /* calculate vertex lighting for surfaces without lightmaps */
2575 if( lm == NULL || cpmaHack )
2578 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2579 trace.forceSunlight = info->si->forceSunlight;
2580 trace.recvShadows = info->recvShadows;
2581 trace.numSurfaces = 1;
2582 trace.surfaces = #
2583 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2585 /* twosided lighting */
2586 trace.twoSided = info->si->twoSided;
2588 /* make light list for this surface */
2589 CreateTraceLightsForSurface( num, &trace );
2592 verts = yDrawVerts + ds->firstVert;
2594 memset( avgColors, 0, sizeof( avgColors ) );
2596 /* walk the surface verts */
2597 for( i = 0; i < ds->numVerts; i++ )
2599 /* get vertex luxel */
2600 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2602 /* color the luxel with raw lightmap num? */
2604 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2606 /* color the luxel with luxel origin? */
2607 else if( debugOrigin )
2609 VectorSubtract( info->maxs, info->mins, temp );
2610 VectorScale( temp, (1.0f / 255.0f), temp );
2611 VectorSubtract( origin, lm->mins, temp2 );
2612 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2613 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2614 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2617 /* color the luxel with the normal */
2618 else if( normalmap )
2620 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2621 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2622 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2625 /* illuminate the vertex */
2628 /* clear vertex luxel */
2629 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2631 /* try at initial origin */
2632 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2633 if( trace.cluster >= 0 )
2636 VectorCopy( verts[ i ].xyz, trace.origin );
2637 VectorCopy( verts[ i ].normal, trace.normal );
2641 dirt = DirtForSample( &trace );
2646 LightingAtSample( &trace, ds->vertexStyles, colors );
2649 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2652 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2655 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2656 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2657 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2661 /* is this sample bright enough? */
2662 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2663 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2664 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2665 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2667 /* nudge the sample point around a bit */
2668 for( x = 0; x < 4; x++ )
2670 /* two's complement 0, 1, -1, 2, -2, etc */
2671 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2673 for( y = 0; y < 4; y++ )
2675 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2677 for( z = 0; z < 4; z++ )
2679 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2682 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2683 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2684 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2686 /* try at nudged origin */
2687 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2688 if( trace.cluster < 0 )
2692 LightingAtSample( &trace, ds->vertexStyles, colors );
2695 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2698 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2701 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2702 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2705 /* bright enough? */
2706 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2707 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2708 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2709 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2716 /* add to average? */
2717 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2718 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2719 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2720 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2723 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2725 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2726 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2731 /* another happy customer */
2732 numVertsIlluminated++;
2735 /* set average color */
2738 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2739 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2743 VectorCopy( ambientColor, avgColors[ 0 ] );
2746 /* clean up and store vertex color */
2747 for( i = 0; i < ds->numVerts; i++ )
2749 /* get vertex luxel */
2750 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2752 /* store average in occluded vertexes */
2753 if( radVertLuxel[ 0 ] < 0.0f )
2755 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2757 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2758 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2761 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2766 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2769 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2770 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2773 if( bouncing || bounce == 0 || !bounceOnly )
2774 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2775 if( !info->si->noVertexLight )
2776 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2780 /* free light list */
2781 FreeTraceLights( &trace );
2783 /* return to sender */
2787 /* -----------------------------------------------------------------
2788 reconstitute vertex lighting from the luxels
2789 ----------------------------------------------------------------- */
2791 /* set styles from lightmap */
2792 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2793 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2795 /* get max search radius */
2797 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
2799 /* walk the surface verts */
2800 verts = yDrawVerts + ds->firstVert;
2801 for( i = 0; i < ds->numVerts; i++ )
2803 /* do each lightmap */
2804 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2807 if( lm->superLuxels[ lightmapNum ] == NULL )
2810 /* get luxel coords */
2811 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
2812 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
2815 else if( x >= lm->sw )
2819 else if( y >= lm->sh )
2822 /* get vertex luxels */
2823 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2824 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2826 /* color the luxel with the normal? */
2829 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2830 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2831 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2834 /* color the luxel with surface num? */
2835 else if( debugSurfaces )
2836 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2838 /* divine color from the superluxels */
2841 /* increasing radius */
2842 VectorClear( radVertLuxel );
2844 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
2846 /* sample within radius */
2847 for( sy = (y - radius); sy <= (y + radius); sy++ )
2849 if( sy < 0 || sy >= lm->sh )
2852 for( sx = (x - radius); sx <= (x + radius); sx++ )
2854 if( sx < 0 || sx >= lm->sw )
2857 /* get luxel particulars */
2858 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2859 cluster = SUPER_CLUSTER( sx, sy );
2863 /* testing: must be brigher than ambient color */
2864 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
2867 /* add its distinctiveness to our own */
2868 VectorAdd( radVertLuxel, luxel, radVertLuxel );
2869 samples += luxel[ 3 ];
2875 if( samples > 0.0f )
2876 VectorDivide( radVertLuxel, samples, radVertLuxel );
2878 VectorCopy( ambientColor, radVertLuxel );
2881 /* store into floating point storage */
2882 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2883 numVertsIlluminated++;
2885 /* store into bytes (for vertex approximation) */
2886 if( !info->si->noVertexLight )
2887 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
2894 /* -------------------------------------------------------------------------------
2896 light optimization (-fast)
2898 creates a list of lights that will affect a surface and stores it in tw
2899 this is to optimize surface lighting by culling out as many of the
2900 lights in the world as possible from further calculation
2902 ------------------------------------------------------------------------------- */
2906 determines opaque brushes in the world and find sky shaders for sunlight calculations
2909 void SetupBrushes( void )
2911 int i, j, b, compileFlags;
2914 bspBrushSide_t *side;
2915 bspShader_t *shader;
2920 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
2923 if( opaqueBrushes == NULL )
2924 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
2927 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
2928 numOpaqueBrushes = 0;
2930 /* walk the list of worldspawn brushes */
2931 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
2934 b = bspModels[ 0 ].firstBSPBrush + i;
2935 brush = &bspBrushes[ b ];
2937 /* check all sides */
2940 for( j = 0; j < brush->numSides && inside; j++ )
2942 /* do bsp shader calculations */
2943 side = &bspBrushSides[ brush->firstSide + j ];
2944 shader = &bspShaders[ side->shaderNum ];
2946 /* get shader info */
2947 si = ShaderInfoForShader( shader->shader );
2951 /* or together compile flags */
2952 compileFlags |= si->compileFlags;
2955 /* determine if this brush is opaque to light */
2956 if( !(compileFlags & C_TRANSLUCENT) )
2958 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
2964 /* emit some statistics */
2965 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
2972 determines if two clusters are visible to each other using the PVS
2975 qboolean ClusterVisible( int a, int b )
2977 int portalClusters, leafBytes;
2982 if( a < 0 || b < 0 )
2990 if( numBSPVisBytes <=8 )
2994 portalClusters = ((int *) bspVisBytes)[ 0 ];
2995 leafBytes = ((int*) bspVisBytes)[ 1 ];
2996 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
2999 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3008 borrowed from vlight.c
3011 int PointInLeafNum_r( vec3_t point, int nodenum )
3019 while( nodenum >= 0 )
3021 node = &bspNodes[ nodenum ];
3022 plane = &bspPlanes[ node->planeNum ];
3023 dist = DotProduct( point, plane->normal ) - plane->dist;
3025 nodenum = node->children[ 0 ];
3026 else if( dist < -0.1 )
3027 nodenum = node->children[ 1 ];
3030 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3031 if( bspLeafs[ leafnum ].cluster != -1 )
3033 nodenum = node->children[ 1 ];
3037 leafnum = -nodenum - 1;
3045 borrowed from vlight.c
3048 int PointInLeafNum( vec3_t point )
3050 return PointInLeafNum_r( point, 0 );
3056 ClusterVisibleToPoint() - ydnar
3057 returns qtrue if point can "see" cluster
3060 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3065 /* get leafNum for point */
3066 pointCluster = ClusterForPoint( point );
3067 if( pointCluster < 0 )
3071 return ClusterVisible( pointCluster, cluster );
3077 ClusterForPoint() - ydnar
3078 returns the pvs cluster for point
3081 int ClusterForPoint( vec3_t point )
3086 /* get leafNum for point */
3087 leafNum = PointInLeafNum( point );
3091 /* return the cluster */
3092 return bspLeafs[ leafNum ].cluster;
3098 ClusterForPointExt() - ydnar
3099 also takes brushes into account for occlusion testing
3102 int ClusterForPointExt( vec3_t point, float epsilon )
3104 int i, j, b, leafNum, cluster;
3107 int *brushes, numBSPBrushes;
3113 /* get leaf for point */
3114 leafNum = PointInLeafNum( point );
3117 leaf = &bspLeafs[ leafNum ];
3119 /* get the cluster */
3120 cluster = leaf->cluster;
3124 /* transparent leaf, so check point against all brushes in the leaf */
3125 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3126 numBSPBrushes = leaf->numBSPLeafBrushes;
3127 for( i = 0; i < numBSPBrushes; i++ )
3131 if( b > maxOpaqueBrush )
3133 brush = &bspBrushes[ b ];
3134 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3137 /* check point against all planes */
3139 for( j = 0; j < brush->numSides && inside; j++ )
3141 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3142 dot = DotProduct( point, plane->normal );
3148 /* if inside, return bogus cluster */
3153 /* if the point made it this far, it's not inside any opaque brushes */
3160 ClusterForPointExtFilter() - ydnar
3161 adds cluster checking against a list of known valid clusters
3164 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3169 /* get cluster for point */
3170 cluster = ClusterForPointExt( point, epsilon );
3172 /* check if filtering is necessary */
3173 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3177 for( i = 0; i < numClusters; i++ )
3179 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3190 ShaderForPointInLeaf() - ydnar
3191 checks a point against all brushes in a leaf, returning the shader of the brush
3192 also sets the cumulative surface and content flags for the brush hit
3195 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3200 int *brushes, numBSPBrushes;
3203 bspBrushSide_t *side;
3205 bspShader_t *shader;
3206 int allSurfaceFlags, allContentFlags;
3209 /* clear things out first */
3216 leaf = &bspLeafs[ leafNum ];
3218 /* transparent leaf, so check point against all brushes in the leaf */
3219 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3220 numBSPBrushes = leaf->numBSPLeafBrushes;
3221 for( i = 0; i < numBSPBrushes; i++ )
3224 brush = &bspBrushes[ brushes[ i ] ];
3226 /* check point against all planes */
3228 allSurfaceFlags = 0;
3229 allContentFlags = 0;
3230 for( j = 0; j < brush->numSides && inside; j++ )
3232 side = &bspBrushSides[ brush->firstSide + j ];
3233 plane = &bspPlanes[ side->planeNum ];
3234 dot = DotProduct( point, plane->normal );
3240 shader = &bspShaders[ side->shaderNum ];
3241 allSurfaceFlags |= shader->surfaceFlags;
3242 allContentFlags |= shader->contentFlags;
3246 /* handle if inside */
3249 /* if there are desired flags, check for same and continue if they aren't matched */
3250 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3252 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3255 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3256 *surfaceFlags = allSurfaceFlags;
3257 *contentFlags = allContentFlags;
3258 return brush->shaderNum;
3262 /* if the point made it this far, it's not inside any brushes */
3270 chops a bounding box by the plane defined by origin and normal
3271 returns qfalse if the bounds is entirely clipped away
3273 this is not exactly the fastest way to do this...
3276 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3278 /* FIXME: rewrite this so it doesn't use bloody brushes */
3286 calculates each light's effective envelope,
3287 taking into account brightness, type, and pvs.
3290 #define LIGHT_EPSILON 0.125f
3291 #define LIGHT_NUDGE 2.0f
3293 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3295 int i, x, y, z, x1, y1, z1;
3296 light_t *light, *light2, **owner;
3298 vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
3299 float radius, intensity;
3300 light_t *buckets[ 256 ];
3303 /* early out for weird cases where there are no lights */
3304 if( lights == NULL )
3308 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3312 numCulledLights = 0;
3314 while( *owner != NULL )
3319 /* handle negative lights */
3320 if( light->photons < 0.0f || light->add < 0.0f )
3322 light->photons *= -1.0f;
3323 light->add *= -1.0f;
3324 light->flags |= LIGHT_NEGATIVE;
3328 if( light->type == EMIT_SUN )
3332 light->envelope = MAX_WORLD_COORD * 8.0f;
3333 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3334 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3337 /* everything else */
3340 /* get pvs cluster for light */
3341 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3343 /* invalid cluster? */
3344 if( light->cluster < 0 )
3346 /* nudge the sample point around a bit */
3347 for( x = 0; x < 4; x++ )
3349 /* two's complement 0, 1, -1, 2, -2, etc */
3350 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3352 for( y = 0; y < 4; y++ )
3354 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3356 for( z = 0; z < 4; z++ )
3358 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3361 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3362 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3363 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3365 /* try at nudged origin */
3366 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3367 if( light->cluster < 0 )
3371 VectorCopy( origin, light->origin );
3377 /* only calculate for lights in pvs and outside of opaque brushes */
3378 if( light->cluster >= 0 )
3380 /* set light fast flag */
3382 light->flags |= LIGHT_FAST_TEMP;
3384 light->flags &= ~LIGHT_FAST_TEMP;
3385 if( light->si && light->si->noFast )
3386 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3388 /* clear light envelope */
3389 light->envelope = 0;
3391 /* handle area lights */
3392 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3394 /* ugly hack to calculate extent for area lights, but only done once */
3395 VectorScale( light->normal, -1.0f, dir );
3396 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3400 VectorMA( light->origin, radius, light->normal, origin );
3401 factor = PointToPolygonFormFactor( origin, dir, light->w );
3404 if( (factor * light->add) <= light->falloffTolerance )
3405 light->envelope = radius;
3408 /* check for fast mode */
3409 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3410 light->envelope = MAX_WORLD_COORD * 8.0f;
3415 intensity = light->photons;
3419 if( light->envelope <= 0.0f )
3421 /* solve distance for non-distance lights */
3422 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3423 light->envelope = MAX_WORLD_COORD * 8.0f;
3425 /* solve distance for linear lights */
3426 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3427 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3428 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3431 add = angle * light->photons * linearScale - (dist * light->fade);
3432 T = (light->photons * linearScale) - (dist * light->fade);
3433 T + (dist * light->fade) = (light->photons * linearScale);
3434 dist * light->fade = (light->photons * linearScale) - T;
3435 dist = ((light->photons * linearScale) - T) / light->fade;
3438 /* solve for inverse square falloff */
3440 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3443 add = light->photons / (dist * dist);
3444 T = light->photons / (dist * dist);
3445 T * (dist * dist) = light->photons;
3446 dist = sqrt( light->photons / T );
3450 /* chop radius against pvs */
3453 ClearBounds( mins, maxs );
3455 /* check all leaves */
3456 for( i = 0; i < numBSPLeafs; i++ )
3459 leaf = &bspLeafs[ i ];
3462 if( leaf->cluster < 0 )
3464 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3467 /* add this leafs bbox to the bounds */
3468 VectorCopy( leaf->mins, origin );
3469 AddPointToBounds( origin, mins, maxs );
3470 VectorCopy( leaf->maxs, origin );
3471 AddPointToBounds( origin, mins, maxs );
3474 /* test to see if bounds encompass light */
3475 for( i = 0; i < 3; i++ )
3477 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3479 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3480 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3481 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3482 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3483 AddPointToBounds( light->origin, mins, maxs );
3487 /* chop the bounds by a plane for area lights and spotlights */
3488 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3489 ChopBounds( mins, maxs, light->origin, light->normal );
3492 VectorCopy( mins, light->mins );
3493 VectorCopy( maxs, light->maxs );
3495 /* reflect bounds around light origin */
3496 //% VectorMA( light->origin, -1.0f, origin, origin );
3497 VectorScale( light->origin, 2, origin );
3498 VectorSubtract( origin, maxs, origin );
3499 AddPointToBounds( origin, mins, maxs );
3500 //% VectorMA( light->origin, -1.0f, mins, origin );
3501 VectorScale( light->origin, 2, origin );
3502 VectorSubtract( origin, mins, origin );
3503 AddPointToBounds( origin, mins, maxs );
3505 /* calculate spherical bounds */
3506 VectorSubtract( maxs, light->origin, dir );
3507 radius = (float) VectorLength( dir );
3509 /* if this radius is smaller than the envelope, then set the envelope to it */
3510 if( radius < light->envelope )
3512 light->envelope = radius;
3513 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3516 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3519 /* add grid/surface only check */
3522 if( !(light->flags & LIGHT_GRID) )
3523 light->envelope = 0.0f;
3527 if( !(light->flags & LIGHT_SURFACES) )
3528 light->envelope = 0.0f;
3533 if( light->cluster < 0 || light->envelope <= 0.0f )
3536 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3538 /* delete the light */
3540 *owner = light->next;
3541 if( light->w != NULL )
3548 /* square envelope */
3549 light->envelope2 = (light->envelope * light->envelope);
3551 /* increment light count */
3554 /* set next light */
3555 owner = &((**owner).next);
3558 /* bucket sort lights by style */
3559 memset( buckets, 0, sizeof( buckets ) );
3561 for( light = lights; light != NULL; light = light2 )
3563 /* get next light */
3564 light2 = light->next;
3566 /* filter into correct bucket */
3567 light->next = buckets[ light->style ];
3568 buckets[ light->style ] = light;
3570 /* if any styled light is present, automatically set nocollapse */
3571 if( light->style != LS_NORMAL )
3575 /* filter back into light list */
3577 for( i = 255; i >= 0; i-- )
3580 for( light = buckets[ i ]; light != NULL; light = light2 )
3582 light2 = light->next;
3583 light->next = lights;
3588 /* emit some statistics */
3589 Sys_Printf( "%9d total lights\n", numLights );
3590 Sys_Printf( "%9d culled lights\n", numCulledLights );
3596 CreateTraceLightsForBounds()
3597 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3600 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3604 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3605 float radius, dist, length;
3608 /* potential pre-setup */
3609 if( numLights == 0 )
3610 SetupEnvelopes( qfalse, fast );
3613 //% 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 ] );
3615 /* allocate the light list */
3616 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3617 trace->numLights = 0;
3619 /* calculate spherical bounds */
3620 VectorAdd( mins, maxs, origin );
3621 VectorScale( origin, 0.5f, origin );
3622 VectorSubtract( maxs, origin, dir );
3623 radius = (float) VectorLength( dir );
3625 /* get length of normal vector */
3626 if( normal != NULL )
3627 length = VectorLength( normal );
3630 normal = nullVector;
3634 /* test each light and see if it reaches the sphere */
3635 /* note: the attenuation code MUST match LightingAtSample() */
3636 for( light = lights; light; light = light->next )
3638 /* check zero sized envelope */
3639 if( light->envelope <= 0 )
3641 lightsEnvelopeCulled++;
3646 if( !(light->flags & flags) )
3649 /* sunlight skips all this nonsense */
3650 if( light->type != EMIT_SUN )
3656 /* check against pvs cluster */
3657 if( numClusters > 0 && clusters != NULL )
3659 for( i = 0; i < numClusters; i++ )
3661 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3666 if( i == numClusters )
3668 lightsClusterCulled++;
3673 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3674 VectorSubtract( light->origin, origin, dir );
3675 dist = VectorLength( dir );
3676 dist -= light->envelope;
3680 lightsEnvelopeCulled++;
3684 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3687 for( i = 0; i < 3; i++ )
3689 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3694 lightsBoundsCulled++;
3700 /* planar surfaces (except twosided surfaces) have a couple more checks */
3701 if( length > 0.0f && trace->twoSided == qfalse )
3703 /* lights coplanar with a surface won't light it */
3704 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3706 lightsPlaneCulled++;
3710 /* check to see if light is behind the plane */
3711 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3713 lightsPlaneCulled++;
3718 /* add this light */
3719 trace->lights[ trace->numLights++ ] = light;
3722 /* make last night null */
3723 trace->lights[ trace->numLights ] = NULL;
3728 void FreeTraceLights( trace_t *trace )
3730 if( trace->lights != NULL )
3731 free( trace->lights );
3737 CreateTraceLightsForSurface()
3738 creates a list of lights that can potentially affect a drawsurface
3741 void CreateTraceLightsForSurface( int num, trace_t *trace )
3744 vec3_t mins, maxs, normal;
3746 bspDrawSurface_t *ds;
3747 surfaceInfo_t *info;
3754 /* get drawsurface and info */
3755 ds = &bspDrawSurfaces[ num ];
3756 info = &surfaceInfos[ num ];
3758 /* get the mins/maxs for the dsurf */
3759 ClearBounds( mins, maxs );
3760 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3761 for( i = 0; i < ds->numVerts; i++ )
3763 dv = &yDrawVerts[ ds->firstVert + i ];
3764 AddPointToBounds( dv->xyz, mins, maxs );
3765 if( !VectorCompare( dv->normal, normal ) )
3766 VectorClear( normal );
3769 /* create the lights for the bounding box */
3770 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
3773 /////////////////////////////////////////////////////////////
3775 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
3776 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
3777 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
3778 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
3780 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
3781 static int numFloodVectors = 0;
3783 void SetupFloodLight( void )
3786 float angle, elevation, angleStep, elevationStep;
3788 double v1,v2,v3,v4,v5;
3791 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
3793 /* calculate angular steps */
3794 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
3795 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
3799 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
3801 /* iterate elevation */
3802 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
3804 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
3805 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
3806 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
3811 /* emit some statistics */
3812 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
3815 value = ValueForKey( &entities[ 0 ], "_floodlight" );
3817 if( value[ 0 ] != '\0' )
3820 v4=floodlightDistance;
3821 v5=floodlightIntensity;
3823 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
3825 floodlightRGB[0]=v1;
3826 floodlightRGB[1]=v2;
3827 floodlightRGB[2]=v3;
3829 if (VectorLength(floodlightRGB)==0)
3831 VectorSet(floodlightRGB,240,240,255);
3837 floodlightDistance=v4;
3838 floodlightIntensity=v5;
3840 floodlighty = qtrue;
3841 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3845 VectorSet(floodlightRGB,240,240,255);
3846 //floodlighty = qtrue;
3847 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3849 VectorNormalize(floodlightRGB,floodlightRGB);
3852 //27 - lighttracer style ambient occlusion light hack.
3853 //Kudos to the dirtmapping author for most of this source.
3854 void FloodLightRawLightmap( int rawLightmapNum )
3856 int i, x, y, sx, sy, *cluster;
3857 float *origin, *normal, *floodlight, *floodlight2, average, samples;
3859 surfaceInfo_t *info;
3862 /* bail if this number exceeds the number of raw lightmaps */
3863 if( rawLightmapNum >= numRawLightmaps )
3867 lm = &rawLightmaps[ rawLightmapNum ];
3869 memset(&trace,0,sizeof(trace_t));
3871 trace.testOcclusion = qtrue;
3872 trace.forceSunlight = qfalse;
3873 trace.twoSided = qtrue;
3874 trace.recvShadows = lm->recvShadows;
3875 trace.numSurfaces = lm->numLightSurfaces;
3876 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
3877 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
3878 trace.testAll = qfalse;
3879 trace.distance = 1024;
3881 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
3882 //trace.twoSided = qfalse;
3883 for( i = 0; i < trace.numSurfaces; i++ )
3886 info = &surfaceInfos[ trace.surfaces[ i ] ];
3888 /* check twosidedness */
3889 if( info->si->twoSided )
3891 trace.twoSided = qtrue;
3897 for( y = 0; y < lm->sh; y++ )
3899 for( x = 0; x < lm->sw; x++ )
3902 cluster = SUPER_CLUSTER( x, y );
3903 origin = SUPER_ORIGIN( x, y );
3904 normal = SUPER_NORMAL( x, y );
3905 floodlight = SUPER_FLOODLIGHT( x, y );
3907 /* set default dirt */
3910 /* only look at mapped luxels */
3915 trace.cluster = *cluster;
3916 VectorCopy( origin, trace.origin );
3917 VectorCopy( normal, trace.normal );
3922 *floodlight = FloodLightForSample( &trace );
3926 /* testing no filtering */
3930 for( y = 0; y < lm->sh; y++ )
3932 for( x = 0; x < lm->sw; x++ )
3935 cluster = SUPER_CLUSTER( x, y );
3936 floodlight = SUPER_FLOODLIGHT( x, y );
3938 /* filter dirt by adjacency to unmapped luxels */
3939 average = *floodlight;
3941 for( sy = (y - 1); sy <= (y + 1); sy++ )
3943 if( sy < 0 || sy >= lm->sh )
3946 for( sx = (x - 1); sx <= (x + 1); sx++ )
3948 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
3951 /* get neighboring luxel */
3952 cluster = SUPER_CLUSTER( sx, sy );
3953 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
3954 if( *cluster < 0 || *floodlight2 <= 0.0f )
3958 average += *floodlight2;
3963 if( samples <= 0.0f )
3968 if( samples <= 0.0f )
3972 *floodlight = average / samples;
3978 FloodLightForSample()
3979 calculates floodlight value for a given sample
3980 once again, kudos to the dirtmapping coder
3982 float FloodLightForSample( trace_t *trace )
3988 float gatherLight, outLight;
3989 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
3997 if( trace == NULL || trace->cluster < 0 )
4002 dd = floodlightDistance;
4003 VectorCopy( trace->normal, normal );
4005 /* check if the normal is aligned to the world-up */
4006 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f )
4008 if( normal[ 2 ] == 1.0f )
4010 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4011 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4013 else if( normal[ 2 ] == -1.0f )
4015 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4016 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4021 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4022 CrossProduct( normal, worldUp, myRt );
4023 VectorNormalize( myRt, myRt );
4024 CrossProduct( myRt, normal, myUp );
4025 VectorNormalize( myUp, myUp );
4028 /* iterate through ordered vectors */
4029 for( i = 0; i < numFloodVectors; i++ )
4031 if (floodlight_lowquality==qtrue)
4033 if (rand()%10 != 0 ) continue;
4038 /* transform vector into tangent space */
4039 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4040 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4041 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4044 VectorMA( trace->origin, dd, direction, trace->end );
4046 //VectorMA( trace->origin, 1, direction, trace->origin );
4048 SetupTrace( trace );
4053 if (trace->compileFlags & C_SKY )
4057 else if ( trace->opaque )
4059 VectorSubtract( trace->hit, trace->origin, displacement );
4060 d=VectorLength( displacement );
4062 // d=trace->distance;
4063 //if (d>256) gatherDirt+=1;
4065 if (contribution>1) contribution=1.0f;
4067 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4070 gatherLight+=contribution;
4074 if( gatherLight <= 0.0f )
4082 outLight=gatherLight;
4083 if( outLight > 1.0f )
4086 /* return to sender */