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 /* 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 ) {
73 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
76 if ( lightmapExposure == 0 ) {
77 /* clamp with color normalization */
79 if ( sample[ 1 ] > max ) {
82 if ( sample[ 2 ] > max ) {
86 VectorScale( sample, ( 255.0f / max ), sample );
91 inv = 1.f / lightmapExposure;
95 if ( sample[ 1 ] > max ) {
98 if ( sample[ 2 ] > max ) {
102 dif = ( 1 - exp( -max * inv ) ) * 255;
112 for ( i = 0; i < 3; i++ )
119 /* compensate for ingame overbrighting/bitshifting */
120 VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );
123 if ( lightmapsRGB ) {
124 sample[0] = floor( Image_sRGBFloatFromLinearFloat( sample[0] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
125 sample[1] = floor( Image_sRGBFloatFromLinearFloat( sample[1] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
126 sample[2] = floor( Image_sRGBFloatFromLinearFloat( sample[2] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
130 colorBytes[ 0 ] = sample[ 0 ];
131 colorBytes[ 1 ] = sample[ 1 ];
132 colorBytes[ 2 ] = sample[ 2 ];
137 /* -------------------------------------------------------------------------------
139 this section deals with phong shading (normal interpolation across brush faces)
141 ------------------------------------------------------------------------------- */
145 smooths together coincident vertex normals across the bsp
148 #define MAX_SAMPLES 256
149 #define THETA_EPSILON 0.000001
150 #define EQUAL_NORMAL_EPSILON 0.01
152 void SmoothNormals( void ){
153 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
154 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
155 bspDrawSurface_t *ds;
159 vec3_t average, diff;
160 int indexes[ MAX_SAMPLES ];
161 vec3_t votes[ MAX_SAMPLES ];
164 /* allocate shade angle table */
165 shadeAngles = safe_malloc0( numBSPDrawVerts * sizeof( float ) );
167 /* allocate smoothed table */
168 cs = ( numBSPDrawVerts / 8 ) + 1;
169 smoothed = safe_malloc0( cs );
171 /* set default shade angle */
172 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
175 /* run through every surface and flag verts belonging to non-lightmapped surfaces
176 and set per-vertex smoothing angle */
177 for ( i = 0; i < numBSPDrawSurfaces; i++ )
180 ds = &bspDrawSurfaces[ i ];
182 /* get shader for shade angle */
183 si = surfaceInfos[ i ].si;
184 if ( si->shadeAngleDegrees ) {
185 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
188 shadeAngle = defaultShadeAngle;
190 if ( shadeAngle > maxShadeAngle ) {
191 maxShadeAngle = shadeAngle;
195 for ( j = 0; j < ds->numVerts; j++ )
197 f = ds->firstVert + j;
198 shadeAngles[ f ] = shadeAngle;
199 if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
200 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
204 /* ydnar: optional force-to-trisoup */
205 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
206 ds->surfaceType = MST_TRIANGLE_SOUP;
207 ds->lightmapNum[ 0 ] = -3;
211 /* bail if no surfaces have a shade angle */
212 if ( maxShadeAngle == 0 ) {
220 start = I_FloatTime();
222 /* go through the list of vertexes */
223 for ( i = 0; i < numBSPDrawVerts; i++ )
226 f = 10 * i / numBSPDrawVerts;
229 Sys_Printf( "%i...", f );
232 /* already smoothed? */
233 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
238 VectorClear( average );
242 /* build a table of coincident vertexes */
243 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
245 /* already smoothed? */
246 if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
251 if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
255 /* use smallest shade angle */
256 shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
258 /* check shade angle */
259 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
263 else if ( dot < -1.0 ) {
266 testAngle = acos( dot ) + THETA_EPSILON;
267 if ( testAngle >= shadeAngle ) {
268 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
271 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
273 /* add to the list */
274 indexes[ numVerts++ ] = j;
277 smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
279 /* see if this normal has already been voted */
280 for ( k = 0; k < numVotes; k++ )
282 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
283 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
284 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
285 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
290 /* add a new vote? */
291 if ( k == numVotes && numVotes < MAX_SAMPLES ) {
292 VectorAdd( average, bspDrawVerts[ j ].normal, average );
293 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
298 /* don't average for less than 2 verts */
299 if ( numVerts < 2 ) {
304 if ( VectorNormalize( average, average ) > 0 ) {
306 for ( j = 0; j < numVerts; j++ )
307 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
311 /* free the tables */
316 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
321 /* -------------------------------------------------------------------------------
323 this section deals with phong shaded lightmap tracing
325 ------------------------------------------------------------------------------- */
327 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
331 calculates the st tangent vectors for normalmapping
334 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
340 /* calculate barycentric basis for the triangle */
341 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 ] );
342 if ( fabs( bb ) < 0.00000001f ) {
347 for ( i = 0; i < numVerts; i++ )
349 /* calculate s tangent vector */
350 s = dv[ i ]->st[ 0 ] + 10.0f;
351 t = dv[ i ]->st[ 1 ];
352 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
353 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
354 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
356 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
357 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
358 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
360 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
361 VectorNormalize( stv[ i ], stv[ i ] );
363 /* calculate t tangent vector */
364 s = dv[ i ]->st[ 0 ];
365 t = dv[ i ]->st[ 1 ] + 10.0f;
366 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
367 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
368 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
370 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
371 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
372 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
374 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
375 VectorNormalize( ttv[ i ], ttv[ i ] );
378 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
379 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
382 /* return to caller */
391 perterbs the normal by the shader's normalmap in tangent space
394 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
400 VectorCopy( dv->normal, pNormal );
402 /* sample normalmap */
403 if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
407 /* remap sampled normal from [0,255] to [-1,-1] */
408 for ( i = 0; i < 3; i++ )
409 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
411 /* scale tangent vectors and add to original normal */
412 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
413 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
414 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
416 /* renormalize and return */
417 VectorNormalize( pNormal, pNormal );
424 maps a luxel for triangle bv at
428 #define BOGUS_NUDGE -99999.0f
430 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 ] ){
431 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
432 float *luxel, *origin, *normal, d, lightmapSampleOffset;
439 vec4_t sideplane, hostplane;
444 static float nudges[][ 2 ] =
446 //%{ 0, 0 }, /* try center first */
447 { -NUDGE, 0 }, /* left */
448 { NUDGE, 0 }, /* right */
449 { 0, NUDGE }, /* up */
450 { 0, -NUDGE }, /* down */
451 { -NUDGE, NUDGE }, /* left/up */
452 { NUDGE, -NUDGE }, /* right/down */
453 { NUDGE, NUDGE }, /* right/up */
454 { -NUDGE, -NUDGE }, /* left/down */
455 { BOGUS_NUDGE, BOGUS_NUDGE }
459 /* find luxel xy coords (fixme: subtract 0.5?) */
460 x = dv->lightmap[ 0 ][ 0 ];
461 y = dv->lightmap[ 0 ][ 1 ];
465 else if ( x >= lm->sw ) {
471 else if ( y >= lm->sh ) {
475 /* set shader and cluster list */
476 if ( info != NULL ) {
478 numClusters = info->numSurfaceClusters;
479 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
488 /* get luxel, origin, cluster, and normal */
489 luxel = SUPER_LUXEL( 0, x, y );
490 origin = SUPER_ORIGIN( x, y );
491 normal = SUPER_NORMAL( x, y );
492 cluster = SUPER_CLUSTER( x, y );
494 /* don't attempt to remap occluded luxels for planar surfaces */
495 if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
499 /* only average the normal for premapped luxels */
500 else if ( ( *cluster ) >= 0 ) {
501 /* do bumpmap calculations */
503 PerturbNormal( dv, si, pNormal, stv, ttv );
506 VectorCopy( dv->normal, pNormal );
509 /* add the additional normal data */
510 VectorAdd( normal, pNormal, normal );
515 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
519 /* axial lightmap projection */
520 if ( lm->vecs != NULL ) {
521 /* calculate an origin for the sample from the lightmap vectors */
522 VectorCopy( lm->origin, origin );
523 for ( i = 0; i < 3; i++ )
525 /* add unless it's the axis, which is taken care of later */
526 if ( i == lm->axisNum ) {
529 origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
532 /* project the origin onto the plane */
533 d = DotProduct( origin, plane ) - plane[ 3 ];
534 d /= plane[ lm->axisNum ];
535 origin[ lm->axisNum ] -= d;
538 /* non axial lightmap projection (explicit xyz) */
540 VectorCopy( dv->xyz, origin );
543 //////////////////////
544 //27's test to make sure samples stay within the triangle boundaries
545 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
546 //2) if it does, nudge it onto the correct side.
548 if ( worldverts != NULL && lightmapTriangleCheck ) {
549 for ( j = 0; j < 3; j++ )
551 VectorCopy( worldverts[j],cverts[j] );
553 PlaneFromPoints( hostplane,cverts[0],cverts[1],cverts[2] );
555 for ( j = 0; j < 3; j++ )
557 for ( i = 0; i < 3; i++ )
559 //build plane using 2 edges and a normal
560 next = ( i + 1 ) % 3;
562 VectorCopy( cverts[next],temp );
563 VectorAdd( temp,hostplane,temp );
564 PlaneFromPoints( sideplane,cverts[i],cverts[ next ], temp );
566 //planetest sample point
567 e = DotProduct( origin,sideplane );
568 e = e - sideplane[3];
571 //VectorClear(origin);
572 //Move the sample point back inside triangle bounds
573 origin[0] -= sideplane[0] * ( e + 1 );
574 origin[1] -= sideplane[1] * ( e + 1 );
575 origin[2] -= sideplane[2] * ( e + 1 );
577 VectorClear( origin );
584 ////////////////////////
586 /* planar surfaces have precalculated lightmap vectors for nudging */
587 if ( lm->plane != NULL ) {
588 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
589 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
590 VectorCopy( lm->plane, vecs[ 2 ] );
593 /* non-planar surfaces must calculate them */
596 if ( plane != NULL ) {
597 VectorCopy( plane, vecs[ 2 ] );
600 VectorCopy( dv->normal, vecs[ 2 ] );
602 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
605 /* push the origin off the surface a bit */
607 lightmapSampleOffset = si->lightmapSampleOffset;
610 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
612 if ( lm->axisNum < 0 ) {
613 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
615 else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
616 origin[ lm->axisNum ] -= lightmapSampleOffset;
619 origin[ lm->axisNum ] += lightmapSampleOffset;
622 VectorCopy( origin,origintwo );
623 if ( lightmapExtraVisClusterNudge ) {
624 origintwo[0] += vecs[2][0];
625 origintwo[1] += vecs[2][1];
626 origintwo[2] += vecs[2][2];
630 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
632 /* another retarded hack, storing nudge count in luxel[ 1 ] */
635 /* point in solid? (except in dark mode) */
636 if ( pointCluster < 0 && dark == qfalse ) {
637 /* nudge the the location around */
639 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
641 /* nudge the vector around a bit */
642 for ( i = 0; i < 3; i++ )
644 /* set nudged point*/
645 nudged[ i ] = origintwo[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
649 /* get pvs cluster */
650 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
651 if ( pointCluster >= 0 ) {
652 VectorCopy( nudged, origin );
658 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
659 if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
660 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
661 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
662 if ( pointCluster >= 0 ) {
663 VectorCopy( nudged, origin );
669 if ( pointCluster < 0 ) {
670 ( *cluster ) = CLUSTER_OCCLUDED;
671 VectorClear( origin );
672 VectorClear( normal );
678 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
680 /* do bumpmap calculations */
682 PerturbNormal( dv, si, pNormal, stv, ttv );
685 VectorCopy( dv->normal, pNormal );
688 /* store the cluster and normal */
689 ( *cluster ) = pointCluster;
690 VectorCopy( pNormal, normal );
692 /* store explicit mapping pass and implicit mapping pass */
707 recursively subdivides a triangle until its edges are shorter
708 than the distance between two luxels (thanks jc :)
711 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 ] ){
712 bspDrawVert_t mid, *dv2[ 3 ];
716 /* map the vertexes */
718 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
719 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
720 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
726 float *a, *b, dx, dy, dist, maxDist;
729 /* find the longest edge and split it */
732 for ( i = 0; i < 3; i++ )
735 a = dv[ i ]->lightmap[ 0 ];
736 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
739 dx = a[ 0 ] - b[ 0 ];
740 dy = a[ 1 ] - b[ 1 ];
741 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
744 if ( dist > maxDist ) {
750 /* try to early out */
751 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
756 /* split the longest edge and map it */
757 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
758 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
760 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
761 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
763 /* recurse to first triangle */
764 VectorCopy( dv, dv2 );
766 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
768 /* recurse to second triangle */
769 VectorCopy( dv, dv2 );
770 dv2[ ( max + 1 ) % 3 ] = ∣
771 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
778 seed function for MapTriangle_r()
779 requires a cw ordered triangle
782 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
785 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
786 vec3_t worldverts[ 3 ];
789 /* get plane if possible */
790 if ( lm->plane != NULL ) {
791 VectorCopy( lm->plane, plane );
792 plane[ 3 ] = lm->plane[ 3 ];
795 /* otherwise make one from the points */
796 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
800 /* check to see if we need to calculate texture->world tangent vectors */
801 if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
811 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
812 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
813 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
815 /* map the vertexes */
816 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
817 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
818 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
820 /* 2002-11-20: prefer axial triangle edges */
822 /* subdivide the triangle */
823 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
827 for ( i = 0; i < 3; i++ )
830 bspDrawVert_t *dv2[ 3 ];
834 a = dv[ i ]->lightmap[ 0 ];
835 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
837 /* make degenerate triangles for mapping edges */
838 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
840 dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
841 dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
843 /* map the degenerate triangle */
844 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
855 recursively subdivides a quad until its edges are shorter
856 than the distance between two luxels
859 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 ] ){
860 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
867 float *a, *b, dx, dy, dist, maxDist;
870 /* find the longest edge and split it */
873 for ( i = 0; i < 4; i++ )
876 a = dv[ i ]->lightmap[ 0 ];
877 b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
880 dx = a[ 0 ] - b[ 0 ];
881 dy = a[ 1 ] - b[ 1 ];
882 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
885 if ( dist > maxDist ) {
891 /* try to early out */
892 if ( max < 0 || maxDist <= subdivideThreshold ) {
897 /* we only care about even/odd edges */
900 /* split the longest edges */
901 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
902 LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
904 /* map the vertexes */
905 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
906 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
910 /* recurse to first quad */
912 dv2[ 1 ] = &mid[ 0 ];
913 dv2[ 2 ] = &mid[ 1 ];
915 MapQuad_r( lm, info, dv2, plane, stv, ttv );
917 /* recurse to second quad */
918 dv2[ 0 ] = &mid[ 0 ];
921 dv2[ 3 ] = &mid[ 1 ];
922 MapQuad_r( lm, info, dv2, plane, stv, ttv );
928 /* recurse to first quad */
931 dv2[ 2 ] = &mid[ 0 ];
932 dv2[ 3 ] = &mid[ 1 ];
933 MapQuad_r( lm, info, dv2, plane, stv, ttv );
935 /* recurse to second quad */
936 dv2[ 0 ] = &mid[ 1 ];
937 dv2[ 1 ] = &mid[ 0 ];
940 MapQuad_r( lm, info, dv2, plane, stv, ttv );
948 seed function for MapQuad_r()
949 requires a cw ordered triangle quad
952 #define QUAD_PLANAR_EPSILON 0.5f
954 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
957 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
960 /* get plane if possible */
961 if ( lm->plane != NULL ) {
962 VectorCopy( lm->plane, plane );
963 plane[ 3 ] = lm->plane[ 3 ];
966 /* otherwise make one from the points */
967 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
971 /* 4th point must fall on the plane */
972 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
973 if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
977 /* check to see if we need to calculate texture->world tangent vectors */
978 if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
988 /* map the vertexes */
989 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
990 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
991 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
992 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
994 /* subdivide the quad */
995 MapQuad_r( lm, info, dv, plane, stv, ttv );
1003 maps the locations, normals, and pvs clusters for a raw lightmap
1006 #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)
1008 void MapRawLightmap( int rawLightmapNum ){
1009 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1010 float *luxel, *origin, *normal, samples, radius, pass;
1012 bspDrawSurface_t *ds;
1013 surfaceInfo_t *info;
1014 mesh_t src, *subdivided, *mesh;
1015 bspDrawVert_t *verts, *dv[ 4 ], fake;
1018 /* bail if this number exceeds the number of raw lightmaps */
1019 if ( rawLightmapNum >= numRawLightmaps ) {
1024 lm = &rawLightmaps[ rawLightmapNum ];
1026 /* -----------------------------------------------------------------
1027 map referenced surfaces onto the raw lightmap
1028 ----------------------------------------------------------------- */
1030 /* walk the list of surfaces on this raw lightmap */
1031 for ( n = 0; n < lm->numLightSurfaces; n++ )
1033 /* with > 1 surface per raw lightmap, clear occluded */
1035 for ( y = 0; y < lm->sh; y++ )
1037 for ( x = 0; x < lm->sw; x++ )
1040 cluster = SUPER_CLUSTER( x, y );
1041 if ( *cluster < 0 ) {
1042 *cluster = CLUSTER_UNMAPPED;
1049 num = lightSurfaces[ lm->firstLightSurface + n ];
1050 ds = &bspDrawSurfaces[ num ];
1051 info = &surfaceInfos[ num ];
1053 /* bail if no lightmap to calculate */
1054 if ( info->lm != lm ) {
1059 /* map the surface onto the lightmap origin/cluster/normal buffers */
1060 switch ( ds->surfaceType )
1064 verts = yDrawVerts + ds->firstVert;
1066 /* map the triangles */
1067 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1069 for ( i = 0; i < ds->numIndexes; i += 3 )
1071 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1072 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1073 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1074 MapTriangle( lm, info, dv, mapNonAxial );
1080 /* make a mesh from the drawsurf */
1081 src.width = ds->patchWidth;
1082 src.height = ds->patchHeight;
1083 src.verts = &yDrawVerts[ ds->firstVert ];
1084 //% subdivided = SubdivideMesh( src, 8, 512 );
1085 subdivided = SubdivideMesh2( src, info->patchIterations );
1087 /* fit it to the curve and remove colinear verts on rows/columns */
1088 PutMeshOnCurve( *subdivided );
1089 mesh = RemoveLinearMeshColumnsRows( subdivided );
1090 FreeMesh( subdivided );
1093 verts = mesh->verts;
1098 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1099 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1100 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1101 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1105 /* map the mesh quads */
1108 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1110 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1112 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1115 pw[ 0 ] = x + ( y * mesh->width );
1116 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1117 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1118 pw[ 3 ] = x + 1 + ( y * mesh->width );
1119 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1124 /* get drawverts and map first triangle */
1125 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1126 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1127 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1128 MapTriangle( lm, info, dv, mapNonAxial );
1130 /* get drawverts and map second triangle */
1131 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1132 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1133 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1134 MapTriangle( lm, info, dv, mapNonAxial );
1141 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1143 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1146 pw[ 0 ] = x + ( y * mesh->width );
1147 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1148 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1149 pw[ 3 ] = x + 1 + ( y * mesh->width );
1155 /* attempt to map quad first */
1156 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1157 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1158 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1159 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1160 if ( MapQuad( lm, info, dv ) ) {
1164 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1166 /* get drawverts and map first triangle */
1167 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1168 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1169 MapTriangle( lm, info, dv, mapNonAxial );
1171 /* get drawverts and map second triangle */
1172 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1173 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1174 MapTriangle( lm, info, dv, mapNonAxial );
1190 /* -----------------------------------------------------------------
1191 average and clean up luxel normals
1192 ----------------------------------------------------------------- */
1194 /* walk the luxels */
1195 for ( y = 0; y < lm->sh; y++ )
1197 for ( x = 0; x < lm->sw; x++ )
1200 luxel = SUPER_LUXEL( 0, x, y );
1201 normal = SUPER_NORMAL( x, y );
1202 cluster = SUPER_CLUSTER( x, y );
1204 /* only look at mapped luxels */
1205 if ( *cluster < 0 ) {
1209 /* the normal data could be the sum of multiple samples */
1210 if ( luxel[ 3 ] > 1.0f ) {
1211 VectorNormalize( normal, normal );
1214 /* mark this luxel as having only one normal */
1219 /* non-planar surfaces stop here */
1220 if ( lm->plane == NULL ) {
1224 /* -----------------------------------------------------------------
1225 map occluded or unuxed luxels
1226 ----------------------------------------------------------------- */
1228 /* walk the luxels */
1229 radius = floor( superSample / 2 );
1230 radius = radius > 0 ? radius : 1.0f;
1232 for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1234 for ( y = 0; y < lm->sh; y++ )
1236 for ( x = 0; x < lm->sw; x++ )
1239 luxel = SUPER_LUXEL( 0, x, y );
1240 normal = SUPER_NORMAL( x, y );
1241 cluster = SUPER_CLUSTER( x, y );
1243 /* only look at unmapped luxels */
1244 if ( *cluster != CLUSTER_UNMAPPED ) {
1248 /* divine a normal and origin from neighboring luxels */
1249 VectorClear( fake.xyz );
1250 VectorClear( fake.normal );
1251 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1252 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1254 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1256 if ( sy < 0 || sy >= lm->sh ) {
1260 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1262 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1266 /* get neighboring luxel */
1267 luxel = SUPER_LUXEL( 0, sx, sy );
1268 origin = SUPER_ORIGIN( sx, sy );
1269 normal = SUPER_NORMAL( sx, sy );
1270 cluster = SUPER_CLUSTER( sx, sy );
1272 /* only consider luxels mapped in previous passes */
1273 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1277 /* add its distinctiveness to our own */
1278 VectorAdd( fake.xyz, origin, fake.xyz );
1279 VectorAdd( fake.normal, normal, fake.normal );
1280 samples += luxel[ 3 ];
1285 if ( samples == 0.0f ) {
1290 VectorDivide( fake.xyz, samples, fake.xyz );
1291 //% VectorDivide( fake.normal, samples, fake.normal );
1292 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1296 /* map the fake vert */
1297 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1302 /* -----------------------------------------------------------------
1303 average and clean up luxel normals
1304 ----------------------------------------------------------------- */
1306 /* walk the luxels */
1307 for ( y = 0; y < lm->sh; y++ )
1309 for ( x = 0; x < lm->sw; x++ )
1312 luxel = SUPER_LUXEL( 0, x, y );
1313 normal = SUPER_NORMAL( x, y );
1314 cluster = SUPER_CLUSTER( x, y );
1316 /* only look at mapped luxels */
1317 if ( *cluster < 0 ) {
1321 /* the normal data could be the sum of multiple samples */
1322 if ( luxel[ 3 ] > 1.0f ) {
1323 VectorNormalize( normal, normal );
1326 /* mark this luxel as having only one normal */
1334 for ( y = 0; y < lm->sh; y++ )
1336 for ( x = 0; x < lm->sw; x++ )
1341 cluster = SUPER_CLUSTER( x, y );
1342 origin = SUPER_ORIGIN( x, y );
1343 normal = SUPER_NORMAL( x, y );
1344 luxel = SUPER_LUXEL( x, y );
1346 if ( *cluster < 0 ) {
1350 /* check if within the bounding boxes of all surfaces referenced */
1351 ClearBounds( mins, maxs );
1352 for ( n = 0; n < lm->numLightSurfaces; n++ )
1355 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1356 TOL = info->sampleSize + 2;
1357 AddPointToBounds( info->mins, mins, maxs );
1358 AddPointToBounds( info->maxs, mins, maxs );
1359 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1360 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1361 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1367 if ( n < lm->numLightSurfaces ) {
1371 /* report bogus origin */
1372 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",
1373 rawLightmapNum, x, y, *cluster,
1374 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1375 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1376 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1387 sets up dirtmap (ambient occlusion)
1390 #define DIRT_CONE_ANGLE 88 /* degrees */
1391 #define DIRT_NUM_ANGLE_STEPS 16
1392 #define DIRT_NUM_ELEVATION_STEPS 3
1393 #define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1395 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1396 static int numDirtVectors = 0;
1398 void SetupDirt( void ){
1400 float angle, elevation, angleStep, elevationStep;
1404 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1406 /* calculate angular steps */
1407 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1408 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1412 for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1414 /* iterate elevation */
1415 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1417 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1418 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1419 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1424 /* emit some statistics */
1425 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1431 calculates dirt value for a given sample
1434 float DirtForSample( trace_t *trace ){
1436 float gatherDirt, outDirt, angle, elevation, ooDepth;
1437 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1444 if ( trace == NULL || trace->cluster < 0 ) {
1450 ooDepth = 1.0f / dirtDepth;
1451 VectorCopy( trace->normal, normal );
1453 /* check if the normal is aligned to the world-up */
1454 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
1455 if ( normal[ 2 ] == 1.0f ) {
1456 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1457 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1459 else if ( normal[ 2 ] == -1.0f ) {
1460 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1461 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1466 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1467 CrossProduct( normal, worldUp, myRt );
1468 VectorNormalize( myRt, myRt );
1469 CrossProduct( myRt, normal, myUp );
1470 VectorNormalize( myUp, myUp );
1473 /* 1 = random mode, 0 (well everything else) = non-random mode */
1474 if ( dirtMode == 1 ) {
1476 for ( i = 0; i < numDirtVectors; i++ )
1478 /* get random vector */
1479 angle = Random() * DEG2RAD( 360.0f );
1480 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1481 temp[ 0 ] = cos( angle ) * sin( elevation );
1482 temp[ 1 ] = sin( angle ) * sin( elevation );
1483 temp[ 2 ] = cos( elevation );
1485 /* transform into tangent space */
1486 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1487 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1488 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1491 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1492 SetupTrace( trace );
1493 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1497 if ( trace->opaque && !( trace->compileFlags & C_SKY ) ) {
1498 VectorSubtract( trace->hit, trace->origin, displacement );
1499 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1505 /* iterate through ordered vectors */
1506 for ( i = 0; i < numDirtVectors; i++ )
1508 /* transform vector into tangent space */
1509 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1510 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1511 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1514 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1515 SetupTrace( trace );
1516 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1520 if ( trace->opaque ) {
1521 VectorSubtract( trace->hit, trace->origin, displacement );
1522 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1528 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1529 SetupTrace( trace );
1530 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1534 if ( trace->opaque ) {
1535 VectorSubtract( trace->hit, trace->origin, displacement );
1536 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1540 if ( gatherDirt <= 0.0f ) {
1544 /* apply gain (does this even do much? heh) */
1545 outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1546 if ( outDirt > 1.0f ) {
1551 outDirt *= dirtScale;
1552 if ( outDirt > 1.0f ) {
1556 /* return to sender */
1557 return 1.0f - outDirt;
1564 calculates dirty fraction for each luxel
1567 void DirtyRawLightmap( int rawLightmapNum ){
1568 int i, x, y, sx, sy, *cluster;
1569 float *origin, *normal, *dirt, *dirt2, average, samples;
1571 surfaceInfo_t *info;
1576 /* bail if this number exceeds the number of raw lightmaps */
1577 if ( rawLightmapNum >= numRawLightmaps ) {
1582 lm = &rawLightmaps[ rawLightmapNum ];
1585 trace.testOcclusion = qtrue;
1586 trace.forceSunlight = qfalse;
1587 trace.recvShadows = lm->recvShadows;
1588 trace.numSurfaces = lm->numLightSurfaces;
1589 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1590 trace.inhibitRadius = 0.0f;
1591 trace.testAll = qfalse;
1593 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1594 trace.twoSided = qfalse;
1595 for ( i = 0; i < trace.numSurfaces; i++ )
1598 info = &surfaceInfos[ trace.surfaces[ i ] ];
1600 /* check twosidedness */
1601 if ( info->si->twoSided ) {
1602 trace.twoSided = qtrue;
1608 for ( i = 0; i < trace.numSurfaces; i++ )
1611 info = &surfaceInfos[ trace.surfaces[ i ] ];
1613 /* check twosidedness */
1614 if ( info->si->noDirty ) {
1621 for ( y = 0; y < lm->sh; y++ )
1623 for ( x = 0; x < lm->sw; x++ )
1626 cluster = SUPER_CLUSTER( x, y );
1627 origin = SUPER_ORIGIN( x, y );
1628 normal = SUPER_NORMAL( x, y );
1629 dirt = SUPER_DIRT( x, y );
1631 /* set default dirt */
1634 /* only look at mapped luxels */
1635 if ( *cluster < 0 ) {
1639 /* don't apply dirty on this surface */
1646 trace.cluster = *cluster;
1647 VectorCopy( origin, trace.origin );
1648 VectorCopy( normal, trace.normal );
1651 *dirt = DirtForSample( &trace );
1655 /* testing no filtering */
1659 for ( y = 0; y < lm->sh; y++ )
1661 for ( x = 0; x < lm->sw; x++ )
1664 cluster = SUPER_CLUSTER( x, y );
1665 dirt = SUPER_DIRT( x, y );
1667 /* filter dirt by adjacency to unmapped luxels */
1670 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1672 if ( sy < 0 || sy >= lm->sh ) {
1676 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1678 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1682 /* get neighboring luxel */
1683 cluster = SUPER_CLUSTER( sx, sy );
1684 dirt2 = SUPER_DIRT( sx, sy );
1685 if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1695 if ( samples <= 0.0f ) {
1701 if ( samples <= 0.0f ) {
1706 *dirt = average / samples;
1715 calculates the pvs cluster, origin, normal of a sub-luxel
1718 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1719 int i, *cluster, *cluster2;
1720 float *origin, *origin2, *normal; //% , *normal2;
1721 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1724 /* calulate x vector */
1725 if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1726 cluster = SUPER_CLUSTER( x, y );
1727 origin = SUPER_ORIGIN( x, y );
1728 //% normal = SUPER_NORMAL( x, y );
1729 cluster2 = SUPER_CLUSTER( x + 1, y );
1730 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1731 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1733 else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1734 cluster = SUPER_CLUSTER( x - 1, y );
1735 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1736 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1737 cluster2 = SUPER_CLUSTER( x, y );
1738 origin2 = SUPER_ORIGIN( x, y );
1739 //% normal2 = SUPER_NORMAL( x, y );
1742 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap S vector\n" );
1745 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1746 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1748 /* calulate y vector */
1749 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1750 cluster = SUPER_CLUSTER( x, y );
1751 origin = SUPER_ORIGIN( x, y );
1752 //% normal = SUPER_NORMAL( x, y );
1753 cluster2 = SUPER_CLUSTER( x, y + 1 );
1754 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1755 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1757 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1758 cluster = SUPER_CLUSTER( x, y - 1 );
1759 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1760 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1761 cluster2 = SUPER_CLUSTER( x, y );
1762 origin2 = SUPER_ORIGIN( x, y );
1763 //% normal2 = SUPER_NORMAL( x, y );
1766 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap T vector\n" );
1769 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1771 /* calculate new origin */
1772 for ( i = 0; i < 3; i++ )
1773 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1776 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1777 if ( *sampleCluster < 0 ) {
1781 /* calculate new normal */
1782 normal = SUPER_NORMAL( x, y );
1783 VectorCopy( normal, sampleNormal );
1791 SubsampleRawLuxel_r()
1792 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1795 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1796 int b, samples, mapped, lighted;
1799 vec3_t deluxel[ 3 ];
1800 vec3_t origin[ 4 ], normal[ 4 ];
1801 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1802 vec3_t color, direction = { 0, 0, 0 }, total;
1806 if ( lightLuxel[ 3 ] >= lightSamples ) {
1811 VectorClear( total );
1815 /* make 2x2 subsample stamp */
1816 for ( b = 0; b < 4; b++ )
1819 VectorCopy( sampleOrigin, origin[ b ] );
1821 /* calculate position */
1822 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1828 /* increment sample count */
1829 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1832 trace->cluster = *cluster;
1833 VectorCopy( origin[ b ], trace->origin );
1834 VectorCopy( normal[ b ], trace->normal );
1838 LightContributionToSample( trace );
1839 if ( trace->forceSubsampling > 1.0f ) {
1840 /* alphashadow: we subsample as deep as we can */
1846 /* add to totals (fixme: make contrast function) */
1847 VectorCopy( trace->color, luxel[ b ] );
1848 if ( lightDeluxel ) {
1849 VectorCopy( trace->directionContribution, deluxel[ b ] );
1851 VectorAdd( total, trace->color, total );
1852 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1857 /* subsample further? */
1858 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1859 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1860 lighted != 0 && lighted != mapped ) {
1861 for ( b = 0; b < 4; b++ )
1863 if ( cluster[ b ] < 0 ) {
1866 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1871 //% VectorClear( color );
1873 VectorCopy( lightLuxel, color );
1874 if ( lightDeluxel ) {
1875 VectorCopy( lightDeluxel, direction );
1878 for ( b = 0; b < 4; b++ )
1880 if ( cluster[ b ] < 0 ) {
1883 VectorAdd( color, luxel[ b ], color );
1884 if ( lightDeluxel ) {
1885 VectorAdd( direction, deluxel[ b ], direction );
1891 if ( samples > 0 ) {
1893 color[ 0 ] /= samples;
1894 color[ 1 ] /= samples;
1895 color[ 2 ] /= samples;
1898 VectorCopy( color, lightLuxel );
1899 lightLuxel[ 3 ] += 1.0f;
1901 if ( lightDeluxel ) {
1902 direction[ 0 ] /= samples;
1903 direction[ 1 ] /= samples;
1904 direction[ 2 ] /= samples;
1905 VectorCopy( direction, lightDeluxel );
1910 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1911 static void GaussLikeRandom( float sigma, float *x, float *y ){
1913 r = Random() * 2 * Q_PI;
1914 *x = sigma * 2.73861278752581783822 * cos( r );
1915 *y = sigma * 2.73861278752581783822 * sin( r );
1922 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1925 vec3_t origin, normal;
1926 vec3_t total, totaldirection;
1929 VectorClear( total );
1930 VectorClear( totaldirection );
1932 for ( b = 0; b < lightSamples; ++b )
1935 VectorCopy( sampleOrigin, origin );
1936 GaussLikeRandom( bias, &dx, &dy );
1938 /* calculate position */
1939 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1945 trace->cluster = cluster;
1946 VectorCopy( origin, trace->origin );
1947 VectorCopy( normal, trace->normal );
1949 LightContributionToSample( trace );
1950 VectorAdd( total, trace->color, total );
1951 if ( lightDeluxel ) {
1952 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1959 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1960 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1961 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1963 if ( lightDeluxel ) {
1964 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1965 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1966 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1974 IlluminateRawLightmap()
1975 illuminates the luxels
1978 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
1979 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
1980 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
1982 void IlluminateRawLightmap( int rawLightmapNum ){
1983 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1984 int *cluster, *cluster2, mapped, lighted, totalLighted;
1985 size_t llSize, ldSize;
1987 surfaceInfo_t *info;
1988 qboolean filterColor, filterDir;
1990 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1991 unsigned char *flag;
1992 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
1993 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
1994 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1996 float stackLightLuxels[ STACK_LL_SIZE ];
1999 /* bail if this number exceeds the number of raw lightmaps */
2000 if ( rawLightmapNum >= numRawLightmaps ) {
2005 lm = &rawLightmaps[ rawLightmapNum ];
2008 trace.testOcclusion = !noTrace;
2009 trace.forceSunlight = qfalse;
2010 trace.recvShadows = lm->recvShadows;
2011 trace.numSurfaces = lm->numLightSurfaces;
2012 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2013 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2015 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2016 trace.twoSided = qfalse;
2017 for ( i = 0; i < trace.numSurfaces; i++ )
2020 info = &surfaceInfos[ trace.surfaces[ i ] ];
2022 /* check twosidedness */
2023 if ( info->si->twoSided ) {
2024 trace.twoSided = qtrue;
2029 /* create a culled light list for this raw lightmap */
2030 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2032 /* -----------------------------------------------------------------
2034 ----------------------------------------------------------------- */
2037 numLuxelsIlluminated += ( lm->sw * lm->sh );
2039 /* test debugging state */
2040 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2041 /* debug fill the luxels */
2042 for ( y = 0; y < lm->sh; y++ )
2044 for ( x = 0; x < lm->sw; x++ )
2047 cluster = SUPER_CLUSTER( x, y );
2049 /* only fill mapped luxels */
2050 if ( *cluster < 0 ) {
2054 /* get particulars */
2055 luxel = SUPER_LUXEL( 0, x, y );
2056 origin = SUPER_ORIGIN( x, y );
2057 normal = SUPER_NORMAL( x, y );
2059 /* color the luxel with raw lightmap num? */
2060 if ( debugSurfaces ) {
2061 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2064 /* color the luxel with lightmap axis? */
2065 else if ( debugAxis ) {
2066 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2067 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2068 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2071 /* color the luxel with luxel cluster? */
2072 else if ( debugCluster ) {
2073 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2076 /* color the luxel with luxel origin? */
2077 else if ( debugOrigin ) {
2078 VectorSubtract( lm->maxs, lm->mins, temp );
2079 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2080 VectorSubtract( origin, lm->mins, temp2 );
2081 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2082 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2083 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2086 /* color the luxel with the normal */
2087 else if ( normalmap ) {
2088 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2089 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2090 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2093 /* otherwise clear it */
2095 VectorClear( luxel );
2105 /* allocate temporary per-light luxel storage */
2106 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2107 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2108 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2109 lightLuxels = stackLightLuxels;
2112 lightLuxels = safe_malloc( llSize );
2115 lightDeluxels = safe_malloc( ldSize );
2118 lightDeluxels = NULL;
2122 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2124 /* set ambient color */
2125 for ( y = 0; y < lm->sh; y++ )
2127 for ( x = 0; x < lm->sw; x++ )
2130 cluster = SUPER_CLUSTER( x, y );
2131 luxel = SUPER_LUXEL( 0, x, y );
2132 normal = SUPER_NORMAL( x, y );
2133 deluxel = SUPER_DELUXEL( x, y );
2135 /* blacken unmapped clusters */
2136 if ( *cluster < 0 ) {
2137 VectorClear( luxel );
2143 VectorCopy( ambientColor, luxel );
2145 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2147 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2148 if ( brightness < 0.00390625f ) {
2149 brightness = 0.00390625f;
2152 VectorScale( normal, brightness, deluxel );
2159 /* clear styled lightmaps */
2160 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2161 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2163 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2164 memset( lm->superLuxels[ lightmapNum ], 0, size );
2168 /* debugging code */
2169 //% if( trace.numLights <= 0 )
2170 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2172 /* walk light list */
2173 for ( i = 0; i < trace.numLights; i++ )
2176 trace.light = trace.lights[ i ];
2179 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2181 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2182 lm->styles[ lightmapNum ] == LS_NONE ) {
2187 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2188 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2189 Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2194 memset( lightLuxels, 0, llSize );
2196 memset( lightDeluxels, 0, ldSize );
2200 /* determine filter radius */
2201 filterRadius = lm->filterRadius > trace.light->filterRadius
2203 : trace.light->filterRadius;
2204 if ( filterRadius < 0.0f ) {
2205 filterRadius = 0.0f;
2208 /* set luxel filter radius */
2209 luxelFilterRadius = lm->sampleSize != 0 ? superSample * filterRadius / lm->sampleSize : 0;
2210 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2211 luxelFilterRadius = 1;
2214 /* allocate sampling flags storage */
2215 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2216 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2217 if ( lm->superFlags == NULL ) {
2218 lm->superFlags = safe_malloc( size );
2220 memset( (void *) lm->superFlags, 0, size );
2223 /* initial pass, one sample per luxel */
2224 for ( y = 0; y < lm->sh; y++ )
2226 for ( x = 0; x < lm->sw; x++ )
2229 cluster = SUPER_CLUSTER( x, y );
2230 if ( *cluster < 0 ) {
2234 /* get particulars */
2235 lightLuxel = LIGHT_LUXEL( x, y );
2236 lightDeluxel = LIGHT_DELUXEL( x, y );
2237 origin = SUPER_ORIGIN( x, y );
2238 normal = SUPER_NORMAL( x, y );
2239 flag = SUPER_FLAG( x, y );
2241 /* set contribution count */
2242 lightLuxel[ 3 ] = 1.0f;
2245 trace.cluster = *cluster;
2246 VectorCopy( origin, trace.origin );
2247 VectorCopy( normal, trace.normal );
2249 /* get light for this sample */
2250 LightContributionToSample( &trace );
2251 VectorCopy( trace.color, lightLuxel );
2253 /* add the contribution to the deluxemap */
2255 VectorCopy( trace.directionContribution, lightDeluxel );
2258 /* check for evilness */
2259 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2261 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2264 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2270 /* don't even bother with everything else if nothing was lit */
2271 if ( totalLighted == 0 ) {
2275 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2276 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2277 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2279 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2281 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2286 VectorClear( total );
2288 /* test 2x2 stamp */
2289 for ( t = 0; t < 4; t++ )
2291 /* set sample coords */
2292 sx = x + tests[ t ][ 0 ];
2293 sy = y + tests[ t ][ 1 ];
2296 cluster = SUPER_CLUSTER( sx, sy );
2297 if ( *cluster < 0 ) {
2303 flag = SUPER_FLAG( sx, sy );
2304 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2305 /* force a lighted/mapped discrepancy so we subsample */
2310 lightLuxel = LIGHT_LUXEL( sx, sy );
2311 VectorAdd( total, lightLuxel, total );
2312 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2317 /* if total color is under a certain amount, then don't bother subsampling */
2318 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2322 /* if all 4 pixels are either in shadow or light, then don't subsample */
2323 if ( lighted != 0 && lighted != mapped ) {
2324 for ( t = 0; t < 4; t++ )
2326 /* set sample coords */
2327 sx = x + tests[ t ][ 0 ];
2328 sy = y + tests[ t ][ 1 ];
2331 cluster = SUPER_CLUSTER( sx, sy );
2332 if ( *cluster < 0 ) {
2335 flag = SUPER_FLAG( sx, sy );
2336 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2339 lightLuxel = LIGHT_LUXEL( sx, sy );
2340 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2341 origin = SUPER_ORIGIN( sx, sy );
2343 /* only subsample shadowed luxels */
2344 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2348 if ( lightRandomSamples ) {
2349 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2352 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2355 *flag |= FLAG_ALREADY_SUBSAMPLED;
2357 /* debug code to colorize subsampled areas to yellow */
2358 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2359 //% VectorSet( luxel, 255, 204, 0 );
2366 /* tertiary pass, apply dirt map (ambient occlusion) */
2369 for ( y = 0; y < lm->sh; y++ )
2371 for ( x = 0; x < lm->sw; x++ )
2374 cluster = SUPER_CLUSTER( x, y );
2375 if ( *cluster < 0 ) {
2379 /* get particulars */
2380 lightLuxel = LIGHT_LUXEL( x, y );
2381 dirt = SUPER_DIRT( x, y );
2383 /* scale light value */
2384 VectorScale( lightLuxel, *dirt, lightLuxel );
2389 /* allocate sampling lightmap storage */
2390 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2391 /* allocate sampling lightmap storage */
2392 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2393 lm->superLuxels[ lightmapNum ] = safe_malloc0( size );
2397 if ( lightmapNum > 0 ) {
2398 lm->styles[ lightmapNum ] = trace.light->style;
2399 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2402 /* copy to permanent luxels */
2403 for ( y = 0; y < lm->sh; y++ )
2405 for ( x = 0; x < lm->sw; x++ )
2407 /* get cluster and origin */
2408 cluster = SUPER_CLUSTER( x, y );
2409 if ( *cluster < 0 ) {
2412 origin = SUPER_ORIGIN( x, y );
2415 if ( luxelFilterRadius ) {
2417 VectorClear( averageColor );
2418 VectorClear( averageDir );
2421 /* cheaper distance-based filtering */
2422 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2424 if ( sy < 0 || sy >= lm->sh ) {
2428 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2430 if ( sx < 0 || sx >= lm->sw ) {
2434 /* get particulars */
2435 cluster = SUPER_CLUSTER( sx, sy );
2436 if ( *cluster < 0 ) {
2439 lightLuxel = LIGHT_LUXEL( sx, sy );
2440 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2443 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2444 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2446 /* scale luxel by filter weight */
2447 VectorScale( lightLuxel, weight, color );
2448 VectorAdd( averageColor, color, averageColor );
2450 VectorScale( lightDeluxel, weight, direction );
2451 VectorAdd( averageDir, direction, averageDir );
2458 if ( samples <= 0.0f ) {
2462 /* scale into luxel */
2463 luxel = SUPER_LUXEL( lightmapNum, x, y );
2466 /* handle negative light */
2467 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2468 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2469 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2470 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2473 /* handle normal light */
2476 luxel[ 0 ] += averageColor[ 0 ] / samples;
2477 luxel[ 1 ] += averageColor[ 1 ] / samples;
2478 luxel[ 2 ] += averageColor[ 2 ] / samples;
2482 /* scale into luxel */
2483 deluxel = SUPER_DELUXEL( x, y );
2484 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2485 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2486 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2493 /* get particulars */
2494 lightLuxel = LIGHT_LUXEL( x, y );
2495 lightDeluxel = LIGHT_DELUXEL( x, y );
2496 luxel = SUPER_LUXEL( lightmapNum, x, y );
2497 deluxel = SUPER_DELUXEL( x, y );
2499 /* handle negative light */
2500 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2501 VectorScale( averageColor, -1.0f, averageColor );
2507 /* handle negative light */
2508 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2509 VectorSubtract( luxel, lightLuxel, luxel );
2512 /* handle normal light */
2514 VectorAdd( luxel, lightLuxel, luxel );
2518 VectorAdd( deluxel, lightDeluxel, deluxel );
2525 /* free temporary luxels */
2526 if ( lightLuxels != stackLightLuxels ) {
2527 free( lightLuxels );
2531 free( lightDeluxels );
2535 /* free light list */
2536 FreeTraceLights( &trace );
2538 /* floodlight pass */
2539 if ( floodlighty ) {
2540 FloodlightIlluminateLightmap( lm );
2543 if ( debugnormals ) {
2544 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2547 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2551 for ( y = 0; y < lm->sh; y++ )
2553 for ( x = 0; x < lm->sw; x++ )
2556 cluster = SUPER_CLUSTER( x, y );
2557 //% if( *cluster < 0 )
2560 /* get particulars */
2561 luxel = SUPER_LUXEL( lightmapNum, x, y );
2562 normal = SUPER_NORMAL( x, y );
2564 luxel[0] = ( normal[0] * 127 ) + 127;
2565 luxel[1] = ( normal[1] * 127 ) + 127;
2566 luxel[2] = ( normal[2] * 127 ) + 127;
2572 /* -----------------------------------------------------------------
2574 ----------------------------------------------------------------- */
2577 /* walk lightmaps */
2578 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2581 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2585 /* apply dirt to each luxel */
2586 for ( y = 0; y < lm->sh; y++ )
2588 for ( x = 0; x < lm->sw; x++ )
2591 cluster = SUPER_CLUSTER( x, y );
2593 /* get particulars */
2594 luxel = SUPER_LUXEL( lightmapNum, x, y );
2595 dirt = SUPER_DIRT( x, y );
2598 VectorScale( luxel, *dirt, luxel );
2602 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2609 /* -----------------------------------------------------------------
2611 ----------------------------------------------------------------- */
2613 /* walk lightmaps */
2614 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2617 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2621 /* average occluded luxels from neighbors */
2622 for ( y = 0; y < lm->sh; y++ )
2624 for ( x = 0; x < lm->sw; x++ )
2626 /* get particulars */
2627 cluster = SUPER_CLUSTER( x, y );
2628 luxel = SUPER_LUXEL( lightmapNum, x, y );
2629 deluxel = SUPER_DELUXEL( x, y );
2630 normal = SUPER_NORMAL( x, y );
2632 /* determine if filtering is necessary */
2633 filterColor = qfalse;
2635 if ( *cluster < 0 ||
2636 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2637 filterColor = qtrue;
2640 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2644 if ( !filterColor && !filterDir ) {
2648 /* choose seed amount */
2649 VectorClear( averageColor );
2650 VectorClear( averageDir );
2653 /* walk 3x3 matrix */
2654 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2656 if ( sy < 0 || sy >= lm->sh ) {
2660 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2662 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2666 /* get neighbor's particulars */
2667 cluster2 = SUPER_CLUSTER( sx, sy );
2668 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2669 deluxel2 = SUPER_DELUXEL( sx, sy );
2671 /* ignore unmapped/unlit luxels */
2672 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2673 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2677 /* add its distinctiveness to our own */
2678 VectorAdd( averageColor, luxel2, averageColor );
2679 samples += luxel2[ 3 ];
2681 VectorAdd( averageDir, deluxel2, averageDir );
2687 if ( samples <= 0.0f ) {
2691 /* dark lightmap seams */
2693 if ( lightmapNum == 0 ) {
2694 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2700 if ( filterColor ) {
2701 VectorDivide( averageColor, samples, luxel );
2705 VectorDivide( averageDir, samples, deluxel );
2708 /* set cluster to -3 */
2709 if ( *cluster < 0 ) {
2710 *cluster = CLUSTER_FLOODED;
2720 IlluminateVertexes()
2721 light the surface vertexes
2724 #define VERTEX_NUDGE 4.0f
2726 void IlluminateVertexes( int num ){
2727 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2728 int lightmapNum, numAvg;
2729 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2730 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2731 bspDrawSurface_t *ds;
2732 surfaceInfo_t *info;
2734 bspDrawVert_t *verts;
2736 float floodLightAmount;
2740 /* get surface, info, and raw lightmap */
2741 ds = &bspDrawSurfaces[ num ];
2742 info = &surfaceInfos[ num ];
2745 /* -----------------------------------------------------------------
2746 illuminate the vertexes
2747 ----------------------------------------------------------------- */
2749 /* calculate vertex lighting for surfaces without lightmaps */
2750 if ( lm == NULL || cpmaHack ) {
2752 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2753 trace.forceSunlight = info->si->forceSunlight;
2754 trace.recvShadows = info->recvShadows;
2755 trace.numSurfaces = 1;
2756 trace.surfaces = #
2757 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2759 /* twosided lighting */
2760 trace.twoSided = info->si->twoSided;
2762 /* make light list for this surface */
2763 CreateTraceLightsForSurface( num, &trace );
2766 verts = yDrawVerts + ds->firstVert;
2768 memset( avgColors, 0, sizeof( avgColors ) );
2770 /* walk the surface verts */
2771 for ( i = 0; i < ds->numVerts; i++ )
2773 /* get vertex luxel */
2774 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2776 /* color the luxel with raw lightmap num? */
2777 if ( debugSurfaces ) {
2778 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2781 /* color the luxel with luxel origin? */
2782 else if ( debugOrigin ) {
2783 VectorSubtract( info->maxs, info->mins, temp );
2784 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2785 VectorSubtract( origin, lm->mins, temp2 );
2786 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2787 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2788 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2791 /* color the luxel with the normal */
2792 else if ( normalmap ) {
2793 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2794 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2795 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2798 /* illuminate the vertex */
2801 /* clear vertex luxel */
2802 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2804 /* try at initial origin */
2805 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2806 if ( trace.cluster >= 0 ) {
2808 VectorCopy( verts[ i ].xyz, trace.origin );
2809 VectorCopy( verts[ i ].normal, trace.normal );
2812 if ( dirty && !bouncing ) {
2813 dirt = DirtForSample( &trace );
2819 /* jal: floodlight */
2820 floodLightAmount = 0.0f;
2821 VectorClear( floodColor );
2822 if ( floodlighty && !bouncing ) {
2823 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2824 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2828 LightingAtSample( &trace, ds->vertexStyles, colors );
2831 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2834 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2836 /* jal: floodlight */
2837 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2840 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2841 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2842 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2846 /* is this sample bright enough? */
2847 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2848 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2849 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2850 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2851 /* nudge the sample point around a bit */
2852 for ( x = 0; x < 5; x++ )
2854 /* two's complement 0, 1, -1, 2, -2, etc */
2855 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2857 for ( y = 0; y < 5; y++ )
2859 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2861 for ( z = 0; z < 5; z++ )
2863 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2866 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2867 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2868 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2870 /* try at nudged origin */
2871 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2872 if ( trace.cluster < 0 ) {
2877 if ( dirty && !bouncing ) {
2878 dirt = DirtForSample( &trace );
2884 /* jal: floodlight */
2885 floodLightAmount = 0.0f;
2886 VectorClear( floodColor );
2887 if ( floodlighty && !bouncing ) {
2888 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2889 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2893 LightingAtSample( &trace, ds->vertexStyles, colors );
2896 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2899 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2901 /* jal: floodlight */
2902 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2905 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2906 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2909 /* bright enough? */
2910 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2911 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2912 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2913 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2921 /* add to average? */
2922 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2923 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2924 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2925 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2927 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2929 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2930 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2935 /* another happy customer */
2936 numVertsIlluminated++;
2939 /* set average color */
2941 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2942 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2946 VectorCopy( ambientColor, avgColors[ 0 ] );
2949 /* clean up and store vertex color */
2950 for ( i = 0; i < ds->numVerts; i++ )
2952 /* get vertex luxel */
2953 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2955 /* store average in occluded vertexes */
2956 if ( radVertLuxel[ 0 ] < 0.0f ) {
2957 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2959 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2960 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2963 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2968 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2971 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2972 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2975 if ( bouncing || bounce == 0 || !bounceOnly ) {
2976 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2978 if ( !info->si->noVertexLight ) {
2979 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2984 /* free light list */
2985 FreeTraceLights( &trace );
2987 /* return to sender */
2991 /* -----------------------------------------------------------------
2992 reconstitute vertex lighting from the luxels
2993 ----------------------------------------------------------------- */
2995 /* set styles from lightmap */
2996 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2997 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2999 /* get max search radius */
3001 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3003 /* walk the surface verts */
3004 verts = yDrawVerts + ds->firstVert;
3005 for ( i = 0; i < ds->numVerts; i++ )
3007 /* do each lightmap */
3008 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3011 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3015 /* get luxel coords */
3016 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3017 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3021 else if ( x >= lm->sw ) {
3027 else if ( y >= lm->sh ) {
3031 /* get vertex luxels */
3032 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3033 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3035 /* color the luxel with the normal? */
3037 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3038 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3039 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3042 /* color the luxel with surface num? */
3043 else if ( debugSurfaces ) {
3044 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3047 /* divine color from the superluxels */
3050 /* increasing radius */
3051 VectorClear( radVertLuxel );
3053 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3055 /* sample within radius */
3056 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3058 if ( sy < 0 || sy >= lm->sh ) {
3062 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3064 if ( sx < 0 || sx >= lm->sw ) {
3068 /* get luxel particulars */
3069 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3070 cluster = SUPER_CLUSTER( sx, sy );
3071 if ( *cluster < 0 ) {
3075 /* testing: must be brigher than ambient color */
3076 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3079 /* add its distinctiveness to our own */
3080 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3081 samples += luxel[ 3 ];
3087 if ( samples > 0.0f ) {
3088 VectorDivide( radVertLuxel, samples, radVertLuxel );
3091 VectorCopy( ambientColor, radVertLuxel );
3095 /* store into floating point storage */
3096 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3097 numVertsIlluminated++;
3099 /* store into bytes (for vertex approximation) */
3100 if ( !info->si->noVertexLight ) {
3101 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3109 /* -------------------------------------------------------------------------------
3111 light optimization (-fast)
3113 creates a list of lights that will affect a surface and stores it in tw
3114 this is to optimize surface lighting by culling out as many of the
3115 lights in the world as possible from further calculation
3117 ------------------------------------------------------------------------------- */
3121 determines opaque brushes in the world and find sky shaders for sunlight calculations
3124 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3126 unsigned int compileFlags, allCompileFlags;
3129 bspBrushSide_t *side;
3130 bspShader_t *shader;
3135 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3138 if ( opaqueBrushes == NULL ) {
3139 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3143 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3144 numOpaqueBrushes = 0;
3146 /* walk the list of worldspawn brushes */
3147 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3150 b = bspModels[ 0 ].firstBSPBrush + i;
3151 brush = &bspBrushes[ b ];
3153 /* check all sides */
3156 allCompileFlags = ~( 0u );
3157 for ( j = 0; j < brush->numSides && inside; j++ )
3159 /* do bsp shader calculations */
3160 side = &bspBrushSides[ brush->firstSide + j ];
3161 shader = &bspShaders[ side->shaderNum ];
3163 /* get shader info */
3164 si = ShaderInfoForShaderNull( shader->shader );
3169 /* or together compile flags */
3170 compileFlags |= si->compileFlags;
3171 allCompileFlags &= si->compileFlags;
3174 /* determine if this brush is opaque to light */
3175 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3176 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3182 /* emit some statistics */
3183 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3185 void SetupBrushes( void ){
3186 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3193 determines if two clusters are visible to each other using the PVS
3196 qboolean ClusterVisible( int a, int b ){
3202 if ( a < 0 || b < 0 ) {
3212 if ( numBSPVisBytes <= 8 ) {
3217 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3218 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3219 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3222 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3232 borrowed from vlight.c
3235 int PointInLeafNum_r( vec3_t point, int nodenum ){
3242 while ( nodenum >= 0 )
3244 node = &bspNodes[ nodenum ];
3245 plane = &bspPlanes[ node->planeNum ];
3246 dist = DotProduct( point, plane->normal ) - plane->dist;
3248 nodenum = node->children[ 0 ];
3250 else if ( dist < -0.1 ) {
3251 nodenum = node->children[ 1 ];
3255 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3256 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3259 nodenum = node->children[ 1 ];
3263 leafnum = -nodenum - 1;
3271 borrowed from vlight.c
3274 int PointInLeafNum( vec3_t point ){
3275 return PointInLeafNum_r( point, 0 );
3281 ClusterVisibleToPoint() - ydnar
3282 returns qtrue if point can "see" cluster
3285 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3289 /* get leafNum for point */
3290 pointCluster = ClusterForPoint( point );
3291 if ( pointCluster < 0 ) {
3296 return ClusterVisible( pointCluster, cluster );
3302 ClusterForPoint() - ydnar
3303 returns the pvs cluster for point
3306 int ClusterForPoint( vec3_t point ){
3310 /* get leafNum for point */
3311 leafNum = PointInLeafNum( point );
3312 if ( leafNum < 0 ) {
3316 /* return the cluster */
3317 return bspLeafs[ leafNum ].cluster;
3323 ClusterForPointExt() - ydnar
3324 also takes brushes into account for occlusion testing
3327 int ClusterForPointExt( vec3_t point, float epsilon ){
3328 int i, j, b, leafNum, cluster;
3331 int *brushes, numBSPBrushes;
3337 /* get leaf for point */
3338 leafNum = PointInLeafNum( point );
3339 if ( leafNum < 0 ) {
3342 leaf = &bspLeafs[ leafNum ];
3344 /* get the cluster */
3345 cluster = leaf->cluster;
3346 if ( cluster < 0 ) {
3350 /* transparent leaf, so check point against all brushes in the leaf */
3351 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3352 numBSPBrushes = leaf->numBSPLeafBrushes;
3353 for ( i = 0; i < numBSPBrushes; i++ )
3357 if ( b > maxOpaqueBrush ) {
3360 brush = &bspBrushes[ b ];
3361 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3365 /* check point against all planes */
3367 for ( j = 0; j < brush->numSides && inside; j++ )
3369 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3370 dot = DotProduct( point, plane->normal );
3372 if ( dot > epsilon ) {
3377 /* if inside, return bogus cluster */
3383 /* if the point made it this far, it's not inside any opaque brushes */
3390 ClusterForPointExtFilter() - ydnar
3391 adds cluster checking against a list of known valid clusters
3394 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3398 /* get cluster for point */
3399 cluster = ClusterForPointExt( point, epsilon );
3401 /* check if filtering is necessary */
3402 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3407 for ( i = 0; i < numClusters; i++ )
3409 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3421 ShaderForPointInLeaf() - ydnar
3422 checks a point against all brushes in a leaf, returning the shader of the brush
3423 also sets the cumulative surface and content flags for the brush hit
3426 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3430 int *brushes, numBSPBrushes;
3433 bspBrushSide_t *side;
3435 bspShader_t *shader;
3436 int allSurfaceFlags, allContentFlags;
3439 /* clear things out first */
3444 if ( leafNum < 0 ) {
3447 leaf = &bspLeafs[ leafNum ];
3449 /* transparent leaf, so check point against all brushes in the leaf */
3450 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3451 numBSPBrushes = leaf->numBSPLeafBrushes;
3452 for ( i = 0; i < numBSPBrushes; i++ )
3455 brush = &bspBrushes[ brushes[ i ] ];
3457 /* check point against all planes */
3459 allSurfaceFlags = 0;
3460 allContentFlags = 0;
3461 for ( j = 0; j < brush->numSides && inside; j++ )
3463 side = &bspBrushSides[ brush->firstSide + j ];
3464 plane = &bspPlanes[ side->planeNum ];
3465 dot = DotProduct( point, plane->normal );
3467 if ( dot > epsilon ) {
3472 shader = &bspShaders[ side->shaderNum ];
3473 allSurfaceFlags |= shader->surfaceFlags;
3474 allContentFlags |= shader->contentFlags;
3478 /* handle if inside */
3480 /* if there are desired flags, check for same and continue if they aren't matched */
3481 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3484 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3488 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3489 *surfaceFlags = allSurfaceFlags;
3490 *contentFlags = allContentFlags;
3491 return brush->shaderNum;
3495 /* if the point made it this far, it's not inside any brushes */
3503 chops a bounding box by the plane defined by origin and normal
3504 returns qfalse if the bounds is entirely clipped away
3506 this is not exactly the fastest way to do this...
3509 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3510 /* FIXME: rewrite this so it doesn't use bloody brushes */
3518 calculates each light's effective envelope,
3519 taking into account brightness, type, and pvs.
3522 #define LIGHT_EPSILON 0.125f
3523 #define LIGHT_NUDGE 2.0f
3525 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3526 int i, x, y, z, x1, y1, z1;
3527 light_t *light, *light2, **owner;
3529 vec3_t origin, dir, mins, maxs;
3530 float radius, intensity;
3531 light_t *buckets[ 256 ];
3534 /* early out for weird cases where there are no lights */
3535 if ( lights == NULL ) {
3540 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3544 numCulledLights = 0;
3546 while ( *owner != NULL )
3551 /* handle negative lights */
3552 if ( light->photons < 0.0f || light->add < 0.0f ) {
3553 light->photons *= -1.0f;
3554 light->add *= -1.0f;
3555 light->flags |= LIGHT_NEGATIVE;
3559 if ( light->type == EMIT_SUN ) {
3562 light->envelope = MAX_WORLD_COORD * 8.0f;
3563 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3564 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3567 /* everything else */
3570 /* get pvs cluster for light */
3571 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3573 /* invalid cluster? */
3574 if ( light->cluster < 0 ) {
3575 /* nudge the sample point around a bit */
3576 for ( x = 0; x < 4; x++ )
3578 /* two's complement 0, 1, -1, 2, -2, etc */
3579 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3581 for ( y = 0; y < 4; y++ )
3583 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3585 for ( z = 0; z < 4; z++ )
3587 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3590 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3591 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3592 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3594 /* try at nudged origin */
3595 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3596 if ( light->cluster < 0 ) {
3601 VectorCopy( origin, light->origin );
3607 /* only calculate for lights in pvs and outside of opaque brushes */
3608 if ( light->cluster >= 0 ) {
3609 /* set light fast flag */
3611 light->flags |= LIGHT_FAST_TEMP;
3614 light->flags &= ~LIGHT_FAST_TEMP;
3616 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3617 light->flags |= LIGHT_FAST_TEMP;
3619 if ( light->si && light->si->noFast ) {
3620 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3623 /* clear light envelope */
3624 light->envelope = 0;
3626 /* handle area lights */
3627 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3628 light->envelope = MAX_WORLD_COORD * 8.0f;
3630 /* check for fast mode */
3631 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3632 /* ugly hack to calculate extent for area lights, but only done once */
3633 VectorScale( light->normal, -1.0f, dir );
3634 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3638 VectorMA( light->origin, radius, light->normal, origin );
3639 factor = PointToPolygonFormFactor( origin, dir, light->w );
3640 if ( factor < 0.0f ) {
3643 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3644 light->envelope = radius;
3650 intensity = light->photons; /* hopefully not used */
3655 intensity = light->photons;
3659 if ( light->envelope <= 0.0f ) {
3660 /* solve distance for non-distance lights */
3661 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3662 light->envelope = MAX_WORLD_COORD * 8.0f;
3665 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3666 /* solve distance for linear lights */
3667 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3668 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3672 add = angle * light->photons * linearScale - (dist * light->fade);
3673 T = (light->photons * linearScale) - (dist * light->fade);
3674 T + (dist * light->fade) = (light->photons * linearScale);
3675 dist * light->fade = (light->photons * linearScale) - T;
3676 dist = ((light->photons * linearScale) - T) / light->fade;
3679 /* solve for inverse square falloff */
3681 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3685 add = light->photons / (dist * dist);
3686 T = light->photons / (dist * dist);
3687 T * (dist * dist) = light->photons;
3688 dist = sqrt( light->photons / T );
3693 /* solve distance for linear lights */
3694 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3695 light->envelope = ( intensity * linearScale ) / light->fade;
3698 /* can't cull these */
3700 light->envelope = MAX_WORLD_COORD * 8.0f;
3705 /* chop radius against pvs */
3708 ClearBounds( mins, maxs );
3710 /* check all leaves */
3711 for ( i = 0; i < numBSPLeafs; i++ )
3714 leaf = &bspLeafs[ i ];
3717 if ( leaf->cluster < 0 ) {
3720 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3724 /* add this leafs bbox to the bounds */
3725 VectorCopy( leaf->mins, origin );
3726 AddPointToBounds( origin, mins, maxs );
3727 VectorCopy( leaf->maxs, origin );
3728 AddPointToBounds( origin, mins, maxs );
3731 /* test to see if bounds encompass light */
3732 for ( i = 0; i < 3; i++ )
3734 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3735 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3736 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3737 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3738 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3739 AddPointToBounds( light->origin, mins, maxs );
3743 /* chop the bounds by a plane for area lights and spotlights */
3744 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3745 ChopBounds( mins, maxs, light->origin, light->normal );
3749 VectorCopy( mins, light->mins );
3750 VectorCopy( maxs, light->maxs );
3752 /* reflect bounds around light origin */
3753 //% VectorMA( light->origin, -1.0f, origin, origin );
3754 VectorScale( light->origin, 2, origin );
3755 VectorSubtract( origin, maxs, origin );
3756 AddPointToBounds( origin, mins, maxs );
3757 //% VectorMA( light->origin, -1.0f, mins, origin );
3758 VectorScale( light->origin, 2, origin );
3759 VectorSubtract( origin, mins, origin );
3760 AddPointToBounds( origin, mins, maxs );
3762 /* calculate spherical bounds */
3763 VectorSubtract( maxs, light->origin, dir );
3764 radius = (float) VectorLength( dir );
3766 /* if this radius is smaller than the envelope, then set the envelope to it */
3767 if ( radius < light->envelope ) {
3768 light->envelope = radius;
3769 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3772 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3775 /* add grid/surface only check */
3777 if ( !( light->flags & LIGHT_GRID ) ) {
3778 light->envelope = 0.0f;
3783 if ( !( light->flags & LIGHT_SURFACES ) ) {
3784 light->envelope = 0.0f;
3790 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3792 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3794 /* delete the light */
3796 *owner = light->next;
3797 if ( light->w != NULL ) {
3805 /* square envelope */
3806 light->envelope2 = ( light->envelope * light->envelope );
3808 /* increment light count */
3811 /* set next light */
3812 owner = &( ( **owner ).next );
3815 /* bucket sort lights by style */
3816 memset( buckets, 0, sizeof( buckets ) );
3818 for ( light = lights; light != NULL; light = light2 )
3820 /* get next light */
3821 light2 = light->next;
3823 /* filter into correct bucket */
3824 light->next = buckets[ light->style ];
3825 buckets[ light->style ] = light;
3827 /* if any styled light is present, automatically set nocollapse */
3828 if ( light->style != LS_NORMAL ) {
3833 /* filter back into light list */
3835 for ( i = 255; i >= 0; i-- )
3838 for ( light = buckets[ i ]; light != NULL; light = light2 )
3840 light2 = light->next;
3841 light->next = lights;
3846 /* emit some statistics */
3847 Sys_Printf( "%9d total lights\n", numLights );
3848 Sys_Printf( "%9d culled lights\n", numCulledLights );
3854 CreateTraceLightsForBounds()
3855 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3858 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3861 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3862 float radius, dist, length;
3865 /* potential pre-setup */
3866 if ( numLights == 0 ) {
3867 SetupEnvelopes( qfalse, fast );
3871 //% 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 ] );
3873 /* allocate the light list */
3874 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3875 trace->numLights = 0;
3877 /* calculate spherical bounds */
3878 VectorAdd( mins, maxs, origin );
3879 VectorScale( origin, 0.5f, origin );
3880 VectorSubtract( maxs, origin, dir );
3881 radius = (float) VectorLength( dir );
3883 /* get length of normal vector */
3884 if ( normal != NULL ) {
3885 length = VectorLength( normal );
3889 normal = nullVector;
3893 /* test each light and see if it reaches the sphere */
3894 /* note: the attenuation code MUST match LightingAtSample() */
3895 for ( light = lights; light; light = light->next )
3897 /* check zero sized envelope */
3898 if ( light->envelope <= 0 ) {
3899 lightsEnvelopeCulled++;
3904 if ( !( light->flags & flags ) ) {
3908 /* sunlight skips all this nonsense */
3909 if ( light->type != EMIT_SUN ) {
3915 /* check against pvs cluster */
3916 if ( numClusters > 0 && clusters != NULL ) {
3917 for ( i = 0; i < numClusters; i++ )
3919 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3925 if ( i == numClusters ) {
3926 lightsClusterCulled++;
3931 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3932 VectorSubtract( light->origin, origin, dir );
3933 dist = VectorLength( dir );
3934 dist -= light->envelope;
3937 lightsEnvelopeCulled++;
3941 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3944 for ( i = 0; i < 3; i++ )
3946 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3951 lightsBoundsCulled++;
3957 /* planar surfaces (except twosided surfaces) have a couple more checks */
3958 if ( length > 0.0f && trace->twoSided == qfalse ) {
3959 /* lights coplanar with a surface won't light it */
3960 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
3961 lightsPlaneCulled++;
3965 /* check to see if light is behind the plane */
3966 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
3967 lightsPlaneCulled++;
3972 /* add this light */
3973 trace->lights[ trace->numLights++ ] = light;
3976 /* make last night null */
3977 trace->lights[ trace->numLights ] = NULL;
3982 void FreeTraceLights( trace_t *trace ){
3983 if ( trace->lights != NULL ) {
3984 free( trace->lights );
3991 CreateTraceLightsForSurface()
3992 creates a list of lights that can potentially affect a drawsurface
3995 void CreateTraceLightsForSurface( int num, trace_t *trace ){
3997 vec3_t mins, maxs, normal;
3999 bspDrawSurface_t *ds;
4000 surfaceInfo_t *info;
4008 /* get drawsurface and info */
4009 ds = &bspDrawSurfaces[ num ];
4010 info = &surfaceInfos[ num ];
4012 /* get the mins/maxs for the dsurf */
4013 ClearBounds( mins, maxs );
4014 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4015 for ( i = 0; i < ds->numVerts; i++ )
4017 dv = &yDrawVerts[ ds->firstVert + i ];
4018 AddPointToBounds( dv->xyz, mins, maxs );
4019 if ( !VectorCompare( dv->normal, normal ) ) {
4020 VectorClear( normal );
4024 /* create the lights for the bounding box */
4025 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4028 /////////////////////////////////////////////////////////////
4030 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4031 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4032 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4033 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4035 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4036 static int numFloodVectors = 0;
4038 void SetupFloodLight( void ){
4040 float angle, elevation, angleStep, elevationStep;
4042 double v1,v2,v3,v4,v5,v6;
4045 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4047 /* calculate angular steps */
4048 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4049 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4053 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4055 /* iterate elevation */
4056 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4058 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4059 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4060 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4065 /* emit some statistics */
4066 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4069 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4071 if ( value[ 0 ] != '\0' ) {
4073 v4 = floodlightDistance;
4074 v5 = floodlightIntensity;
4075 v6 = floodlightDirectionScale;
4077 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4079 floodlightRGB[0] = v1;
4080 floodlightRGB[1] = v2;
4081 floodlightRGB[2] = v3;
4083 if ( VectorLength( floodlightRGB ) == 0 ) {
4084 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4097 floodlightDistance = v4;
4098 floodlightIntensity = v5;
4099 floodlightDirectionScale = v6;
4101 floodlighty = qtrue;
4102 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4106 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4109 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4110 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4111 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4113 ColorNormalize( floodlightRGB,floodlightRGB );
4117 FloodLightForSample()
4118 calculates floodlight value for a given sample
4119 once again, kudos to the dirtmapping coder
4122 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4127 float gatherLight, outLight;
4128 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4136 if ( trace == NULL || trace->cluster < 0 ) {
4142 dd = floodLightDistance;
4143 VectorCopy( trace->normal, normal );
4145 /* check if the normal is aligned to the world-up */
4146 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4147 if ( normal[ 2 ] == 1.0f ) {
4148 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4149 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4151 else if ( normal[ 2 ] == -1.0f ) {
4152 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4153 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4158 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4159 CrossProduct( normal, worldUp, myRt );
4160 VectorNormalize( myRt, myRt );
4161 CrossProduct( myRt, normal, myUp );
4162 VectorNormalize( myUp, myUp );
4165 /* vortex: optimise floodLightLowQuality a bit */
4166 if ( floodLightLowQuality == qtrue ) {
4167 /* iterate through ordered vectors */
4168 for ( i = 0; i < numFloodVectors; i++ )
4169 if ( rand() % 10 != 0 ) {
4175 /* iterate through ordered vectors */
4176 for ( i = 0; i < numFloodVectors; i++ )
4180 /* transform vector into tangent space */
4181 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4182 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4183 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4186 VectorMA( trace->origin, dd, direction, trace->end );
4188 //VectorMA( trace->origin, 1, direction, trace->origin );
4190 SetupTrace( trace );
4191 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4196 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4197 contribution = 1.0f;
4199 else if ( trace->opaque ) {
4200 VectorSubtract( trace->hit, trace->origin, displacement );
4201 d = VectorLength( displacement );
4203 // d=trace->distance;
4204 //if (d>256) gatherDirt+=1;
4205 contribution = d / dd;
4206 if ( contribution > 1 ) {
4207 contribution = 1.0f;
4210 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4213 gatherLight += contribution;
4218 if ( gatherLight <= 0.0f ) {
4227 gatherLight /= ( sub );
4229 outLight = gatherLight;
4230 if ( outLight > 1.0f ) {
4234 /* return to sender */
4239 FloodLightRawLightmap
4240 lighttracer style ambient occlusion light hack.
4241 Kudos to the dirtmapping author for most of this source.
4242 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4243 VorteX: fixed problems with deluxemapping
4246 // floodlight pass on a lightmap
4247 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4248 int i, x, y, *cluster;
4249 float *origin, *normal, *floodlight, floodLightAmount;
4250 surfaceInfo_t *info;
4253 // float samples, average, *floodlight2;
4255 memset( &trace,0,sizeof( trace_t ) );
4258 trace.testOcclusion = qtrue;
4259 trace.forceSunlight = qfalse;
4260 trace.twoSided = qtrue;
4261 trace.recvShadows = lm->recvShadows;
4262 trace.numSurfaces = lm->numLightSurfaces;
4263 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4264 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4265 trace.testAll = qfalse;
4266 trace.distance = 1024;
4268 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4269 //trace.twoSided = qfalse;
4270 for ( i = 0; i < trace.numSurfaces; i++ )
4273 info = &surfaceInfos[ trace.surfaces[ i ] ];
4275 /* check twosidedness */
4276 if ( info->si->twoSided ) {
4277 trace.twoSided = qtrue;
4282 /* gather floodlight */
4283 for ( y = 0; y < lm->sh; y++ )
4285 for ( x = 0; x < lm->sw; x++ )
4288 cluster = SUPER_CLUSTER( x, y );
4289 origin = SUPER_ORIGIN( x, y );
4290 normal = SUPER_NORMAL( x, y );
4291 floodlight = SUPER_FLOODLIGHT( x, y );
4293 /* set default dirt */
4296 /* only look at mapped luxels */
4297 if ( *cluster < 0 ) {
4302 trace.cluster = *cluster;
4303 VectorCopy( origin, trace.origin );
4304 VectorCopy( normal, trace.normal );
4306 /* get floodlight */
4307 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4309 /* add floodlight */
4310 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4311 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4312 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4313 floodlight[3] += floodlightDirectionScale;
4317 /* testing no filtering */
4323 for ( y = 0; y < lm->sh; y++ )
4325 for ( x = 0; x < lm->sw; x++ )
4328 cluster = SUPER_CLUSTER( x, y );
4329 floodlight = SUPER_FLOODLIGHT( x, y );
4331 /* filter dirt by adjacency to unmapped luxels */
4332 average = *floodlight;
4334 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4336 if ( sy < 0 || sy >= lm->sh ) {
4340 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4342 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4346 /* get neighboring luxel */
4347 cluster = SUPER_CLUSTER( sx, sy );
4348 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4349 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4354 average += *floodlight2;
4359 if ( samples <= 0.0f ) {
4365 if ( samples <= 0.0f ) {
4370 *floodlight = average / samples;
4376 void FloodLightRawLightmap( int rawLightmapNum ){
4379 /* bail if this number exceeds the number of raw lightmaps */
4380 if ( rawLightmapNum >= numRawLightmaps ) {
4384 lm = &rawLightmaps[ rawLightmapNum ];
4387 if ( floodlighty && floodlightIntensity ) {
4388 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4392 if ( lm->floodlightIntensity ) {
4393 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4394 numSurfacesFloodlighten += 1;
4398 void FloodlightRawLightmaps(){
4399 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4400 numSurfacesFloodlighten = 0;
4401 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4402 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4406 FloodLightIlluminate()
4407 illuminate floodlight into lightmap luxels
4410 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4411 float *luxel, *floodlight, *deluxel, *normal;
4414 int x, y, lightmapNum;
4416 /* walk lightmaps */
4417 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4420 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4424 /* apply floodlight to each luxel */
4425 for ( y = 0; y < lm->sh; y++ )
4427 for ( x = 0; x < lm->sw; x++ )
4429 /* get floodlight */
4430 floodlight = SUPER_FLOODLIGHT( x, y );
4431 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4436 cluster = SUPER_CLUSTER( x, y );
4438 /* only process mapped luxels */
4439 if ( *cluster < 0 ) {
4443 /* get particulars */
4444 luxel = SUPER_LUXEL( lightmapNum, x, y );
4445 deluxel = SUPER_DELUXEL( x, y );
4447 /* add to lightmap */
4448 luxel[0] += floodlight[0];
4449 luxel[1] += floodlight[1];
4450 luxel[2] += floodlight[2];
4452 if ( luxel[3] == 0 ) {
4456 /* add to deluxemap */
4457 if ( deluxemap && floodlight[3] > 0 ) {
4460 normal = SUPER_NORMAL( x, y );
4461 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4463 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4464 if ( brightness < 0.00390625f ) {
4465 brightness = 0.00390625f;
4468 VectorScale( normal, brightness, lightvector );
4469 VectorAdd( deluxel, lightvector, deluxel );