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 ];
135 void ColorToBytesNonZero( const float *color, byte *colorBytes, float scale) {
137 ColorToBytes(color, colorBytes, scale);
138 for (i = 0; i < 3; ++i)
139 if (colorBytes[i] == 0)
144 /* -------------------------------------------------------------------------------
146 this section deals with phong shading (normal interpolation across brush faces)
148 ------------------------------------------------------------------------------- */
152 smooths together coincident vertex normals across the bsp
155 #define MAX_SAMPLES 256
156 #define THETA_EPSILON 0.000001
157 #define EQUAL_NORMAL_EPSILON 0.01
159 void SmoothNormals( void ){
160 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
161 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
162 bspDrawSurface_t *ds;
166 vec3_t average, diff;
167 int indexes[ MAX_SAMPLES ];
168 vec3_t votes[ MAX_SAMPLES ];
171 /* allocate shade angle table */
172 shadeAngles = safe_malloc0( numBSPDrawVerts * sizeof( float ) );
174 /* allocate smoothed table */
175 cs = ( numBSPDrawVerts / 8 ) + 1;
176 smoothed = safe_malloc0( cs );
178 /* set default shade angle */
179 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
182 /* run through every surface and flag verts belonging to non-lightmapped surfaces
183 and set per-vertex smoothing angle */
184 for ( i = 0; i < numBSPDrawSurfaces; i++ )
187 ds = &bspDrawSurfaces[ i ];
189 /* get shader for shade angle */
190 si = surfaceInfos[ i ].si;
191 if ( si->shadeAngleDegrees ) {
192 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
195 shadeAngle = defaultShadeAngle;
197 if ( shadeAngle > maxShadeAngle ) {
198 maxShadeAngle = shadeAngle;
202 for ( j = 0; j < ds->numVerts; j++ )
204 f = ds->firstVert + j;
205 shadeAngles[ f ] = shadeAngle;
206 if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
207 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
211 /* ydnar: optional force-to-trisoup */
212 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
213 ds->surfaceType = MST_TRIANGLE_SOUP;
214 ds->lightmapNum[ 0 ] = -3;
218 /* bail if no surfaces have a shade angle */
219 if ( maxShadeAngle == 0 ) {
227 start = I_FloatTime();
229 /* go through the list of vertexes */
230 for ( i = 0; i < numBSPDrawVerts; i++ )
233 f = 10 * i / numBSPDrawVerts;
236 Sys_Printf( "%i...", f );
239 /* already smoothed? */
240 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
245 VectorClear( average );
249 /* build a table of coincident vertexes */
250 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
252 /* already smoothed? */
253 if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
258 if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
262 /* use smallest shade angle */
263 shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
265 /* check shade angle */
266 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
270 else if ( dot < -1.0 ) {
273 testAngle = acos( dot ) + THETA_EPSILON;
274 if ( testAngle >= shadeAngle ) {
275 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
278 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
280 /* add to the list */
281 indexes[ numVerts++ ] = j;
284 smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
286 /* see if this normal has already been voted */
287 for ( k = 0; k < numVotes; k++ )
289 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
290 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
291 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
292 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
297 /* add a new vote? */
298 if ( k == numVotes && numVotes < MAX_SAMPLES ) {
299 VectorAdd( average, bspDrawVerts[ j ].normal, average );
300 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
305 /* don't average for less than 2 verts */
306 if ( numVerts < 2 ) {
311 if ( VectorNormalize( average, average ) > 0 ) {
313 for ( j = 0; j < numVerts; j++ )
314 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
318 /* free the tables */
323 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
328 /* -------------------------------------------------------------------------------
330 this section deals with phong shaded lightmap tracing
332 ------------------------------------------------------------------------------- */
334 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
338 calculates the st tangent vectors for normalmapping
341 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
347 /* calculate barycentric basis for the triangle */
348 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 ] );
349 if ( fabs( bb ) < 0.00000001f ) {
354 for ( i = 0; i < numVerts; i++ )
356 /* calculate s tangent vector */
357 s = dv[ i ]->st[ 0 ] + 10.0f;
358 t = dv[ i ]->st[ 1 ];
359 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
360 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
361 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
363 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
367 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
368 VectorNormalize( stv[ i ], stv[ i ] );
370 /* calculate t tangent vector */
371 s = dv[ i ]->st[ 0 ];
372 t = dv[ i ]->st[ 1 ] + 10.0f;
373 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
374 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
375 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
377 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
378 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
379 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
381 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
382 VectorNormalize( ttv[ i ], ttv[ i ] );
385 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
386 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
389 /* return to caller */
398 perterbs the normal by the shader's normalmap in tangent space
401 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
407 VectorCopy( dv->normal, pNormal );
409 /* sample normalmap */
410 if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
414 /* remap sampled normal from [0,255] to [-1,-1] */
415 for ( i = 0; i < 3; i++ )
416 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
418 /* scale tangent vectors and add to original normal */
419 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
420 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
421 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
423 /* renormalize and return */
424 VectorNormalize( pNormal, pNormal );
431 maps a luxel for triangle bv at
435 #define BOGUS_NUDGE -99999.0f
437 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 ] ){
438 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
439 float *luxel, *origin, *normal, d, lightmapSampleOffset;
446 vec4_t sideplane, hostplane;
451 static float nudges[][ 2 ] =
453 //%{ 0, 0 }, /* try center first */
454 { -NUDGE, 0 }, /* left */
455 { NUDGE, 0 }, /* right */
456 { 0, NUDGE }, /* up */
457 { 0, -NUDGE }, /* down */
458 { -NUDGE, NUDGE }, /* left/up */
459 { NUDGE, -NUDGE }, /* right/down */
460 { NUDGE, NUDGE }, /* right/up */
461 { -NUDGE, -NUDGE }, /* left/down */
462 { BOGUS_NUDGE, BOGUS_NUDGE }
466 /* find luxel xy coords (fixme: subtract 0.5?) */
467 x = dv->lightmap[ 0 ][ 0 ];
468 y = dv->lightmap[ 0 ][ 1 ];
472 else if ( x >= lm->sw ) {
478 else if ( y >= lm->sh ) {
482 /* set shader and cluster list */
483 if ( info != NULL ) {
485 numClusters = info->numSurfaceClusters;
486 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
495 /* get luxel, origin, cluster, and normal */
496 luxel = SUPER_LUXEL( 0, x, y );
497 origin = SUPER_ORIGIN( x, y );
498 normal = SUPER_NORMAL( x, y );
499 cluster = SUPER_CLUSTER( x, y );
501 /* don't attempt to remap occluded luxels for planar surfaces */
502 if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
506 /* only average the normal for premapped luxels */
507 else if ( ( *cluster ) >= 0 ) {
508 /* do bumpmap calculations */
510 PerturbNormal( dv, si, pNormal, stv, ttv );
513 VectorCopy( dv->normal, pNormal );
516 /* add the additional normal data */
517 VectorAdd( normal, pNormal, normal );
522 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
526 /* axial lightmap projection */
527 if ( lm->vecs != NULL ) {
528 /* calculate an origin for the sample from the lightmap vectors */
529 VectorCopy( lm->origin, origin );
530 for ( i = 0; i < 3; i++ )
532 /* add unless it's the axis, which is taken care of later */
533 if ( i == lm->axisNum ) {
536 origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
539 /* project the origin onto the plane */
540 d = DotProduct( origin, plane ) - plane[ 3 ];
541 d /= plane[ lm->axisNum ];
542 origin[ lm->axisNum ] -= d;
545 /* non axial lightmap projection (explicit xyz) */
547 VectorCopy( dv->xyz, origin );
550 //////////////////////
551 //27's test to make sure samples stay within the triangle boundaries
552 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
553 //2) if it does, nudge it onto the correct side.
555 if ( worldverts != NULL && lightmapTriangleCheck ) {
556 for ( j = 0; j < 3; j++ )
558 VectorCopy( worldverts[j],cverts[j] );
560 PlaneFromPoints( hostplane,cverts[0],cverts[1],cverts[2] );
562 for ( j = 0; j < 3; j++ )
564 for ( i = 0; i < 3; i++ )
566 //build plane using 2 edges and a normal
567 next = ( i + 1 ) % 3;
569 VectorCopy( cverts[next],temp );
570 VectorAdd( temp,hostplane,temp );
571 PlaneFromPoints( sideplane,cverts[i],cverts[ next ], temp );
573 //planetest sample point
574 e = DotProduct( origin,sideplane );
575 e = e - sideplane[3];
578 //VectorClear(origin);
579 //Move the sample point back inside triangle bounds
580 origin[0] -= sideplane[0] * ( e + 1 );
581 origin[1] -= sideplane[1] * ( e + 1 );
582 origin[2] -= sideplane[2] * ( e + 1 );
584 VectorClear( origin );
591 ////////////////////////
593 /* planar surfaces have precalculated lightmap vectors for nudging */
594 if ( lm->plane != NULL ) {
595 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
596 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
597 VectorCopy( lm->plane, vecs[ 2 ] );
600 /* non-planar surfaces must calculate them */
603 if ( plane != NULL ) {
604 VectorCopy( plane, vecs[ 2 ] );
607 VectorCopy( dv->normal, vecs[ 2 ] );
609 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
612 /* push the origin off the surface a bit */
614 lightmapSampleOffset = si->lightmapSampleOffset;
617 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
619 if ( lm->axisNum < 0 ) {
620 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
622 else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
623 origin[ lm->axisNum ] -= lightmapSampleOffset;
626 origin[ lm->axisNum ] += lightmapSampleOffset;
629 VectorCopy( origin,origintwo );
630 if ( lightmapExtraVisClusterNudge ) {
631 origintwo[0] += vecs[2][0];
632 origintwo[1] += vecs[2][1];
633 origintwo[2] += vecs[2][2];
637 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
639 /* another retarded hack, storing nudge count in luxel[ 1 ] */
642 /* point in solid? (except in dark mode) */
643 if ( pointCluster < 0 && dark == qfalse ) {
644 /* nudge the the location around */
646 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
648 /* nudge the vector around a bit */
649 for ( i = 0; i < 3; i++ )
651 /* set nudged point*/
652 nudged[ i ] = origintwo[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
656 /* get pvs cluster */
657 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
658 if ( pointCluster >= 0 ) {
659 VectorCopy( nudged, origin );
665 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
666 if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
667 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
668 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
669 if ( pointCluster >= 0 ) {
670 VectorCopy( nudged, origin );
676 if ( pointCluster < 0 ) {
677 ( *cluster ) = CLUSTER_OCCLUDED;
678 VectorClear( origin );
679 VectorClear( normal );
685 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
687 /* do bumpmap calculations */
689 PerturbNormal( dv, si, pNormal, stv, ttv );
692 VectorCopy( dv->normal, pNormal );
695 /* store the cluster and normal */
696 ( *cluster ) = pointCluster;
697 VectorCopy( pNormal, normal );
699 /* store explicit mapping pass and implicit mapping pass */
714 recursively subdivides a triangle until its edges are shorter
715 than the distance between two luxels (thanks jc :)
718 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 ] ){
719 bspDrawVert_t mid, *dv2[ 3 ];
723 /* map the vertexes */
725 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
726 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
727 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
733 float *a, *b, dx, dy, dist, maxDist;
736 /* find the longest edge and split it */
739 for ( i = 0; i < 3; i++ )
742 a = dv[ i ]->lightmap[ 0 ];
743 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
746 dx = a[ 0 ] - b[ 0 ];
747 dy = a[ 1 ] - b[ 1 ];
748 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
751 if ( dist > maxDist ) {
757 /* try to early out */
758 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
763 /* split the longest edge and map it */
764 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
765 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
767 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
768 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
770 /* recurse to first triangle */
771 VectorCopy( dv, dv2 );
773 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
775 /* recurse to second triangle */
776 VectorCopy( dv, dv2 );
777 dv2[ ( max + 1 ) % 3 ] = ∣
778 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
785 seed function for MapTriangle_r()
786 requires a cw ordered triangle
789 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
792 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
793 vec3_t worldverts[ 3 ];
796 /* get plane if possible */
797 if ( lm->plane != NULL ) {
798 VectorCopy( lm->plane, plane );
799 plane[ 3 ] = lm->plane[ 3 ];
802 /* otherwise make one from the points */
803 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
807 /* check to see if we need to calculate texture->world tangent vectors */
808 if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
818 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
819 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
820 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
822 /* map the vertexes */
823 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
824 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
825 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
827 /* 2002-11-20: prefer axial triangle edges */
829 /* subdivide the triangle */
830 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
834 for ( i = 0; i < 3; i++ )
837 bspDrawVert_t *dv2[ 3 ];
841 a = dv[ i ]->lightmap[ 0 ];
842 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
844 /* make degenerate triangles for mapping edges */
845 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
847 dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
848 dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
850 /* map the degenerate triangle */
851 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
862 recursively subdivides a quad until its edges are shorter
863 than the distance between two luxels
866 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 ] ){
867 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
874 float *a, *b, dx, dy, dist, maxDist;
877 /* find the longest edge and split it */
880 for ( i = 0; i < 4; i++ )
883 a = dv[ i ]->lightmap[ 0 ];
884 b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
887 dx = a[ 0 ] - b[ 0 ];
888 dy = a[ 1 ] - b[ 1 ];
889 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
892 if ( dist > maxDist ) {
898 /* try to early out */
899 if ( max < 0 || maxDist <= subdivideThreshold ) {
904 /* we only care about even/odd edges */
907 /* split the longest edges */
908 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
909 LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
911 /* map the vertexes */
912 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
913 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
917 /* recurse to first quad */
919 dv2[ 1 ] = &mid[ 0 ];
920 dv2[ 2 ] = &mid[ 1 ];
922 MapQuad_r( lm, info, dv2, plane, stv, ttv );
924 /* recurse to second quad */
925 dv2[ 0 ] = &mid[ 0 ];
928 dv2[ 3 ] = &mid[ 1 ];
929 MapQuad_r( lm, info, dv2, plane, stv, ttv );
935 /* recurse to first quad */
938 dv2[ 2 ] = &mid[ 0 ];
939 dv2[ 3 ] = &mid[ 1 ];
940 MapQuad_r( lm, info, dv2, plane, stv, ttv );
942 /* recurse to second quad */
943 dv2[ 0 ] = &mid[ 1 ];
944 dv2[ 1 ] = &mid[ 0 ];
947 MapQuad_r( lm, info, dv2, plane, stv, ttv );
955 seed function for MapQuad_r()
956 requires a cw ordered triangle quad
959 #define QUAD_PLANAR_EPSILON 0.5f
961 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
964 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
967 /* get plane if possible */
968 if ( lm->plane != NULL ) {
969 VectorCopy( lm->plane, plane );
970 plane[ 3 ] = lm->plane[ 3 ];
973 /* otherwise make one from the points */
974 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
978 /* 4th point must fall on the plane */
979 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
980 if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
984 /* check to see if we need to calculate texture->world tangent vectors */
985 if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
995 /* map the vertexes */
996 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
997 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
998 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
999 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
1001 /* subdivide the quad */
1002 MapQuad_r( lm, info, dv, plane, stv, ttv );
1010 maps the locations, normals, and pvs clusters for a raw lightmap
1013 #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)
1015 void MapRawLightmap( int rawLightmapNum ){
1016 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1017 float *luxel, *origin, *normal, samples, radius, pass;
1019 bspDrawSurface_t *ds;
1020 surfaceInfo_t *info;
1021 mesh_t src, *subdivided, *mesh;
1022 bspDrawVert_t *verts, *dv[ 4 ], fake;
1025 /* bail if this number exceeds the number of raw lightmaps */
1026 if ( rawLightmapNum >= numRawLightmaps ) {
1031 lm = &rawLightmaps[ rawLightmapNum ];
1033 /* -----------------------------------------------------------------
1034 map referenced surfaces onto the raw lightmap
1035 ----------------------------------------------------------------- */
1037 /* walk the list of surfaces on this raw lightmap */
1038 for ( n = 0; n < lm->numLightSurfaces; n++ )
1040 /* with > 1 surface per raw lightmap, clear occluded */
1042 for ( y = 0; y < lm->sh; y++ )
1044 for ( x = 0; x < lm->sw; x++ )
1047 cluster = SUPER_CLUSTER( x, y );
1048 if ( *cluster < 0 ) {
1049 *cluster = CLUSTER_UNMAPPED;
1056 num = lightSurfaces[ lm->firstLightSurface + n ];
1057 ds = &bspDrawSurfaces[ num ];
1058 info = &surfaceInfos[ num ];
1060 /* bail if no lightmap to calculate */
1061 if ( info->lm != lm ) {
1066 /* map the surface onto the lightmap origin/cluster/normal buffers */
1067 switch ( ds->surfaceType )
1071 verts = yDrawVerts + ds->firstVert;
1073 /* map the triangles */
1074 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1076 for ( i = 0; i < ds->numIndexes; i += 3 )
1078 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1079 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1080 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1081 MapTriangle( lm, info, dv, mapNonAxial );
1087 /* make a mesh from the drawsurf */
1088 src.width = ds->patchWidth;
1089 src.height = ds->patchHeight;
1090 src.verts = &yDrawVerts[ ds->firstVert ];
1091 //% subdivided = SubdivideMesh( src, 8, 512 );
1092 subdivided = SubdivideMesh2( src, info->patchIterations );
1094 /* fit it to the curve and remove colinear verts on rows/columns */
1095 PutMeshOnCurve( *subdivided );
1096 mesh = RemoveLinearMeshColumnsRows( subdivided );
1097 FreeMesh( subdivided );
1100 verts = mesh->verts;
1105 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1106 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1107 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1108 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1112 /* map the mesh quads */
1115 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1117 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1119 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1122 pw[ 0 ] = x + ( y * mesh->width );
1123 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1124 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1125 pw[ 3 ] = x + 1 + ( y * mesh->width );
1126 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1131 /* get drawverts and map first triangle */
1132 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1133 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1134 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1135 MapTriangle( lm, info, dv, mapNonAxial );
1137 /* get drawverts and map second triangle */
1138 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1139 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1140 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1141 MapTriangle( lm, info, dv, mapNonAxial );
1148 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1150 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1153 pw[ 0 ] = x + ( y * mesh->width );
1154 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1155 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1156 pw[ 3 ] = x + 1 + ( y * mesh->width );
1162 /* attempt to map quad first */
1163 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1164 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1165 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1166 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1167 if ( MapQuad( lm, info, dv ) ) {
1171 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1173 /* get drawverts and map first triangle */
1174 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1175 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1176 MapTriangle( lm, info, dv, mapNonAxial );
1178 /* get drawverts and map second triangle */
1179 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1180 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1181 MapTriangle( lm, info, dv, mapNonAxial );
1197 /* -----------------------------------------------------------------
1198 average and clean up luxel normals
1199 ----------------------------------------------------------------- */
1201 /* walk the luxels */
1202 for ( y = 0; y < lm->sh; y++ )
1204 for ( x = 0; x < lm->sw; x++ )
1207 luxel = SUPER_LUXEL( 0, x, y );
1208 normal = SUPER_NORMAL( x, y );
1209 cluster = SUPER_CLUSTER( x, y );
1211 /* only look at mapped luxels */
1212 if ( *cluster < 0 ) {
1216 /* the normal data could be the sum of multiple samples */
1217 if ( luxel[ 3 ] > 1.0f ) {
1218 VectorNormalize( normal, normal );
1221 /* mark this luxel as having only one normal */
1226 /* non-planar surfaces stop here */
1227 if ( lm->plane == NULL ) {
1231 /* -----------------------------------------------------------------
1232 map occluded or unuxed luxels
1233 ----------------------------------------------------------------- */
1235 /* walk the luxels */
1236 radius = floor( superSample / 2 );
1237 radius = radius > 0 ? radius : 1.0f;
1239 for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1241 for ( y = 0; y < lm->sh; y++ )
1243 for ( x = 0; x < lm->sw; x++ )
1246 luxel = SUPER_LUXEL( 0, x, y );
1247 normal = SUPER_NORMAL( x, y );
1248 cluster = SUPER_CLUSTER( x, y );
1250 /* only look at unmapped luxels */
1251 if ( *cluster != CLUSTER_UNMAPPED ) {
1255 /* divine a normal and origin from neighboring luxels */
1256 VectorClear( fake.xyz );
1257 VectorClear( fake.normal );
1258 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1259 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1261 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1263 if ( sy < 0 || sy >= lm->sh ) {
1267 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1269 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1273 /* get neighboring luxel */
1274 luxel = SUPER_LUXEL( 0, sx, sy );
1275 origin = SUPER_ORIGIN( sx, sy );
1276 normal = SUPER_NORMAL( sx, sy );
1277 cluster = SUPER_CLUSTER( sx, sy );
1279 /* only consider luxels mapped in previous passes */
1280 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1284 /* add its distinctiveness to our own */
1285 VectorAdd( fake.xyz, origin, fake.xyz );
1286 VectorAdd( fake.normal, normal, fake.normal );
1287 samples += luxel[ 3 ];
1292 if ( samples == 0.0f ) {
1297 VectorDivide( fake.xyz, samples, fake.xyz );
1298 //% VectorDivide( fake.normal, samples, fake.normal );
1299 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1303 /* map the fake vert */
1304 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1309 /* -----------------------------------------------------------------
1310 average and clean up luxel normals
1311 ----------------------------------------------------------------- */
1313 /* walk the luxels */
1314 for ( y = 0; y < lm->sh; y++ )
1316 for ( x = 0; x < lm->sw; x++ )
1319 luxel = SUPER_LUXEL( 0, x, y );
1320 normal = SUPER_NORMAL( x, y );
1321 cluster = SUPER_CLUSTER( x, y );
1323 /* only look at mapped luxels */
1324 if ( *cluster < 0 ) {
1328 /* the normal data could be the sum of multiple samples */
1329 if ( luxel[ 3 ] > 1.0f ) {
1330 VectorNormalize( normal, normal );
1333 /* mark this luxel as having only one normal */
1341 for ( y = 0; y < lm->sh; y++ )
1343 for ( x = 0; x < lm->sw; x++ )
1348 cluster = SUPER_CLUSTER( x, y );
1349 origin = SUPER_ORIGIN( x, y );
1350 normal = SUPER_NORMAL( x, y );
1351 luxel = SUPER_LUXEL( x, y );
1353 if ( *cluster < 0 ) {
1357 /* check if within the bounding boxes of all surfaces referenced */
1358 ClearBounds( mins, maxs );
1359 for ( n = 0; n < lm->numLightSurfaces; n++ )
1362 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1363 TOL = info->sampleSize + 2;
1364 AddPointToBounds( info->mins, mins, maxs );
1365 AddPointToBounds( info->maxs, mins, maxs );
1366 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1367 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1368 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1374 if ( n < lm->numLightSurfaces ) {
1378 /* report bogus origin */
1379 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",
1380 rawLightmapNum, x, y, *cluster,
1381 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1382 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1383 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1394 sets up dirtmap (ambient occlusion)
1397 #define DIRT_CONE_ANGLE 88 /* degrees */
1398 #define DIRT_NUM_ANGLE_STEPS 16
1399 #define DIRT_NUM_ELEVATION_STEPS 3
1400 #define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1402 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1403 static int numDirtVectors = 0;
1405 void SetupDirt( void ){
1407 float angle, elevation, angleStep, elevationStep;
1411 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1413 /* calculate angular steps */
1414 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1415 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1419 for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1421 /* iterate elevation */
1422 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1424 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1425 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1426 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1431 /* emit some statistics */
1432 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1438 calculates dirt value for a given sample
1441 float DirtForSample( trace_t *trace ){
1443 float gatherDirt, outDirt, angle, elevation, ooDepth;
1444 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1451 if ( trace == NULL || trace->cluster < 0 ) {
1457 ooDepth = 1.0f / dirtDepth;
1458 VectorCopy( trace->normal, normal );
1460 /* check if the normal is aligned to the world-up */
1461 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
1462 if ( normal[ 2 ] == 1.0f ) {
1463 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1464 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1466 else if ( normal[ 2 ] == -1.0f ) {
1467 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1468 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1473 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1474 CrossProduct( normal, worldUp, myRt );
1475 VectorNormalize( myRt, myRt );
1476 CrossProduct( myRt, normal, myUp );
1477 VectorNormalize( myUp, myUp );
1480 /* 1 = random mode, 0 (well everything else) = non-random mode */
1481 if ( dirtMode == 1 ) {
1483 for ( i = 0; i < numDirtVectors; i++ )
1485 /* get random vector */
1486 angle = Random() * DEG2RAD( 360.0f );
1487 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1488 temp[ 0 ] = cos( angle ) * sin( elevation );
1489 temp[ 1 ] = sin( angle ) * sin( elevation );
1490 temp[ 2 ] = cos( elevation );
1492 /* transform into tangent space */
1493 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1494 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1495 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1498 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1499 SetupTrace( trace );
1500 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1504 if ( trace->opaque && !( trace->compileFlags & C_SKY ) ) {
1505 VectorSubtract( trace->hit, trace->origin, displacement );
1506 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1512 /* iterate through ordered vectors */
1513 for ( i = 0; i < numDirtVectors; i++ )
1515 /* transform vector into tangent space */
1516 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1517 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1518 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1521 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1522 SetupTrace( trace );
1523 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1527 if ( trace->opaque ) {
1528 VectorSubtract( trace->hit, trace->origin, displacement );
1529 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1535 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1536 SetupTrace( trace );
1537 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1541 if ( trace->opaque ) {
1542 VectorSubtract( trace->hit, trace->origin, displacement );
1543 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1547 if ( gatherDirt <= 0.0f ) {
1551 /* apply gain (does this even do much? heh) */
1552 outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1553 if ( outDirt > 1.0f ) {
1558 outDirt *= dirtScale;
1559 if ( outDirt > 1.0f ) {
1563 /* return to sender */
1564 return 1.0f - outDirt;
1571 calculates dirty fraction for each luxel
1574 void DirtyRawLightmap( int rawLightmapNum ){
1575 int i, x, y, sx, sy, *cluster;
1576 float *origin, *normal, *dirt, *dirt2, average, samples;
1578 surfaceInfo_t *info;
1583 /* bail if this number exceeds the number of raw lightmaps */
1584 if ( rawLightmapNum >= numRawLightmaps ) {
1589 lm = &rawLightmaps[ rawLightmapNum ];
1592 trace.testOcclusion = qtrue;
1593 trace.forceSunlight = qfalse;
1594 trace.recvShadows = lm->recvShadows;
1595 trace.numSurfaces = lm->numLightSurfaces;
1596 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1597 trace.inhibitRadius = 0.0f;
1598 trace.testAll = qfalse;
1600 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1601 trace.twoSided = qfalse;
1602 for ( i = 0; i < trace.numSurfaces; i++ )
1605 info = &surfaceInfos[ trace.surfaces[ i ] ];
1607 /* check twosidedness */
1608 if ( info->si->twoSided ) {
1609 trace.twoSided = qtrue;
1615 for ( i = 0; i < trace.numSurfaces; i++ )
1618 info = &surfaceInfos[ trace.surfaces[ i ] ];
1620 /* check twosidedness */
1621 if ( info->si->noDirty ) {
1628 for ( y = 0; y < lm->sh; y++ )
1630 for ( x = 0; x < lm->sw; x++ )
1633 cluster = SUPER_CLUSTER( x, y );
1634 origin = SUPER_ORIGIN( x, y );
1635 normal = SUPER_NORMAL( x, y );
1636 dirt = SUPER_DIRT( x, y );
1638 /* set default dirt */
1641 /* only look at mapped luxels */
1642 if ( *cluster < 0 ) {
1646 /* don't apply dirty on this surface */
1653 trace.cluster = *cluster;
1654 VectorCopy( origin, trace.origin );
1655 VectorCopy( normal, trace.normal );
1658 *dirt = DirtForSample( &trace );
1662 /* testing no filtering */
1666 for ( y = 0; y < lm->sh; y++ )
1668 for ( x = 0; x < lm->sw; x++ )
1671 cluster = SUPER_CLUSTER( x, y );
1672 dirt = SUPER_DIRT( x, y );
1674 /* filter dirt by adjacency to unmapped luxels */
1677 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1679 if ( sy < 0 || sy >= lm->sh ) {
1683 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1685 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1689 /* get neighboring luxel */
1690 cluster = SUPER_CLUSTER( sx, sy );
1691 dirt2 = SUPER_DIRT( sx, sy );
1692 if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1702 if ( samples <= 0.0f ) {
1708 if ( samples <= 0.0f ) {
1713 *dirt = average / samples;
1722 calculates the pvs cluster, origin, normal of a sub-luxel
1725 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1726 int i, *cluster, *cluster2;
1727 float *origin, *origin2, *normal; //% , *normal2;
1728 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1731 /* calulate x vector */
1732 if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1733 cluster = SUPER_CLUSTER( x, y );
1734 origin = SUPER_ORIGIN( x, y );
1735 //% normal = SUPER_NORMAL( x, y );
1736 cluster2 = SUPER_CLUSTER( x + 1, y );
1737 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1738 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1740 else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1741 cluster = SUPER_CLUSTER( x - 1, y );
1742 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1743 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1744 cluster2 = SUPER_CLUSTER( x, y );
1745 origin2 = SUPER_ORIGIN( x, y );
1746 //% normal2 = SUPER_NORMAL( x, y );
1749 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap S vector\n" );
1752 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1753 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1755 /* calulate y vector */
1756 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1757 cluster = SUPER_CLUSTER( x, y );
1758 origin = SUPER_ORIGIN( x, y );
1759 //% normal = SUPER_NORMAL( x, y );
1760 cluster2 = SUPER_CLUSTER( x, y + 1 );
1761 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1762 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1764 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1765 cluster = SUPER_CLUSTER( x, y - 1 );
1766 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1767 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1768 cluster2 = SUPER_CLUSTER( x, y );
1769 origin2 = SUPER_ORIGIN( x, y );
1770 //% normal2 = SUPER_NORMAL( x, y );
1773 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap T vector\n" );
1776 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1778 /* calculate new origin */
1779 for ( i = 0; i < 3; i++ )
1780 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1783 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1784 if ( *sampleCluster < 0 ) {
1788 /* calculate new normal */
1789 normal = SUPER_NORMAL( x, y );
1790 VectorCopy( normal, sampleNormal );
1798 SubsampleRawLuxel_r()
1799 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1802 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1803 int b, samples, mapped, lighted;
1806 vec3_t deluxel[ 3 ];
1807 vec3_t origin[ 4 ], normal[ 4 ];
1808 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1809 vec3_t color, direction = { 0, 0, 0 }, total;
1813 if ( lightLuxel[ 3 ] >= lightSamples ) {
1818 VectorClear( total );
1822 /* make 2x2 subsample stamp */
1823 for ( b = 0; b < 4; b++ )
1826 VectorCopy( sampleOrigin, origin[ b ] );
1828 /* calculate position */
1829 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1835 /* increment sample count */
1836 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1839 trace->cluster = *cluster;
1840 VectorCopy( origin[ b ], trace->origin );
1841 VectorCopy( normal[ b ], trace->normal );
1845 LightContributionToSample( trace );
1846 if ( trace->forceSubsampling > 1.0f ) {
1847 /* alphashadow: we subsample as deep as we can */
1853 /* add to totals (fixme: make contrast function) */
1854 VectorCopy( trace->color, luxel[ b ] );
1855 if ( lightDeluxel ) {
1856 VectorCopy( trace->directionContribution, deluxel[ b ] );
1858 VectorAdd( total, trace->color, total );
1859 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1864 /* subsample further? */
1865 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1866 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1867 lighted != 0 && lighted != mapped ) {
1868 for ( b = 0; b < 4; b++ )
1870 if ( cluster[ b ] < 0 ) {
1873 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1878 //% VectorClear( color );
1880 VectorCopy( lightLuxel, color );
1881 if ( lightDeluxel ) {
1882 VectorCopy( lightDeluxel, direction );
1885 for ( b = 0; b < 4; b++ )
1887 if ( cluster[ b ] < 0 ) {
1890 VectorAdd( color, luxel[ b ], color );
1891 if ( lightDeluxel ) {
1892 VectorAdd( direction, deluxel[ b ], direction );
1898 if ( samples > 0 ) {
1900 color[ 0 ] /= samples;
1901 color[ 1 ] /= samples;
1902 color[ 2 ] /= samples;
1905 VectorCopy( color, lightLuxel );
1906 lightLuxel[ 3 ] += 1.0f;
1908 if ( lightDeluxel ) {
1909 direction[ 0 ] /= samples;
1910 direction[ 1 ] /= samples;
1911 direction[ 2 ] /= samples;
1912 VectorCopy( direction, lightDeluxel );
1917 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1918 static void GaussLikeRandom( float sigma, float *x, float *y ){
1920 r = Random() * 2 * Q_PI;
1921 *x = sigma * 2.73861278752581783822 * cos( r );
1922 *y = sigma * 2.73861278752581783822 * sin( r );
1929 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1932 vec3_t origin, normal;
1933 vec3_t total, totaldirection;
1936 VectorClear( total );
1937 VectorClear( totaldirection );
1939 for ( b = 0; b < lightSamples; ++b )
1942 VectorCopy( sampleOrigin, origin );
1943 GaussLikeRandom( bias, &dx, &dy );
1945 /* calculate position */
1946 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1952 trace->cluster = cluster;
1953 VectorCopy( origin, trace->origin );
1954 VectorCopy( normal, trace->normal );
1956 LightContributionToSample( trace );
1957 VectorAdd( total, trace->color, total );
1958 if ( lightDeluxel ) {
1959 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1966 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1967 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1968 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1970 if ( lightDeluxel ) {
1971 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1972 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1973 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1981 IlluminateRawLightmap()
1982 illuminates the luxels
1985 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
1986 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
1987 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
1989 void IlluminateRawLightmap( int rawLightmapNum ){
1990 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1991 int *cluster, *cluster2, mapped, lighted, totalLighted;
1992 size_t llSize, ldSize;
1994 surfaceInfo_t *info;
1995 qboolean filterColor, filterDir;
1997 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1998 unsigned char *flag;
1999 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2000 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2001 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2003 float stackLightLuxels[ STACK_LL_SIZE ];
2006 /* bail if this number exceeds the number of raw lightmaps */
2007 if ( rawLightmapNum >= numRawLightmaps ) {
2012 lm = &rawLightmaps[ rawLightmapNum ];
2015 trace.testOcclusion = !noTrace;
2016 trace.forceSunlight = qfalse;
2017 trace.recvShadows = lm->recvShadows;
2018 trace.numSurfaces = lm->numLightSurfaces;
2019 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2020 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2022 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2023 trace.twoSided = qfalse;
2024 for ( i = 0; i < trace.numSurfaces; i++ )
2027 info = &surfaceInfos[ trace.surfaces[ i ] ];
2029 /* check twosidedness */
2030 if ( info->si->twoSided ) {
2031 trace.twoSided = qtrue;
2036 /* create a culled light list for this raw lightmap */
2037 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2039 /* -----------------------------------------------------------------
2041 ----------------------------------------------------------------- */
2044 numLuxelsIlluminated += ( lm->sw * lm->sh );
2046 /* test debugging state */
2047 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2048 /* debug fill the luxels */
2049 for ( y = 0; y < lm->sh; y++ )
2051 for ( x = 0; x < lm->sw; x++ )
2054 cluster = SUPER_CLUSTER( x, y );
2056 /* only fill mapped luxels */
2057 if ( *cluster < 0 ) {
2061 /* get particulars */
2062 luxel = SUPER_LUXEL( 0, x, y );
2063 origin = SUPER_ORIGIN( x, y );
2064 normal = SUPER_NORMAL( x, y );
2066 /* color the luxel with raw lightmap num? */
2067 if ( debugSurfaces ) {
2068 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2071 /* color the luxel with lightmap axis? */
2072 else if ( debugAxis ) {
2073 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2074 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2075 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2078 /* color the luxel with luxel cluster? */
2079 else if ( debugCluster ) {
2080 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2083 /* color the luxel with luxel origin? */
2084 else if ( debugOrigin ) {
2085 VectorSubtract( lm->maxs, lm->mins, temp );
2086 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2087 VectorSubtract( origin, lm->mins, temp2 );
2088 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2089 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2090 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2093 /* color the luxel with the normal */
2094 else if ( normalmap ) {
2095 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2096 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2097 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2100 /* otherwise clear it */
2102 VectorClear( luxel );
2112 /* allocate temporary per-light luxel storage */
2113 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2114 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2115 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2116 lightLuxels = stackLightLuxels;
2119 lightLuxels = safe_malloc( llSize );
2122 lightDeluxels = safe_malloc( ldSize );
2125 lightDeluxels = NULL;
2129 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2131 /* set ambient color */
2132 for ( y = 0; y < lm->sh; y++ )
2134 for ( x = 0; x < lm->sw; x++ )
2137 cluster = SUPER_CLUSTER( x, y );
2138 luxel = SUPER_LUXEL( 0, x, y );
2139 normal = SUPER_NORMAL( x, y );
2140 deluxel = SUPER_DELUXEL( x, y );
2142 /* blacken unmapped clusters */
2143 if ( *cluster < 0 ) {
2144 VectorClear( luxel );
2150 VectorCopy( ambientColor, luxel );
2152 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2154 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2155 if ( brightness < 0.00390625f ) {
2156 brightness = 0.00390625f;
2159 VectorScale( normal, brightness, deluxel );
2166 /* clear styled lightmaps */
2167 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2168 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2170 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2171 memset( lm->superLuxels[ lightmapNum ], 0, size );
2175 /* debugging code */
2176 //% if( trace.numLights <= 0 )
2177 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2179 /* walk light list */
2180 for ( i = 0; i < trace.numLights; i++ )
2183 trace.light = trace.lights[ i ];
2186 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2188 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2189 lm->styles[ lightmapNum ] == LS_NONE ) {
2194 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2195 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2196 Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2201 memset( lightLuxels, 0, llSize );
2203 memset( lightDeluxels, 0, ldSize );
2207 /* determine filter radius */
2208 filterRadius = lm->filterRadius > trace.light->filterRadius
2210 : trace.light->filterRadius;
2211 if ( filterRadius < 0.0f ) {
2212 filterRadius = 0.0f;
2215 /* set luxel filter radius */
2216 luxelFilterRadius = lm->sampleSize != 0 ? superSample * filterRadius / lm->sampleSize : 0;
2217 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2218 luxelFilterRadius = 1;
2221 /* allocate sampling flags storage */
2222 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2223 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2224 if ( lm->superFlags == NULL ) {
2225 lm->superFlags = safe_malloc( size );
2227 memset( (void *) lm->superFlags, 0, size );
2230 /* initial pass, one sample per luxel */
2231 for ( y = 0; y < lm->sh; y++ )
2233 for ( x = 0; x < lm->sw; x++ )
2236 cluster = SUPER_CLUSTER( x, y );
2237 if ( *cluster < 0 ) {
2241 /* get particulars */
2242 lightLuxel = LIGHT_LUXEL( x, y );
2243 lightDeluxel = LIGHT_DELUXEL( x, y );
2244 origin = SUPER_ORIGIN( x, y );
2245 normal = SUPER_NORMAL( x, y );
2246 flag = SUPER_FLAG( x, y );
2248 /* set contribution count */
2249 lightLuxel[ 3 ] = 1.0f;
2252 trace.cluster = *cluster;
2253 VectorCopy( origin, trace.origin );
2254 VectorCopy( normal, trace.normal );
2256 /* get light for this sample */
2257 LightContributionToSample( &trace );
2258 VectorCopy( trace.color, lightLuxel );
2260 /* add the contribution to the deluxemap */
2262 VectorCopy( trace.directionContribution, lightDeluxel );
2265 /* check for evilness */
2266 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2268 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2271 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2277 /* don't even bother with everything else if nothing was lit */
2278 if ( totalLighted == 0 ) {
2282 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2283 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2284 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2286 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2288 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2293 VectorClear( total );
2295 /* test 2x2 stamp */
2296 for ( t = 0; t < 4; t++ )
2298 /* set sample coords */
2299 sx = x + tests[ t ][ 0 ];
2300 sy = y + tests[ t ][ 1 ];
2303 cluster = SUPER_CLUSTER( sx, sy );
2304 if ( *cluster < 0 ) {
2310 flag = SUPER_FLAG( sx, sy );
2311 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2312 /* force a lighted/mapped discrepancy so we subsample */
2317 lightLuxel = LIGHT_LUXEL( sx, sy );
2318 VectorAdd( total, lightLuxel, total );
2319 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2324 /* if total color is under a certain amount, then don't bother subsampling */
2325 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2329 /* if all 4 pixels are either in shadow or light, then don't subsample */
2330 if ( lighted != 0 && lighted != mapped ) {
2331 for ( t = 0; t < 4; t++ )
2333 /* set sample coords */
2334 sx = x + tests[ t ][ 0 ];
2335 sy = y + tests[ t ][ 1 ];
2338 cluster = SUPER_CLUSTER( sx, sy );
2339 if ( *cluster < 0 ) {
2342 flag = SUPER_FLAG( sx, sy );
2343 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2346 lightLuxel = LIGHT_LUXEL( sx, sy );
2347 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2348 origin = SUPER_ORIGIN( sx, sy );
2350 /* only subsample shadowed luxels */
2351 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2355 if ( lightRandomSamples ) {
2356 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2359 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2362 *flag |= FLAG_ALREADY_SUBSAMPLED;
2364 /* debug code to colorize subsampled areas to yellow */
2365 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2366 //% VectorSet( luxel, 255, 204, 0 );
2373 /* tertiary pass, apply dirt map (ambient occlusion) */
2376 for ( y = 0; y < lm->sh; y++ )
2378 for ( x = 0; x < lm->sw; x++ )
2381 cluster = SUPER_CLUSTER( x, y );
2382 if ( *cluster < 0 ) {
2386 /* get particulars */
2387 lightLuxel = LIGHT_LUXEL( x, y );
2388 dirt = SUPER_DIRT( x, y );
2390 /* scale light value */
2391 VectorScale( lightLuxel, *dirt, lightLuxel );
2396 /* allocate sampling lightmap storage */
2397 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2398 /* allocate sampling lightmap storage */
2399 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2400 lm->superLuxels[ lightmapNum ] = safe_malloc0( size );
2404 if ( lightmapNum > 0 ) {
2405 lm->styles[ lightmapNum ] = trace.light->style;
2406 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2409 /* copy to permanent luxels */
2410 for ( y = 0; y < lm->sh; y++ )
2412 for ( x = 0; x < lm->sw; x++ )
2414 /* get cluster and origin */
2415 cluster = SUPER_CLUSTER( x, y );
2416 if ( *cluster < 0 ) {
2419 origin = SUPER_ORIGIN( x, y );
2422 if ( luxelFilterRadius ) {
2424 VectorClear( averageColor );
2425 VectorClear( averageDir );
2428 /* cheaper distance-based filtering */
2429 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2431 if ( sy < 0 || sy >= lm->sh ) {
2435 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2437 if ( sx < 0 || sx >= lm->sw ) {
2441 /* get particulars */
2442 cluster = SUPER_CLUSTER( sx, sy );
2443 if ( *cluster < 0 ) {
2446 lightLuxel = LIGHT_LUXEL( sx, sy );
2447 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2450 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2451 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2453 /* scale luxel by filter weight */
2454 VectorScale( lightLuxel, weight, color );
2455 VectorAdd( averageColor, color, averageColor );
2457 VectorScale( lightDeluxel, weight, direction );
2458 VectorAdd( averageDir, direction, averageDir );
2465 if ( samples <= 0.0f ) {
2469 /* scale into luxel */
2470 luxel = SUPER_LUXEL( lightmapNum, x, y );
2473 /* handle negative light */
2474 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2475 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2476 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2477 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2480 /* handle normal light */
2483 luxel[ 0 ] += averageColor[ 0 ] / samples;
2484 luxel[ 1 ] += averageColor[ 1 ] / samples;
2485 luxel[ 2 ] += averageColor[ 2 ] / samples;
2489 /* scale into luxel */
2490 deluxel = SUPER_DELUXEL( x, y );
2491 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2492 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2493 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2500 /* get particulars */
2501 lightLuxel = LIGHT_LUXEL( x, y );
2502 lightDeluxel = LIGHT_DELUXEL( x, y );
2503 luxel = SUPER_LUXEL( lightmapNum, x, y );
2504 deluxel = SUPER_DELUXEL( x, y );
2506 /* handle negative light */
2507 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2508 VectorScale( averageColor, -1.0f, averageColor );
2514 /* handle negative light */
2515 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2516 VectorSubtract( luxel, lightLuxel, luxel );
2519 /* handle normal light */
2521 VectorAdd( luxel, lightLuxel, luxel );
2525 VectorAdd( deluxel, lightDeluxel, deluxel );
2532 /* free temporary luxels */
2533 if ( lightLuxels != stackLightLuxels ) {
2534 free( lightLuxels );
2538 free( lightDeluxels );
2542 /* free light list */
2543 FreeTraceLights( &trace );
2545 /* floodlight pass */
2546 if ( floodlighty ) {
2547 FloodlightIlluminateLightmap( lm );
2550 if ( debugnormals ) {
2551 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2554 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2558 for ( y = 0; y < lm->sh; y++ )
2560 for ( x = 0; x < lm->sw; x++ )
2563 cluster = SUPER_CLUSTER( x, y );
2564 //% if( *cluster < 0 )
2567 /* get particulars */
2568 luxel = SUPER_LUXEL( lightmapNum, x, y );
2569 normal = SUPER_NORMAL( x, y );
2571 luxel[0] = ( normal[0] * 127 ) + 127;
2572 luxel[1] = ( normal[1] * 127 ) + 127;
2573 luxel[2] = ( normal[2] * 127 ) + 127;
2579 /* -----------------------------------------------------------------
2581 ----------------------------------------------------------------- */
2584 /* walk lightmaps */
2585 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2588 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2592 /* apply dirt to each luxel */
2593 for ( y = 0; y < lm->sh; y++ )
2595 for ( x = 0; x < lm->sw; x++ )
2598 cluster = SUPER_CLUSTER( x, y );
2600 /* get particulars */
2601 luxel = SUPER_LUXEL( lightmapNum, x, y );
2602 dirt = SUPER_DIRT( x, y );
2605 VectorScale( luxel, *dirt, luxel );
2609 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2616 /* -----------------------------------------------------------------
2618 ----------------------------------------------------------------- */
2620 /* walk lightmaps */
2621 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2624 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2628 /* average occluded luxels from neighbors */
2629 for ( y = 0; y < lm->sh; y++ )
2631 for ( x = 0; x < lm->sw; x++ )
2633 /* get particulars */
2634 cluster = SUPER_CLUSTER( x, y );
2635 luxel = SUPER_LUXEL( lightmapNum, x, y );
2636 deluxel = SUPER_DELUXEL( x, y );
2637 normal = SUPER_NORMAL( x, y );
2639 /* determine if filtering is necessary */
2640 filterColor = qfalse;
2642 if ( *cluster < 0 ||
2643 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2644 filterColor = qtrue;
2647 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2651 if ( !filterColor && !filterDir ) {
2655 /* choose seed amount */
2656 VectorClear( averageColor );
2657 VectorClear( averageDir );
2660 /* walk 3x3 matrix */
2661 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2663 if ( sy < 0 || sy >= lm->sh ) {
2667 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2669 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2673 /* get neighbor's particulars */
2674 cluster2 = SUPER_CLUSTER( sx, sy );
2675 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2676 deluxel2 = SUPER_DELUXEL( sx, sy );
2678 /* ignore unmapped/unlit luxels */
2679 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2680 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2684 /* add its distinctiveness to our own */
2685 VectorAdd( averageColor, luxel2, averageColor );
2686 samples += luxel2[ 3 ];
2688 VectorAdd( averageDir, deluxel2, averageDir );
2694 if ( samples <= 0.0f ) {
2698 /* dark lightmap seams */
2700 if ( lightmapNum == 0 ) {
2701 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2707 if ( filterColor ) {
2708 VectorDivide( averageColor, samples, luxel );
2712 VectorDivide( averageDir, samples, deluxel );
2715 /* set cluster to -3 */
2716 if ( *cluster < 0 ) {
2717 *cluster = CLUSTER_FLOODED;
2727 IlluminateVertexes()
2728 light the surface vertexes
2731 #define VERTEX_NUDGE 4.0f
2733 void IlluminateVertexes( int num ){
2734 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2735 int lightmapNum, numAvg;
2736 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2737 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2738 bspDrawSurface_t *ds;
2739 surfaceInfo_t *info;
2741 bspDrawVert_t *verts;
2743 float floodLightAmount;
2747 /* get surface, info, and raw lightmap */
2748 ds = &bspDrawSurfaces[ num ];
2749 info = &surfaceInfos[ num ];
2752 /* -----------------------------------------------------------------
2753 illuminate the vertexes
2754 ----------------------------------------------------------------- */
2756 /* calculate vertex lighting for surfaces without lightmaps */
2757 if ( lm == NULL || cpmaHack ) {
2759 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2760 trace.forceSunlight = info->si->forceSunlight;
2761 trace.recvShadows = info->recvShadows;
2762 trace.numSurfaces = 1;
2763 trace.surfaces = #
2764 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2766 /* twosided lighting */
2767 trace.twoSided = info->si->twoSided;
2769 /* make light list for this surface */
2770 CreateTraceLightsForSurface( num, &trace );
2773 verts = yDrawVerts + ds->firstVert;
2775 memset( avgColors, 0, sizeof( avgColors ) );
2777 /* walk the surface verts */
2778 for ( i = 0; i < ds->numVerts; i++ )
2780 /* get vertex luxel */
2781 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2783 /* color the luxel with raw lightmap num? */
2784 if ( debugSurfaces ) {
2785 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2788 /* color the luxel with luxel origin? */
2789 else if ( debugOrigin ) {
2790 VectorSubtract( info->maxs, info->mins, temp );
2791 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2792 VectorSubtract( origin, lm->mins, temp2 );
2793 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2794 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2795 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2798 /* color the luxel with the normal */
2799 else if ( normalmap ) {
2800 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2801 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2802 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2805 /* illuminate the vertex */
2808 /* clear vertex luxel */
2809 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2811 /* try at initial origin */
2812 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2813 if ( trace.cluster >= 0 ) {
2815 VectorCopy( verts[ i ].xyz, trace.origin );
2816 VectorCopy( verts[ i ].normal, trace.normal );
2819 if ( dirty && !bouncing ) {
2820 dirt = DirtForSample( &trace );
2826 /* jal: floodlight */
2827 floodLightAmount = 0.0f;
2828 VectorClear( floodColor );
2829 if ( floodlighty && !bouncing ) {
2830 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2831 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2835 LightingAtSample( &trace, ds->vertexStyles, colors );
2838 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2841 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2843 /* jal: floodlight */
2844 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2847 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2848 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2849 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2853 /* is this sample bright enough? */
2854 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2855 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2856 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2857 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2858 /* nudge the sample point around a bit */
2859 for ( x = 0; x < 5; x++ )
2861 /* two's complement 0, 1, -1, 2, -2, etc */
2862 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2864 for ( y = 0; y < 5; y++ )
2866 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2868 for ( z = 0; z < 5; z++ )
2870 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2873 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2874 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2875 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2877 /* try at nudged origin */
2878 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2879 if ( trace.cluster < 0 ) {
2884 if ( dirty && !bouncing ) {
2885 dirt = DirtForSample( &trace );
2891 /* jal: floodlight */
2892 floodLightAmount = 0.0f;
2893 VectorClear( floodColor );
2894 if ( floodlighty && !bouncing ) {
2895 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2896 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2900 LightingAtSample( &trace, ds->vertexStyles, colors );
2903 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2906 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2908 /* jal: floodlight */
2909 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2912 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2913 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2916 /* bright enough? */
2917 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2918 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2919 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2920 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2928 /* add to average? */
2929 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2930 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2931 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2932 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2934 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2936 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2937 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2942 /* another happy customer */
2943 numVertsIlluminated++;
2946 /* set average color */
2948 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2949 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2953 VectorCopy( ambientColor, avgColors[ 0 ] );
2956 /* clean up and store vertex color */
2957 for ( i = 0; i < ds->numVerts; i++ )
2959 /* get vertex luxel */
2960 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2962 /* store average in occluded vertexes */
2963 if ( radVertLuxel[ 0 ] < 0.0f ) {
2964 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2966 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2967 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2970 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2975 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2978 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2979 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2982 if ( bouncing || bounce == 0 || !bounceOnly ) {
2983 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2985 if ( !info->si->noVertexLight ) {
2986 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2991 /* free light list */
2992 FreeTraceLights( &trace );
2994 /* return to sender */
2998 /* -----------------------------------------------------------------
2999 reconstitute vertex lighting from the luxels
3000 ----------------------------------------------------------------- */
3002 /* set styles from lightmap */
3003 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3004 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3006 /* get max search radius */
3008 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3010 /* walk the surface verts */
3011 verts = yDrawVerts + ds->firstVert;
3012 for ( i = 0; i < ds->numVerts; i++ )
3014 /* do each lightmap */
3015 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3018 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3022 /* get luxel coords */
3023 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3024 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3028 else if ( x >= lm->sw ) {
3034 else if ( y >= lm->sh ) {
3038 /* get vertex luxels */
3039 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3040 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3042 /* color the luxel with the normal? */
3044 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3045 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3046 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3049 /* color the luxel with surface num? */
3050 else if ( debugSurfaces ) {
3051 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3054 /* divine color from the superluxels */
3057 /* increasing radius */
3058 VectorClear( radVertLuxel );
3060 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3062 /* sample within radius */
3063 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3065 if ( sy < 0 || sy >= lm->sh ) {
3069 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3071 if ( sx < 0 || sx >= lm->sw ) {
3075 /* get luxel particulars */
3076 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3077 cluster = SUPER_CLUSTER( sx, sy );
3078 if ( *cluster < 0 ) {
3082 /* testing: must be brigher than ambient color */
3083 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3086 /* add its distinctiveness to our own */
3087 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3088 samples += luxel[ 3 ];
3094 if ( samples > 0.0f ) {
3095 VectorDivide( radVertLuxel, samples, radVertLuxel );
3098 VectorCopy( ambientColor, radVertLuxel );
3102 /* store into floating point storage */
3103 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3104 numVertsIlluminated++;
3106 /* store into bytes (for vertex approximation) */
3107 if ( !info->si->noVertexLight ) {
3108 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3116 /* -------------------------------------------------------------------------------
3118 light optimization (-fast)
3120 creates a list of lights that will affect a surface and stores it in tw
3121 this is to optimize surface lighting by culling out as many of the
3122 lights in the world as possible from further calculation
3124 ------------------------------------------------------------------------------- */
3128 determines opaque brushes in the world and find sky shaders for sunlight calculations
3131 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3133 unsigned int compileFlags, allCompileFlags;
3136 bspBrushSide_t *side;
3137 bspShader_t *shader;
3142 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3145 if ( opaqueBrushes == NULL ) {
3146 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3150 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3151 numOpaqueBrushes = 0;
3153 /* walk the list of worldspawn brushes */
3154 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3157 b = bspModels[ 0 ].firstBSPBrush + i;
3158 brush = &bspBrushes[ b ];
3160 /* check all sides */
3163 allCompileFlags = ~( 0u );
3164 for ( j = 0; j < brush->numSides && inside; j++ )
3166 /* do bsp shader calculations */
3167 side = &bspBrushSides[ brush->firstSide + j ];
3168 shader = &bspShaders[ side->shaderNum ];
3170 /* get shader info */
3171 si = ShaderInfoForShaderNull( shader->shader );
3176 /* or together compile flags */
3177 compileFlags |= si->compileFlags;
3178 allCompileFlags &= si->compileFlags;
3181 /* determine if this brush is opaque to light */
3182 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3183 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3189 /* emit some statistics */
3190 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3192 void SetupBrushes( void ){
3193 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3200 determines if two clusters are visible to each other using the PVS
3203 qboolean ClusterVisible( int a, int b ){
3209 if ( a < 0 || b < 0 ) {
3219 if ( numBSPVisBytes <= 8 ) {
3224 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3225 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3226 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3229 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3239 borrowed from vlight.c
3242 int PointInLeafNum_r( vec3_t point, int nodenum ){
3249 while ( nodenum >= 0 )
3251 node = &bspNodes[ nodenum ];
3252 plane = &bspPlanes[ node->planeNum ];
3253 dist = DotProduct( point, plane->normal ) - plane->dist;
3255 nodenum = node->children[ 0 ];
3257 else if ( dist < -0.1 ) {
3258 nodenum = node->children[ 1 ];
3262 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3263 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3266 nodenum = node->children[ 1 ];
3270 leafnum = -nodenum - 1;
3278 borrowed from vlight.c
3281 int PointInLeafNum( vec3_t point ){
3282 return PointInLeafNum_r( point, 0 );
3288 ClusterVisibleToPoint() - ydnar
3289 returns qtrue if point can "see" cluster
3292 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3296 /* get leafNum for point */
3297 pointCluster = ClusterForPoint( point );
3298 if ( pointCluster < 0 ) {
3303 return ClusterVisible( pointCluster, cluster );
3309 ClusterForPoint() - ydnar
3310 returns the pvs cluster for point
3313 int ClusterForPoint( vec3_t point ){
3317 /* get leafNum for point */
3318 leafNum = PointInLeafNum( point );
3319 if ( leafNum < 0 ) {
3323 /* return the cluster */
3324 return bspLeafs[ leafNum ].cluster;
3330 ClusterForPointExt() - ydnar
3331 also takes brushes into account for occlusion testing
3334 int ClusterForPointExt( vec3_t point, float epsilon ){
3335 int i, j, b, leafNum, cluster;
3338 int *brushes, numBSPBrushes;
3344 /* get leaf for point */
3345 leafNum = PointInLeafNum( point );
3346 if ( leafNum < 0 ) {
3349 leaf = &bspLeafs[ leafNum ];
3351 /* get the cluster */
3352 cluster = leaf->cluster;
3353 if ( cluster < 0 ) {
3357 /* transparent leaf, so check point against all brushes in the leaf */
3358 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3359 numBSPBrushes = leaf->numBSPLeafBrushes;
3360 for ( i = 0; i < numBSPBrushes; i++ )
3364 if ( b > maxOpaqueBrush ) {
3367 brush = &bspBrushes[ b ];
3368 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3372 /* check point against all planes */
3374 for ( j = 0; j < brush->numSides && inside; j++ )
3376 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3377 dot = DotProduct( point, plane->normal );
3379 if ( dot > epsilon ) {
3384 /* if inside, return bogus cluster */
3390 /* if the point made it this far, it's not inside any opaque brushes */
3397 ClusterForPointExtFilter() - ydnar
3398 adds cluster checking against a list of known valid clusters
3401 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3405 /* get cluster for point */
3406 cluster = ClusterForPointExt( point, epsilon );
3408 /* check if filtering is necessary */
3409 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3414 for ( i = 0; i < numClusters; i++ )
3416 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3428 ShaderForPointInLeaf() - ydnar
3429 checks a point against all brushes in a leaf, returning the shader of the brush
3430 also sets the cumulative surface and content flags for the brush hit
3433 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3437 int *brushes, numBSPBrushes;
3440 bspBrushSide_t *side;
3442 bspShader_t *shader;
3443 int allSurfaceFlags, allContentFlags;
3446 /* clear things out first */
3451 if ( leafNum < 0 ) {
3454 leaf = &bspLeafs[ leafNum ];
3456 /* transparent leaf, so check point against all brushes in the leaf */
3457 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3458 numBSPBrushes = leaf->numBSPLeafBrushes;
3459 for ( i = 0; i < numBSPBrushes; i++ )
3462 brush = &bspBrushes[ brushes[ i ] ];
3464 /* check point against all planes */
3466 allSurfaceFlags = 0;
3467 allContentFlags = 0;
3468 for ( j = 0; j < brush->numSides && inside; j++ )
3470 side = &bspBrushSides[ brush->firstSide + j ];
3471 plane = &bspPlanes[ side->planeNum ];
3472 dot = DotProduct( point, plane->normal );
3474 if ( dot > epsilon ) {
3479 shader = &bspShaders[ side->shaderNum ];
3480 allSurfaceFlags |= shader->surfaceFlags;
3481 allContentFlags |= shader->contentFlags;
3485 /* handle if inside */
3487 /* if there are desired flags, check for same and continue if they aren't matched */
3488 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3491 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3495 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3496 *surfaceFlags = allSurfaceFlags;
3497 *contentFlags = allContentFlags;
3498 return brush->shaderNum;
3502 /* if the point made it this far, it's not inside any brushes */
3510 chops a bounding box by the plane defined by origin and normal
3511 returns qfalse if the bounds is entirely clipped away
3513 this is not exactly the fastest way to do this...
3516 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3517 /* FIXME: rewrite this so it doesn't use bloody brushes */
3525 calculates each light's effective envelope,
3526 taking into account brightness, type, and pvs.
3529 #define LIGHT_EPSILON 0.125f
3530 #define LIGHT_NUDGE 2.0f
3532 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3533 int i, x, y, z, x1, y1, z1;
3534 light_t *light, *light2, **owner;
3536 vec3_t origin, dir, mins, maxs;
3537 float radius, intensity;
3538 light_t *buckets[ 256 ];
3541 /* early out for weird cases where there are no lights */
3542 if ( lights == NULL ) {
3547 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3551 numCulledLights = 0;
3553 while ( *owner != NULL )
3558 /* handle negative lights */
3559 if ( light->photons < 0.0f || light->add < 0.0f ) {
3560 light->photons *= -1.0f;
3561 light->add *= -1.0f;
3562 light->flags |= LIGHT_NEGATIVE;
3566 if ( light->type == EMIT_SUN ) {
3569 light->envelope = MAX_WORLD_COORD * 8.0f;
3570 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3571 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3574 /* everything else */
3577 /* get pvs cluster for light */
3578 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3580 /* invalid cluster? */
3581 if ( light->cluster < 0 ) {
3582 /* nudge the sample point around a bit */
3583 for ( x = 0; x < 4; x++ )
3585 /* two's complement 0, 1, -1, 2, -2, etc */
3586 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3588 for ( y = 0; y < 4; y++ )
3590 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3592 for ( z = 0; z < 4; z++ )
3594 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3597 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3598 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3599 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3601 /* try at nudged origin */
3602 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3603 if ( light->cluster < 0 ) {
3608 VectorCopy( origin, light->origin );
3614 /* only calculate for lights in pvs and outside of opaque brushes */
3615 if ( light->cluster >= 0 ) {
3616 /* set light fast flag */
3618 light->flags |= LIGHT_FAST_TEMP;
3621 light->flags &= ~LIGHT_FAST_TEMP;
3623 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3624 light->flags |= LIGHT_FAST_TEMP;
3626 if ( light->si && light->si->noFast ) {
3627 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3630 /* clear light envelope */
3631 light->envelope = 0;
3633 /* handle area lights */
3634 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3635 light->envelope = MAX_WORLD_COORD * 8.0f;
3637 /* check for fast mode */
3638 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3639 /* ugly hack to calculate extent for area lights, but only done once */
3640 VectorScale( light->normal, -1.0f, dir );
3641 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3645 VectorMA( light->origin, radius, light->normal, origin );
3646 factor = PointToPolygonFormFactor( origin, dir, light->w );
3647 if ( factor < 0.0f ) {
3650 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3651 light->envelope = radius;
3657 intensity = light->photons; /* hopefully not used */
3662 intensity = light->photons;
3666 if ( light->envelope <= 0.0f ) {
3667 /* solve distance for non-distance lights */
3668 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3669 light->envelope = MAX_WORLD_COORD * 8.0f;
3672 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3673 /* solve distance for linear lights */
3674 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3675 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3679 add = angle * light->photons * linearScale - (dist * light->fade);
3680 T = (light->photons * linearScale) - (dist * light->fade);
3681 T + (dist * light->fade) = (light->photons * linearScale);
3682 dist * light->fade = (light->photons * linearScale) - T;
3683 dist = ((light->photons * linearScale) - T) / light->fade;
3686 /* solve for inverse square falloff */
3688 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3692 add = light->photons / (dist * dist);
3693 T = light->photons / (dist * dist);
3694 T * (dist * dist) = light->photons;
3695 dist = sqrt( light->photons / T );
3700 /* solve distance for linear lights */
3701 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3702 light->envelope = ( intensity * linearScale ) / light->fade;
3705 /* can't cull these */
3707 light->envelope = MAX_WORLD_COORD * 8.0f;
3712 /* chop radius against pvs */
3715 ClearBounds( mins, maxs );
3717 /* check all leaves */
3718 for ( i = 0; i < numBSPLeafs; i++ )
3721 leaf = &bspLeafs[ i ];
3724 if ( leaf->cluster < 0 ) {
3727 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3731 /* add this leafs bbox to the bounds */
3732 VectorCopy( leaf->mins, origin );
3733 AddPointToBounds( origin, mins, maxs );
3734 VectorCopy( leaf->maxs, origin );
3735 AddPointToBounds( origin, mins, maxs );
3738 /* test to see if bounds encompass light */
3739 for ( i = 0; i < 3; i++ )
3741 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3742 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3743 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3744 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3745 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3746 AddPointToBounds( light->origin, mins, maxs );
3750 /* chop the bounds by a plane for area lights and spotlights */
3751 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3752 ChopBounds( mins, maxs, light->origin, light->normal );
3756 VectorCopy( mins, light->mins );
3757 VectorCopy( maxs, light->maxs );
3759 /* reflect bounds around light origin */
3760 //% VectorMA( light->origin, -1.0f, origin, origin );
3761 VectorScale( light->origin, 2, origin );
3762 VectorSubtract( origin, maxs, origin );
3763 AddPointToBounds( origin, mins, maxs );
3764 //% VectorMA( light->origin, -1.0f, mins, origin );
3765 VectorScale( light->origin, 2, origin );
3766 VectorSubtract( origin, mins, origin );
3767 AddPointToBounds( origin, mins, maxs );
3769 /* calculate spherical bounds */
3770 VectorSubtract( maxs, light->origin, dir );
3771 radius = (float) VectorLength( dir );
3773 /* if this radius is smaller than the envelope, then set the envelope to it */
3774 if ( radius < light->envelope ) {
3775 light->envelope = radius;
3776 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3779 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3782 /* add grid/surface only check */
3784 if ( !( light->flags & LIGHT_GRID ) ) {
3785 light->envelope = 0.0f;
3790 if ( !( light->flags & LIGHT_SURFACES ) ) {
3791 light->envelope = 0.0f;
3797 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3799 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3801 /* delete the light */
3803 *owner = light->next;
3804 if ( light->w != NULL ) {
3812 /* square envelope */
3813 light->envelope2 = ( light->envelope * light->envelope );
3815 /* increment light count */
3818 /* set next light */
3819 owner = &( ( **owner ).next );
3822 /* bucket sort lights by style */
3823 memset( buckets, 0, sizeof( buckets ) );
3825 for ( light = lights; light != NULL; light = light2 )
3827 /* get next light */
3828 light2 = light->next;
3830 /* filter into correct bucket */
3831 light->next = buckets[ light->style ];
3832 buckets[ light->style ] = light;
3834 /* if any styled light is present, automatically set nocollapse */
3835 if ( light->style != LS_NORMAL ) {
3840 /* filter back into light list */
3842 for ( i = 255; i >= 0; i-- )
3845 for ( light = buckets[ i ]; light != NULL; light = light2 )
3847 light2 = light->next;
3848 light->next = lights;
3853 /* emit some statistics */
3854 Sys_Printf( "%9d total lights\n", numLights );
3855 Sys_Printf( "%9d culled lights\n", numCulledLights );
3861 CreateTraceLightsForBounds()
3862 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3865 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3868 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3869 float radius, dist, length;
3872 /* potential pre-setup */
3873 if ( numLights == 0 ) {
3874 SetupEnvelopes( qfalse, fast );
3878 //% 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 ] );
3880 /* allocate the light list */
3881 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3882 trace->numLights = 0;
3884 /* calculate spherical bounds */
3885 VectorAdd( mins, maxs, origin );
3886 VectorScale( origin, 0.5f, origin );
3887 VectorSubtract( maxs, origin, dir );
3888 radius = (float) VectorLength( dir );
3890 /* get length of normal vector */
3891 if ( normal != NULL ) {
3892 length = VectorLength( normal );
3896 normal = nullVector;
3900 /* test each light and see if it reaches the sphere */
3901 /* note: the attenuation code MUST match LightingAtSample() */
3902 for ( light = lights; light; light = light->next )
3904 /* check zero sized envelope */
3905 if ( light->envelope <= 0 ) {
3906 lightsEnvelopeCulled++;
3911 if ( !( light->flags & flags ) ) {
3915 /* sunlight skips all this nonsense */
3916 if ( light->type != EMIT_SUN ) {
3922 /* check against pvs cluster */
3923 if ( numClusters > 0 && clusters != NULL ) {
3924 for ( i = 0; i < numClusters; i++ )
3926 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3932 if ( i == numClusters ) {
3933 lightsClusterCulled++;
3938 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3939 VectorSubtract( light->origin, origin, dir );
3940 dist = VectorLength( dir );
3941 dist -= light->envelope;
3944 lightsEnvelopeCulled++;
3948 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3951 for ( i = 0; i < 3; i++ )
3953 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3958 lightsBoundsCulled++;
3964 /* planar surfaces (except twosided surfaces) have a couple more checks */
3965 if ( length > 0.0f && trace->twoSided == qfalse ) {
3966 /* lights coplanar with a surface won't light it */
3967 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
3968 lightsPlaneCulled++;
3972 /* check to see if light is behind the plane */
3973 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
3974 lightsPlaneCulled++;
3979 /* add this light */
3980 trace->lights[ trace->numLights++ ] = light;
3983 /* make last night null */
3984 trace->lights[ trace->numLights ] = NULL;
3989 void FreeTraceLights( trace_t *trace ){
3990 if ( trace->lights != NULL ) {
3991 free( trace->lights );
3998 CreateTraceLightsForSurface()
3999 creates a list of lights that can potentially affect a drawsurface
4002 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4004 vec3_t mins, maxs, normal;
4006 bspDrawSurface_t *ds;
4007 surfaceInfo_t *info;
4015 /* get drawsurface and info */
4016 ds = &bspDrawSurfaces[ num ];
4017 info = &surfaceInfos[ num ];
4019 /* get the mins/maxs for the dsurf */
4020 ClearBounds( mins, maxs );
4021 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4022 for ( i = 0; i < ds->numVerts; i++ )
4024 dv = &yDrawVerts[ ds->firstVert + i ];
4025 AddPointToBounds( dv->xyz, mins, maxs );
4026 if ( !VectorCompare( dv->normal, normal ) ) {
4027 VectorClear( normal );
4031 /* create the lights for the bounding box */
4032 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4035 /////////////////////////////////////////////////////////////
4037 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4038 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4039 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4040 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4042 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4043 static int numFloodVectors = 0;
4045 void SetupFloodLight( void ){
4047 float angle, elevation, angleStep, elevationStep;
4049 double v1,v2,v3,v4,v5,v6;
4052 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4054 /* calculate angular steps */
4055 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4056 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4060 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4062 /* iterate elevation */
4063 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4065 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4066 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4067 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4072 /* emit some statistics */
4073 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4076 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4078 if ( value[ 0 ] != '\0' ) {
4080 v4 = floodlightDistance;
4081 v5 = floodlightIntensity;
4082 v6 = floodlightDirectionScale;
4084 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4086 floodlightRGB[0] = v1;
4087 floodlightRGB[1] = v2;
4088 floodlightRGB[2] = v3;
4090 if ( VectorLength( floodlightRGB ) == 0 ) {
4091 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4104 floodlightDistance = v4;
4105 floodlightIntensity = v5;
4106 floodlightDirectionScale = v6;
4108 floodlighty = qtrue;
4109 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4113 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4116 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4117 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4118 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4120 ColorNormalize( floodlightRGB,floodlightRGB );
4124 FloodLightForSample()
4125 calculates floodlight value for a given sample
4126 once again, kudos to the dirtmapping coder
4129 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4134 float gatherLight, outLight;
4135 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4143 if ( trace == NULL || trace->cluster < 0 ) {
4149 dd = floodLightDistance;
4150 VectorCopy( trace->normal, normal );
4152 /* check if the normal is aligned to the world-up */
4153 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4154 if ( normal[ 2 ] == 1.0f ) {
4155 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4156 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4158 else if ( normal[ 2 ] == -1.0f ) {
4159 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4160 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4165 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4166 CrossProduct( normal, worldUp, myRt );
4167 VectorNormalize( myRt, myRt );
4168 CrossProduct( myRt, normal, myUp );
4169 VectorNormalize( myUp, myUp );
4172 /* vortex: optimise floodLightLowQuality a bit */
4173 if ( floodLightLowQuality == qtrue ) {
4174 /* iterate through ordered vectors */
4175 for ( i = 0; i < numFloodVectors; i++ )
4176 if ( rand() % 10 != 0 ) {
4182 /* iterate through ordered vectors */
4183 for ( i = 0; i < numFloodVectors; i++ )
4187 /* transform vector into tangent space */
4188 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4189 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4190 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4193 VectorMA( trace->origin, dd, direction, trace->end );
4195 //VectorMA( trace->origin, 1, direction, trace->origin );
4197 SetupTrace( trace );
4198 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4203 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4204 contribution = 1.0f;
4206 else if ( trace->opaque ) {
4207 VectorSubtract( trace->hit, trace->origin, displacement );
4208 d = VectorLength( displacement );
4210 // d=trace->distance;
4211 //if (d>256) gatherDirt+=1;
4212 contribution = d / dd;
4213 if ( contribution > 1 ) {
4214 contribution = 1.0f;
4217 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4220 gatherLight += contribution;
4225 if ( gatherLight <= 0.0f ) {
4234 gatherLight /= ( sub );
4236 outLight = gatherLight;
4237 if ( outLight > 1.0f ) {
4241 /* return to sender */
4246 FloodLightRawLightmap
4247 lighttracer style ambient occlusion light hack.
4248 Kudos to the dirtmapping author for most of this source.
4249 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4250 VorteX: fixed problems with deluxemapping
4253 // floodlight pass on a lightmap
4254 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4255 int i, x, y, *cluster;
4256 float *origin, *normal, *floodlight, floodLightAmount;
4257 surfaceInfo_t *info;
4260 // float samples, average, *floodlight2;
4262 memset( &trace,0,sizeof( trace_t ) );
4265 trace.testOcclusion = qtrue;
4266 trace.forceSunlight = qfalse;
4267 trace.twoSided = qtrue;
4268 trace.recvShadows = lm->recvShadows;
4269 trace.numSurfaces = lm->numLightSurfaces;
4270 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4271 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4272 trace.testAll = qfalse;
4273 trace.distance = 1024;
4275 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4276 //trace.twoSided = qfalse;
4277 for ( i = 0; i < trace.numSurfaces; i++ )
4280 info = &surfaceInfos[ trace.surfaces[ i ] ];
4282 /* check twosidedness */
4283 if ( info->si->twoSided ) {
4284 trace.twoSided = qtrue;
4289 /* gather floodlight */
4290 for ( y = 0; y < lm->sh; y++ )
4292 for ( x = 0; x < lm->sw; x++ )
4295 cluster = SUPER_CLUSTER( x, y );
4296 origin = SUPER_ORIGIN( x, y );
4297 normal = SUPER_NORMAL( x, y );
4298 floodlight = SUPER_FLOODLIGHT( x, y );
4300 /* set default dirt */
4303 /* only look at mapped luxels */
4304 if ( *cluster < 0 ) {
4309 trace.cluster = *cluster;
4310 VectorCopy( origin, trace.origin );
4311 VectorCopy( normal, trace.normal );
4313 /* get floodlight */
4314 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4316 /* add floodlight */
4317 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4318 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4319 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4320 floodlight[3] += floodlightDirectionScale;
4324 /* testing no filtering */
4330 for ( y = 0; y < lm->sh; y++ )
4332 for ( x = 0; x < lm->sw; x++ )
4335 cluster = SUPER_CLUSTER( x, y );
4336 floodlight = SUPER_FLOODLIGHT( x, y );
4338 /* filter dirt by adjacency to unmapped luxels */
4339 average = *floodlight;
4341 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4343 if ( sy < 0 || sy >= lm->sh ) {
4347 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4349 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4353 /* get neighboring luxel */
4354 cluster = SUPER_CLUSTER( sx, sy );
4355 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4356 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4361 average += *floodlight2;
4366 if ( samples <= 0.0f ) {
4372 if ( samples <= 0.0f ) {
4377 *floodlight = average / samples;
4383 void FloodLightRawLightmap( int rawLightmapNum ){
4386 /* bail if this number exceeds the number of raw lightmaps */
4387 if ( rawLightmapNum >= numRawLightmaps ) {
4391 lm = &rawLightmaps[ rawLightmapNum ];
4394 if ( floodlighty && floodlightIntensity ) {
4395 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4399 if ( lm->floodlightIntensity ) {
4400 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4401 numSurfacesFloodlighten += 1;
4405 void FloodlightRawLightmaps(){
4406 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4407 numSurfacesFloodlighten = 0;
4408 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4409 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4413 FloodLightIlluminate()
4414 illuminate floodlight into lightmap luxels
4417 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4418 float *luxel, *floodlight, *deluxel, *normal;
4421 int x, y, lightmapNum;
4423 /* walk lightmaps */
4424 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4427 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4431 /* apply floodlight to each luxel */
4432 for ( y = 0; y < lm->sh; y++ )
4434 for ( x = 0; x < lm->sw; x++ )
4436 /* get floodlight */
4437 floodlight = SUPER_FLOODLIGHT( x, y );
4438 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4443 cluster = SUPER_CLUSTER( x, y );
4445 /* only process mapped luxels */
4446 if ( *cluster < 0 ) {
4450 /* get particulars */
4451 luxel = SUPER_LUXEL( lightmapNum, x, y );
4452 deluxel = SUPER_DELUXEL( x, y );
4454 /* add to lightmap */
4455 luxel[0] += floodlight[0];
4456 luxel[1] += floodlight[1];
4457 luxel[2] += floodlight[2];
4459 if ( luxel[3] == 0 ) {
4463 /* add to deluxemap */
4464 if ( deluxemap && floodlight[3] > 0 ) {
4467 normal = SUPER_NORMAL( x, y );
4468 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4470 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4471 if ( brightness < 0.00390625f ) {
4472 brightness = 0.00390625f;
4475 VectorScale( normal, brightness, lightvector );
4476 VectorAdd( deluxel, lightvector, deluxel );