1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale ){
54 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
55 if ( scale <= 0.0f ) {
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for ( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if ( sample[ i ] < 0.0f ) {
73 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
76 if ( lightmapExposure == 0 ) {
77 /* clamp with color normalization */
79 if ( sample[ 1 ] > max ) {
82 if ( sample[ 2 ] > max ) {
86 VectorScale( sample, ( 255.0f / max ), sample );
91 inv = 1.f / lightmapExposure;
95 if ( sample[ 1 ] > max ) {
98 if ( sample[ 2 ] > max ) {
102 dif = ( 1 - exp( -max * inv ) ) * 255;
112 for ( i = 0; i < 3; i++ )
119 /* compensate for ingame overbrighting/bitshifting */
120 VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );
123 if ( lightmapsRGB ) {
124 sample[0] = floor( Image_sRGBFloatFromLinearFloat( sample[0] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
125 sample[1] = floor( Image_sRGBFloatFromLinearFloat( sample[1] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
126 sample[2] = floor( Image_sRGBFloatFromLinearFloat( sample[2] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
130 colorBytes[ 0 ] = sample[ 0 ];
131 colorBytes[ 1 ] = sample[ 1 ];
132 colorBytes[ 2 ] = sample[ 2 ];
137 /* -------------------------------------------------------------------------------
139 this section deals with phong shading (normal interpolation across brush faces)
141 ------------------------------------------------------------------------------- */
145 smooths together coincident vertex normals across the bsp
148 #define MAX_SAMPLES 256
149 #define THETA_EPSILON 0.000001
150 #define EQUAL_NORMAL_EPSILON 0.01
152 void SmoothNormals( void ){
153 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
154 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
155 bspDrawSurface_t *ds;
159 vec3_t average, diff;
160 int indexes[ MAX_SAMPLES ];
161 vec3_t votes[ MAX_SAMPLES ];
164 /* allocate shade angle table */
165 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
166 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
168 /* allocate smoothed table */
169 cs = ( numBSPDrawVerts / 8 ) + 1;
170 smoothed = safe_malloc( cs );
171 memset( smoothed, 0, cs );
173 /* set default shade angle */
174 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
177 /* run through every surface and flag verts belonging to non-lightmapped surfaces
178 and set per-vertex smoothing angle */
179 for ( i = 0; i < numBSPDrawSurfaces; i++ )
182 ds = &bspDrawSurfaces[ i ];
184 /* get shader for shade angle */
185 si = surfaceInfos[ i ].si;
186 if ( si->shadeAngleDegrees ) {
187 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
190 shadeAngle = defaultShadeAngle;
192 if ( shadeAngle > maxShadeAngle ) {
193 maxShadeAngle = shadeAngle;
197 for ( j = 0; j < ds->numVerts; j++ )
199 f = ds->firstVert + j;
200 shadeAngles[ f ] = shadeAngle;
201 if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
202 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
206 /* ydnar: optional force-to-trisoup */
207 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
208 ds->surfaceType = MST_TRIANGLE_SOUP;
209 ds->lightmapNum[ 0 ] = -3;
213 /* bail if no surfaces have a shade angle */
214 if ( maxShadeAngle == 0 ) {
222 start = I_FloatTime();
224 /* go through the list of vertexes */
225 for ( i = 0; i < numBSPDrawVerts; i++ )
228 f = 10 * i / numBSPDrawVerts;
231 Sys_Printf( "%i...", f );
234 /* already smoothed? */
235 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
240 VectorClear( average );
244 /* build a table of coincident vertexes */
245 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
247 /* already smoothed? */
248 if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
253 if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
257 /* use smallest shade angle */
258 shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
260 /* check shade angle */
261 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
265 else if ( dot < -1.0 ) {
268 testAngle = acos( dot ) + THETA_EPSILON;
269 if ( testAngle >= shadeAngle ) {
270 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
273 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
275 /* add to the list */
276 indexes[ numVerts++ ] = j;
279 smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
281 /* see if this normal has already been voted */
282 for ( k = 0; k < numVotes; k++ )
284 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
285 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
286 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
287 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
292 /* add a new vote? */
293 if ( k == numVotes && numVotes < MAX_SAMPLES ) {
294 VectorAdd( average, bspDrawVerts[ j ].normal, average );
295 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
300 /* don't average for less than 2 verts */
301 if ( numVerts < 2 ) {
306 if ( VectorNormalize( average, average ) > 0 ) {
308 for ( j = 0; j < numVerts; j++ )
309 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
313 /* free the tables */
318 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
323 /* -------------------------------------------------------------------------------
325 this section deals with phong shaded lightmap tracing
327 ------------------------------------------------------------------------------- */
329 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
333 calculates the st tangent vectors for normalmapping
336 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
342 /* calculate barycentric basis for the triangle */
343 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 ] );
344 if ( fabs( bb ) < 0.00000001f ) {
349 for ( i = 0; i < numVerts; i++ )
351 /* calculate s tangent vector */
352 s = dv[ i ]->st[ 0 ] + 10.0f;
353 t = dv[ i ]->st[ 1 ];
354 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
355 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
356 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
358 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
359 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
360 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
362 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
363 VectorNormalize( stv[ i ], stv[ i ] );
365 /* calculate t tangent vector */
366 s = dv[ i ]->st[ 0 ];
367 t = dv[ i ]->st[ 1 ] + 10.0f;
368 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
369 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
370 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
372 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
373 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
374 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
376 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
377 VectorNormalize( ttv[ i ], ttv[ i ] );
380 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
381 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
384 /* return to caller */
393 perterbs the normal by the shader's normalmap in tangent space
396 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
402 VectorCopy( dv->normal, pNormal );
404 /* sample normalmap */
405 if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
409 /* remap sampled normal from [0,255] to [-1,-1] */
410 for ( i = 0; i < 3; i++ )
411 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
413 /* scale tangent vectors and add to original normal */
414 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
415 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
416 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
418 /* renormalize and return */
419 VectorNormalize( pNormal, pNormal );
426 maps a luxel for triangle bv at
430 #define BOGUS_NUDGE -99999.0f
432 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] ){
433 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
434 float *luxel, *origin, *normal, d, lightmapSampleOffset;
441 vec4_t sideplane, hostplane;
446 static float nudges[][ 2 ] =
448 //%{ 0, 0 }, /* try center first */
449 { -NUDGE, 0 }, /* left */
450 { NUDGE, 0 }, /* right */
451 { 0, NUDGE }, /* up */
452 { 0, -NUDGE }, /* down */
453 { -NUDGE, NUDGE }, /* left/up */
454 { NUDGE, -NUDGE }, /* right/down */
455 { NUDGE, NUDGE }, /* right/up */
456 { -NUDGE, -NUDGE }, /* left/down */
457 { BOGUS_NUDGE, BOGUS_NUDGE }
461 /* find luxel xy coords (fixme: subtract 0.5?) */
462 x = dv->lightmap[ 0 ][ 0 ];
463 y = dv->lightmap[ 0 ][ 1 ];
467 else if ( x >= lm->sw ) {
473 else if ( y >= lm->sh ) {
477 /* set shader and cluster list */
478 if ( info != NULL ) {
480 numClusters = info->numSurfaceClusters;
481 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
490 /* get luxel, origin, cluster, and normal */
491 luxel = SUPER_LUXEL( 0, x, y );
492 origin = SUPER_ORIGIN( x, y );
493 normal = SUPER_NORMAL( x, y );
494 cluster = SUPER_CLUSTER( x, y );
496 /* don't attempt to remap occluded luxels for planar surfaces */
497 if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
501 /* only average the normal for premapped luxels */
502 else if ( ( *cluster ) >= 0 ) {
503 /* do bumpmap calculations */
505 PerturbNormal( dv, si, pNormal, stv, ttv );
508 VectorCopy( dv->normal, pNormal );
511 /* add the additional normal data */
512 VectorAdd( normal, pNormal, normal );
517 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
521 /* axial lightmap projection */
522 if ( lm->vecs != NULL ) {
523 /* calculate an origin for the sample from the lightmap vectors */
524 VectorCopy( lm->origin, origin );
525 for ( i = 0; i < 3; i++ )
527 /* add unless it's the axis, which is taken care of later */
528 if ( i == lm->axisNum ) {
531 origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
534 /* project the origin onto the plane */
535 d = DotProduct( origin, plane ) - plane[ 3 ];
536 d /= plane[ lm->axisNum ];
537 origin[ lm->axisNum ] -= d;
540 /* non axial lightmap projection (explicit xyz) */
542 VectorCopy( dv->xyz, origin );
545 //////////////////////
546 //27's test to make sure samples stay within the triangle boundaries
547 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
548 //2) if it does, nudge it onto the correct side.
550 if ( worldverts != NULL && lightmapTriangleCheck ) {
551 for ( j = 0; j < 3; j++ )
553 VectorCopy( worldverts[j],cverts[j] );
555 PlaneFromPoints( hostplane,cverts[0],cverts[1],cverts[2] );
557 for ( j = 0; j < 3; j++ )
559 for ( i = 0; i < 3; i++ )
561 //build plane using 2 edges and a normal
562 next = ( i + 1 ) % 3;
564 VectorCopy( cverts[next],temp );
565 VectorAdd( temp,hostplane,temp );
566 PlaneFromPoints( sideplane,cverts[i],cverts[ next ], temp );
568 //planetest sample point
569 e = DotProduct( origin,sideplane );
570 e = e - sideplane[3];
573 //VectorClear(origin);
574 //Move the sample point back inside triangle bounds
575 origin[0] -= sideplane[0] * ( e + 1 );
576 origin[1] -= sideplane[1] * ( e + 1 );
577 origin[2] -= sideplane[2] * ( e + 1 );
583 ////////////////////////
585 /* planar surfaces have precalculated lightmap vectors for nudging */
586 if ( lm->plane != NULL ) {
587 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
588 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
589 VectorCopy( lm->plane, vecs[ 2 ] );
592 /* non-planar surfaces must calculate them */
595 if ( plane != NULL ) {
596 VectorCopy( plane, vecs[ 2 ] );
599 VectorCopy( dv->normal, vecs[ 2 ] );
601 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
604 /* push the origin off the surface a bit */
606 lightmapSampleOffset = si->lightmapSampleOffset;
609 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
611 if ( lm->axisNum < 0 ) {
612 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
614 else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
615 origin[ lm->axisNum ] -= lightmapSampleOffset;
618 origin[ lm->axisNum ] += lightmapSampleOffset;
621 VectorCopy( origin,origintwo );
622 if ( lightmapExtraVisClusterNudge ) {
623 origintwo[0] += vecs[2][0];
624 origintwo[1] += vecs[2][1];
625 origintwo[2] += vecs[2][2];
629 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
631 /* another retarded hack, storing nudge count in luxel[ 1 ] */
634 /* point in solid? (except in dark mode) */
635 if ( pointCluster < 0 && dark == qfalse ) {
636 /* nudge the the location around */
638 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
640 /* nudge the vector around a bit */
641 for ( i = 0; i < 3; i++ )
643 /* set nudged point*/
644 nudged[ i ] = origintwo[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
648 /* get pvs cluster */
649 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
650 if ( pointCluster >= 0 ) {
651 VectorCopy( nudged, origin );
657 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
658 if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
659 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
660 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
661 if ( pointCluster >= 0 ) {
662 VectorCopy( nudged, origin );
668 if ( pointCluster < 0 ) {
669 ( *cluster ) = CLUSTER_OCCLUDED;
670 VectorClear( origin );
671 VectorClear( normal );
677 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
679 /* do bumpmap calculations */
681 PerturbNormal( dv, si, pNormal, stv, ttv );
684 VectorCopy( dv->normal, pNormal );
687 /* store the cluster and normal */
688 ( *cluster ) = pointCluster;
689 VectorCopy( pNormal, normal );
691 /* store explicit mapping pass and implicit mapping pass */
706 recursively subdivides a triangle until its edges are shorter
707 than the distance between two luxels (thanks jc :)
710 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 ] ){
711 bspDrawVert_t mid, *dv2[ 3 ];
715 /* map the vertexes */
717 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
718 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
719 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
725 float *a, *b, dx, dy, dist, maxDist;
728 /* find the longest edge and split it */
731 for ( i = 0; i < 3; i++ )
734 a = dv[ i ]->lightmap[ 0 ];
735 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
738 dx = a[ 0 ] - b[ 0 ];
739 dy = a[ 1 ] - b[ 1 ];
740 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
743 if ( dist > maxDist ) {
749 /* try to early out */
750 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
755 /* split the longest edge and map it */
756 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
757 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
759 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
760 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
762 /* recurse to first triangle */
763 VectorCopy( dv, dv2 );
765 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
767 /* recurse to second triangle */
768 VectorCopy( dv, dv2 );
769 dv2[ ( max + 1 ) % 3 ] = ∣
770 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
777 seed function for MapTriangle_r()
778 requires a cw ordered triangle
781 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
784 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
785 vec3_t worldverts[ 3 ];
788 /* get plane if possible */
789 if ( lm->plane != NULL ) {
790 VectorCopy( lm->plane, plane );
791 plane[ 3 ] = lm->plane[ 3 ];
794 /* otherwise make one from the points */
795 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
799 /* check to see if we need to calculate texture->world tangent vectors */
800 if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
810 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
811 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
812 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
814 /* map the vertexes */
815 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
816 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
817 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
819 /* 2002-11-20: prefer axial triangle edges */
821 /* subdivide the triangle */
822 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
826 for ( i = 0; i < 3; i++ )
829 bspDrawVert_t *dv2[ 3 ];
833 a = dv[ i ]->lightmap[ 0 ];
834 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
836 /* make degenerate triangles for mapping edges */
837 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
839 dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
840 dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
842 /* map the degenerate triangle */
843 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
854 recursively subdivides a quad until its edges are shorter
855 than the distance between two luxels
858 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] ){
859 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
866 float *a, *b, dx, dy, dist, maxDist;
869 /* find the longest edge and split it */
872 for ( i = 0; i < 4; i++ )
875 a = dv[ i ]->lightmap[ 0 ];
876 b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
879 dx = a[ 0 ] - b[ 0 ];
880 dy = a[ 1 ] - b[ 1 ];
881 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
884 if ( dist > maxDist ) {
890 /* try to early out */
891 if ( max < 0 || maxDist <= subdivideThreshold ) {
896 /* we only care about even/odd edges */
899 /* split the longest edges */
900 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
901 LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
903 /* map the vertexes */
904 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
905 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
909 /* recurse to first quad */
911 dv2[ 1 ] = &mid[ 0 ];
912 dv2[ 2 ] = &mid[ 1 ];
914 MapQuad_r( lm, info, dv2, plane, stv, ttv );
916 /* recurse to second quad */
917 dv2[ 0 ] = &mid[ 0 ];
920 dv2[ 3 ] = &mid[ 1 ];
921 MapQuad_r( lm, info, dv2, plane, stv, ttv );
927 /* recurse to first quad */
930 dv2[ 2 ] = &mid[ 0 ];
931 dv2[ 3 ] = &mid[ 1 ];
932 MapQuad_r( lm, info, dv2, plane, stv, ttv );
934 /* recurse to second quad */
935 dv2[ 0 ] = &mid[ 1 ];
936 dv2[ 1 ] = &mid[ 0 ];
939 MapQuad_r( lm, info, dv2, plane, stv, ttv );
947 seed function for MapQuad_r()
948 requires a cw ordered triangle quad
951 #define QUAD_PLANAR_EPSILON 0.5f
953 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
956 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
959 /* get plane if possible */
960 if ( lm->plane != NULL ) {
961 VectorCopy( lm->plane, plane );
962 plane[ 3 ] = lm->plane[ 3 ];
965 /* otherwise make one from the points */
966 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
970 /* 4th point must fall on the plane */
971 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
972 if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
976 /* check to see if we need to calculate texture->world tangent vectors */
977 if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
987 /* map the vertexes */
988 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
989 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
990 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
991 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
993 /* subdivide the quad */
994 MapQuad_r( lm, info, dv, plane, stv, ttv );
1002 maps the locations, normals, and pvs clusters for a raw lightmap
1005 #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)
1007 void MapRawLightmap( int rawLightmapNum ){
1008 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1009 float *luxel, *origin, *normal, samples, radius, pass;
1011 bspDrawSurface_t *ds;
1012 surfaceInfo_t *info;
1013 mesh_t src, *subdivided, *mesh;
1014 bspDrawVert_t *verts, *dv[ 4 ], fake;
1017 /* bail if this number exceeds the number of raw lightmaps */
1018 if ( rawLightmapNum >= numRawLightmaps ) {
1023 lm = &rawLightmaps[ rawLightmapNum ];
1025 /* -----------------------------------------------------------------
1026 map referenced surfaces onto the raw lightmap
1027 ----------------------------------------------------------------- */
1029 /* walk the list of surfaces on this raw lightmap */
1030 for ( n = 0; n < lm->numLightSurfaces; n++ )
1032 /* with > 1 surface per raw lightmap, clear occluded */
1034 for ( y = 0; y < lm->sh; y++ )
1036 for ( x = 0; x < lm->sw; x++ )
1039 cluster = SUPER_CLUSTER( x, y );
1040 if ( *cluster < 0 ) {
1041 *cluster = CLUSTER_UNMAPPED;
1048 num = lightSurfaces[ lm->firstLightSurface + n ];
1049 ds = &bspDrawSurfaces[ num ];
1050 info = &surfaceInfos[ num ];
1052 /* bail if no lightmap to calculate */
1053 if ( info->lm != lm ) {
1058 /* map the surface onto the lightmap origin/cluster/normal buffers */
1059 switch ( ds->surfaceType )
1063 verts = yDrawVerts + ds->firstVert;
1065 /* map the triangles */
1066 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1068 for ( i = 0; i < ds->numIndexes; i += 3 )
1070 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1071 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1072 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1073 MapTriangle( lm, info, dv, mapNonAxial );
1079 /* make a mesh from the drawsurf */
1080 src.width = ds->patchWidth;
1081 src.height = ds->patchHeight;
1082 src.verts = &yDrawVerts[ ds->firstVert ];
1083 //% subdivided = SubdivideMesh( src, 8, 512 );
1084 subdivided = SubdivideMesh2( src, info->patchIterations );
1086 /* fit it to the curve and remove colinear verts on rows/columns */
1087 PutMeshOnCurve( *subdivided );
1088 mesh = RemoveLinearMeshColumnsRows( subdivided );
1089 FreeMesh( subdivided );
1092 verts = mesh->verts;
1097 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1098 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1099 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1100 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1104 /* map the mesh quads */
1107 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1109 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1111 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1114 pw[ 0 ] = x + ( y * mesh->width );
1115 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1116 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1117 pw[ 3 ] = x + 1 + ( y * mesh->width );
1118 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1123 /* get drawverts and map first triangle */
1124 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1125 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1126 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1127 MapTriangle( lm, info, dv, mapNonAxial );
1129 /* get drawverts and map second triangle */
1130 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1131 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1132 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1133 MapTriangle( lm, info, dv, mapNonAxial );
1140 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1142 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1145 pw[ 0 ] = x + ( y * mesh->width );
1146 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1147 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1148 pw[ 3 ] = x + 1 + ( y * mesh->width );
1154 /* attempt to map quad first */
1155 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1156 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1157 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1158 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1159 if ( MapQuad( lm, info, dv ) ) {
1163 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1165 /* get drawverts and map first triangle */
1166 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1167 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1168 MapTriangle( lm, info, dv, mapNonAxial );
1170 /* get drawverts and map second triangle */
1171 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1172 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1173 MapTriangle( lm, info, dv, mapNonAxial );
1189 /* -----------------------------------------------------------------
1190 average and clean up luxel normals
1191 ----------------------------------------------------------------- */
1193 /* walk the luxels */
1194 for ( y = 0; y < lm->sh; y++ )
1196 for ( x = 0; x < lm->sw; x++ )
1199 luxel = SUPER_LUXEL( 0, x, y );
1200 normal = SUPER_NORMAL( x, y );
1201 cluster = SUPER_CLUSTER( x, y );
1203 /* only look at mapped luxels */
1204 if ( *cluster < 0 ) {
1208 /* the normal data could be the sum of multiple samples */
1209 if ( luxel[ 3 ] > 1.0f ) {
1210 VectorNormalize( normal, normal );
1213 /* mark this luxel as having only one normal */
1218 /* non-planar surfaces stop here */
1219 if ( lm->plane == NULL ) {
1223 /* -----------------------------------------------------------------
1224 map occluded or unuxed luxels
1225 ----------------------------------------------------------------- */
1227 /* walk the luxels */
1228 radius = floor( superSample / 2 );
1229 radius = radius > 0 ? radius : 1.0f;
1231 for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1233 for ( y = 0; y < lm->sh; y++ )
1235 for ( x = 0; x < lm->sw; x++ )
1238 luxel = SUPER_LUXEL( 0, x, y );
1239 normal = SUPER_NORMAL( x, y );
1240 cluster = SUPER_CLUSTER( x, y );
1242 /* only look at unmapped luxels */
1243 if ( *cluster != CLUSTER_UNMAPPED ) {
1247 /* divine a normal and origin from neighboring luxels */
1248 VectorClear( fake.xyz );
1249 VectorClear( fake.normal );
1250 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1251 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1253 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1255 if ( sy < 0 || sy >= lm->sh ) {
1259 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1261 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1265 /* get neighboring luxel */
1266 luxel = SUPER_LUXEL( 0, sx, sy );
1267 origin = SUPER_ORIGIN( sx, sy );
1268 normal = SUPER_NORMAL( sx, sy );
1269 cluster = SUPER_CLUSTER( sx, sy );
1271 /* only consider luxels mapped in previous passes */
1272 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1276 /* add its distinctiveness to our own */
1277 VectorAdd( fake.xyz, origin, fake.xyz );
1278 VectorAdd( fake.normal, normal, fake.normal );
1279 samples += luxel[ 3 ];
1284 if ( samples == 0.0f ) {
1289 VectorDivide( fake.xyz, samples, fake.xyz );
1290 //% VectorDivide( fake.normal, samples, fake.normal );
1291 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1295 /* map the fake vert */
1296 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1301 /* -----------------------------------------------------------------
1302 average and clean up luxel normals
1303 ----------------------------------------------------------------- */
1305 /* walk the luxels */
1306 for ( y = 0; y < lm->sh; y++ )
1308 for ( x = 0; x < lm->sw; x++ )
1311 luxel = SUPER_LUXEL( 0, x, y );
1312 normal = SUPER_NORMAL( x, y );
1313 cluster = SUPER_CLUSTER( x, y );
1315 /* only look at mapped luxels */
1316 if ( *cluster < 0 ) {
1320 /* the normal data could be the sum of multiple samples */
1321 if ( luxel[ 3 ] > 1.0f ) {
1322 VectorNormalize( normal, normal );
1325 /* mark this luxel as having only one normal */
1333 for ( y = 0; y < lm->sh; y++ )
1335 for ( x = 0; x < lm->sw; x++ )
1340 cluster = SUPER_CLUSTER( x, y );
1341 origin = SUPER_ORIGIN( x, y );
1342 normal = SUPER_NORMAL( x, y );
1343 luxel = SUPER_LUXEL( x, y );
1345 if ( *cluster < 0 ) {
1349 /* check if within the bounding boxes of all surfaces referenced */
1350 ClearBounds( mins, maxs );
1351 for ( n = 0; n < lm->numLightSurfaces; n++ )
1354 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1355 TOL = info->sampleSize + 2;
1356 AddPointToBounds( info->mins, mins, maxs );
1357 AddPointToBounds( info->maxs, mins, maxs );
1358 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1359 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1360 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1366 if ( n < lm->numLightSurfaces ) {
1370 /* report bogus origin */
1371 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",
1372 rawLightmapNum, x, y, *cluster,
1373 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1374 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1375 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1386 sets up dirtmap (ambient occlusion)
1389 #define DIRT_CONE_ANGLE 88 /* degrees */
1390 #define DIRT_NUM_ANGLE_STEPS 16
1391 #define DIRT_NUM_ELEVATION_STEPS 3
1392 #define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1394 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1395 static int numDirtVectors = 0;
1397 void SetupDirt( void ){
1399 float angle, elevation, angleStep, elevationStep;
1403 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1405 /* calculate angular steps */
1406 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1407 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1411 for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1413 /* iterate elevation */
1414 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1416 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1417 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1418 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1423 /* emit some statistics */
1424 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1430 calculates dirt value for a given sample
1433 float DirtForSample( trace_t *trace ){
1435 float gatherDirt, outDirt, angle, elevation, ooDepth;
1436 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1443 if ( trace == NULL || trace->cluster < 0 ) {
1449 ooDepth = 1.0f / dirtDepth;
1450 VectorCopy( trace->normal, normal );
1452 /* check if the normal is aligned to the world-up */
1453 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
1454 if ( normal[ 2 ] == 1.0f ) {
1455 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1456 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1458 else if ( normal[ 2 ] == -1.0f ) {
1459 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1460 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1465 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1466 CrossProduct( normal, worldUp, myRt );
1467 VectorNormalize( myRt, myRt );
1468 CrossProduct( myRt, normal, myUp );
1469 VectorNormalize( myUp, myUp );
1472 /* 1 = random mode, 0 (well everything else) = non-random mode */
1473 if ( dirtMode == 1 ) {
1475 for ( i = 0; i < numDirtVectors; i++ )
1477 /* get random vector */
1478 angle = Random() * DEG2RAD( 360.0f );
1479 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1480 temp[ 0 ] = cos( angle ) * sin( elevation );
1481 temp[ 1 ] = sin( angle ) * sin( elevation );
1482 temp[ 2 ] = cos( elevation );
1484 /* transform into tangent space */
1485 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1486 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1487 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1490 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1491 SetupTrace( trace );
1492 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1496 if ( trace->opaque && !( trace->compileFlags & C_SKY ) ) {
1497 VectorSubtract( trace->hit, trace->origin, displacement );
1498 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1504 /* iterate through ordered vectors */
1505 for ( i = 0; i < numDirtVectors; i++ )
1507 /* transform vector into tangent space */
1508 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1509 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1510 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1513 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1514 SetupTrace( trace );
1515 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1519 if ( trace->opaque ) {
1520 VectorSubtract( trace->hit, trace->origin, displacement );
1521 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1527 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1528 SetupTrace( trace );
1529 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1533 if ( trace->opaque ) {
1534 VectorSubtract( trace->hit, trace->origin, displacement );
1535 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1539 if ( gatherDirt <= 0.0f ) {
1543 /* apply gain (does this even do much? heh) */
1544 outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1545 if ( outDirt > 1.0f ) {
1550 outDirt *= dirtScale;
1551 if ( outDirt > 1.0f ) {
1555 /* return to sender */
1556 return 1.0f - outDirt;
1563 calculates dirty fraction for each luxel
1566 void DirtyRawLightmap( int rawLightmapNum ){
1567 int i, x, y, sx, sy, *cluster;
1568 float *origin, *normal, *dirt, *dirt2, average, samples;
1570 surfaceInfo_t *info;
1575 /* bail if this number exceeds the number of raw lightmaps */
1576 if ( rawLightmapNum >= numRawLightmaps ) {
1581 lm = &rawLightmaps[ rawLightmapNum ];
1584 trace.testOcclusion = qtrue;
1585 trace.forceSunlight = qfalse;
1586 trace.recvShadows = lm->recvShadows;
1587 trace.numSurfaces = lm->numLightSurfaces;
1588 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1589 trace.inhibitRadius = 0.0f;
1590 trace.testAll = qfalse;
1592 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1593 trace.twoSided = qfalse;
1594 for ( i = 0; i < trace.numSurfaces; i++ )
1597 info = &surfaceInfos[ trace.surfaces[ i ] ];
1599 /* check twosidedness */
1600 if ( info->si->twoSided ) {
1601 trace.twoSided = qtrue;
1607 for ( i = 0; i < trace.numSurfaces; i++ )
1610 info = &surfaceInfos[ trace.surfaces[ i ] ];
1612 /* check twosidedness */
1613 if ( info->si->noDirty ) {
1620 for ( y = 0; y < lm->sh; y++ )
1622 for ( x = 0; x < lm->sw; x++ )
1625 cluster = SUPER_CLUSTER( x, y );
1626 origin = SUPER_ORIGIN( x, y );
1627 normal = SUPER_NORMAL( x, y );
1628 dirt = SUPER_DIRT( x, y );
1630 /* set default dirt */
1633 /* only look at mapped luxels */
1634 if ( *cluster < 0 ) {
1638 /* don't apply dirty on this surface */
1645 trace.cluster = *cluster;
1646 VectorCopy( origin, trace.origin );
1647 VectorCopy( normal, trace.normal );
1650 *dirt = DirtForSample( &trace );
1654 /* testing no filtering */
1658 for ( y = 0; y < lm->sh; y++ )
1660 for ( x = 0; x < lm->sw; x++ )
1663 cluster = SUPER_CLUSTER( x, y );
1664 dirt = SUPER_DIRT( x, y );
1666 /* filter dirt by adjacency to unmapped luxels */
1669 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1671 if ( sy < 0 || sy >= lm->sh ) {
1675 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1677 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1681 /* get neighboring luxel */
1682 cluster = SUPER_CLUSTER( sx, sy );
1683 dirt2 = SUPER_DIRT( sx, sy );
1684 if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1694 if ( samples <= 0.0f ) {
1700 if ( samples <= 0.0f ) {
1705 *dirt = average / samples;
1714 calculates the pvs cluster, origin, normal of a sub-luxel
1717 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1718 int i, *cluster, *cluster2;
1719 float *origin, *origin2, *normal; //% , *normal2;
1720 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1723 /* calulate x vector */
1724 if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1725 cluster = SUPER_CLUSTER( x, y );
1726 origin = SUPER_ORIGIN( x, y );
1727 //% normal = SUPER_NORMAL( x, y );
1728 cluster2 = SUPER_CLUSTER( x + 1, y );
1729 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1730 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1732 else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1733 cluster = SUPER_CLUSTER( x - 1, y );
1734 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1735 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1736 cluster2 = SUPER_CLUSTER( x, y );
1737 origin2 = SUPER_ORIGIN( x, y );
1738 //% normal2 = SUPER_NORMAL( x, y );
1742 Error( "Spurious lightmap S vector\n" );
1745 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1746 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1748 /* calulate y vector */
1749 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1750 cluster = SUPER_CLUSTER( x, y );
1751 origin = SUPER_ORIGIN( x, y );
1752 //% normal = SUPER_NORMAL( x, y );
1753 cluster2 = SUPER_CLUSTER( x, y + 1 );
1754 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1755 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1757 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1758 cluster = SUPER_CLUSTER( x, y - 1 );
1759 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1760 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1761 cluster2 = SUPER_CLUSTER( x, y );
1762 origin2 = SUPER_ORIGIN( x, y );
1763 //% normal2 = SUPER_NORMAL( x, y );
1766 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1769 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1770 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1772 /* calculate new origin */
1773 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1774 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1775 for ( i = 0; i < 3; i++ )
1776 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1779 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1780 if ( *sampleCluster < 0 ) {
1784 /* calculate new normal */
1785 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1786 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1787 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
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_Printf( "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 = superSample * filterRadius / lm->sampleSize;
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 );
2249 ////////// 27's temp hack for testing edge clipping ////
2250 if ( origin[0] == 0 && origin[1] == 0 && origin[2] == 0 ) {
2251 lightLuxel[ 1 ] = 255;
2252 lightLuxel[ 3 ] = 1.0f;
2258 /* set contribution count */
2259 lightLuxel[ 3 ] = 1.0f;
2262 trace.cluster = *cluster;
2263 VectorCopy( origin, trace.origin );
2264 VectorCopy( normal, trace.normal );
2266 /* get light for this sample */
2267 LightContributionToSample( &trace );
2268 VectorCopy( trace.color, lightLuxel );
2270 /* add the contribution to the deluxemap */
2272 VectorCopy( trace.directionContribution, lightDeluxel );
2275 /* check for evilness */
2276 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2278 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2281 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2288 /* don't even bother with everything else if nothing was lit */
2289 if ( totalLighted == 0 ) {
2293 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2294 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2295 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2297 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2299 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2304 VectorClear( total );
2306 /* test 2x2 stamp */
2307 for ( t = 0; t < 4; t++ )
2309 /* set sample coords */
2310 sx = x + tests[ t ][ 0 ];
2311 sy = y + tests[ t ][ 1 ];
2314 cluster = SUPER_CLUSTER( sx, sy );
2315 if ( *cluster < 0 ) {
2321 flag = SUPER_FLAG( sx, sy );
2322 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2323 /* force a lighted/mapped discrepancy so we subsample */
2328 lightLuxel = LIGHT_LUXEL( sx, sy );
2329 VectorAdd( total, lightLuxel, total );
2330 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2335 /* if total color is under a certain amount, then don't bother subsampling */
2336 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2340 /* if all 4 pixels are either in shadow or light, then don't subsample */
2341 if ( lighted != 0 && lighted != mapped ) {
2342 for ( t = 0; t < 4; t++ )
2344 /* set sample coords */
2345 sx = x + tests[ t ][ 0 ];
2346 sy = y + tests[ t ][ 1 ];
2349 cluster = SUPER_CLUSTER( sx, sy );
2350 if ( *cluster < 0 ) {
2353 flag = SUPER_FLAG( sx, sy );
2354 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2357 lightLuxel = LIGHT_LUXEL( sx, sy );
2358 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2359 origin = SUPER_ORIGIN( sx, sy );
2361 /* only subsample shadowed luxels */
2362 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2366 if ( lightRandomSamples ) {
2367 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2370 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2373 *flag |= FLAG_ALREADY_SUBSAMPLED;
2375 /* debug code to colorize subsampled areas to yellow */
2376 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2377 //% VectorSet( luxel, 255, 204, 0 );
2384 /* tertiary pass, apply dirt map (ambient occlusion) */
2387 for ( y = 0; y < lm->sh; y++ )
2389 for ( x = 0; x < lm->sw; x++ )
2392 cluster = SUPER_CLUSTER( x, y );
2393 if ( *cluster < 0 ) {
2397 /* get particulars */
2398 lightLuxel = LIGHT_LUXEL( x, y );
2399 dirt = SUPER_DIRT( x, y );
2401 /* scale light value */
2402 VectorScale( lightLuxel, *dirt, lightLuxel );
2407 /* allocate sampling lightmap storage */
2408 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2409 /* allocate sampling lightmap storage */
2410 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2411 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2412 memset( lm->superLuxels[ lightmapNum ], 0, size );
2416 if ( lightmapNum > 0 ) {
2417 lm->styles[ lightmapNum ] = trace.light->style;
2418 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2421 /* copy to permanent luxels */
2422 for ( y = 0; y < lm->sh; y++ )
2424 for ( x = 0; x < lm->sw; x++ )
2426 /* get cluster and origin */
2427 cluster = SUPER_CLUSTER( x, y );
2428 if ( *cluster < 0 ) {
2431 origin = SUPER_ORIGIN( x, y );
2434 if ( luxelFilterRadius ) {
2436 VectorClear( averageColor );
2437 VectorClear( averageDir );
2440 /* cheaper distance-based filtering */
2441 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2443 if ( sy < 0 || sy >= lm->sh ) {
2447 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2449 if ( sx < 0 || sx >= lm->sw ) {
2453 /* get particulars */
2454 cluster = SUPER_CLUSTER( sx, sy );
2455 if ( *cluster < 0 ) {
2458 lightLuxel = LIGHT_LUXEL( sx, sy );
2459 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2462 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2463 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2465 /* scale luxel by filter weight */
2466 VectorScale( lightLuxel, weight, color );
2467 VectorAdd( averageColor, color, averageColor );
2469 VectorScale( lightDeluxel, weight, direction );
2470 VectorAdd( averageDir, direction, averageDir );
2477 if ( samples <= 0.0f ) {
2481 /* scale into luxel */
2482 luxel = SUPER_LUXEL( lightmapNum, x, y );
2485 /* handle negative light */
2486 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2487 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2488 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2489 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2492 /* handle normal light */
2495 luxel[ 0 ] += averageColor[ 0 ] / samples;
2496 luxel[ 1 ] += averageColor[ 1 ] / samples;
2497 luxel[ 2 ] += averageColor[ 2 ] / samples;
2501 /* scale into luxel */
2502 deluxel = SUPER_DELUXEL( x, y );
2503 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2504 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2505 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2512 /* get particulars */
2513 lightLuxel = LIGHT_LUXEL( x, y );
2514 lightDeluxel = LIGHT_DELUXEL( x, y );
2515 luxel = SUPER_LUXEL( lightmapNum, x, y );
2516 deluxel = SUPER_DELUXEL( x, y );
2518 /* handle negative light */
2519 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2520 VectorScale( averageColor, -1.0f, averageColor );
2526 /* handle negative light */
2527 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2528 VectorSubtract( luxel, lightLuxel, luxel );
2531 /* handle normal light */
2533 VectorAdd( luxel, lightLuxel, luxel );
2537 VectorAdd( deluxel, lightDeluxel, deluxel );
2544 /* free temporary luxels */
2545 if ( lightLuxels != stackLightLuxels ) {
2546 free( lightLuxels );
2550 free( lightDeluxels );
2554 /* free light list */
2555 FreeTraceLights( &trace );
2557 /* floodlight pass */
2558 if ( floodlighty ) {
2559 FloodlightIlluminateLightmap( lm );
2562 if ( debugnormals ) {
2563 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2566 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2570 for ( y = 0; y < lm->sh; y++ )
2572 for ( x = 0; x < lm->sw; x++ )
2575 cluster = SUPER_CLUSTER( x, y );
2576 //% if( *cluster < 0 )
2579 /* get particulars */
2580 luxel = SUPER_LUXEL( lightmapNum, x, y );
2581 normal = SUPER_NORMAL( x, y );
2583 luxel[0] = ( normal[0] * 127 ) + 127;
2584 luxel[1] = ( normal[1] * 127 ) + 127;
2585 luxel[2] = ( normal[2] * 127 ) + 127;
2591 /* -----------------------------------------------------------------
2593 ----------------------------------------------------------------- */
2596 /* walk lightmaps */
2597 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2600 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2604 /* apply dirt to each luxel */
2605 for ( y = 0; y < lm->sh; y++ )
2607 for ( x = 0; x < lm->sw; x++ )
2610 cluster = SUPER_CLUSTER( x, y );
2611 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2614 /* get particulars */
2615 luxel = SUPER_LUXEL( lightmapNum, x, y );
2616 dirt = SUPER_DIRT( x, y );
2619 VectorScale( luxel, *dirt, luxel );
2623 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2630 /* -----------------------------------------------------------------
2632 ----------------------------------------------------------------- */
2634 /* walk lightmaps */
2635 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2638 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2642 /* average occluded luxels from neighbors */
2643 for ( y = 0; y < lm->sh; y++ )
2645 for ( x = 0; x < lm->sw; x++ )
2647 /* get particulars */
2648 cluster = SUPER_CLUSTER( x, y );
2649 luxel = SUPER_LUXEL( lightmapNum, x, y );
2650 deluxel = SUPER_DELUXEL( x, y );
2651 normal = SUPER_NORMAL( x, y );
2653 /* determine if filtering is necessary */
2654 filterColor = qfalse;
2656 if ( *cluster < 0 ||
2657 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2658 filterColor = qtrue;
2661 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2665 if ( !filterColor && !filterDir ) {
2669 /* choose seed amount */
2670 VectorClear( averageColor );
2671 VectorClear( averageDir );
2674 /* walk 3x3 matrix */
2675 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2677 if ( sy < 0 || sy >= lm->sh ) {
2681 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2683 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2687 /* get neighbor's particulars */
2688 cluster2 = SUPER_CLUSTER( sx, sy );
2689 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2690 deluxel2 = SUPER_DELUXEL( sx, sy );
2692 /* ignore unmapped/unlit luxels */
2693 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2694 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2698 /* add its distinctiveness to our own */
2699 VectorAdd( averageColor, luxel2, averageColor );
2700 samples += luxel2[ 3 ];
2702 VectorAdd( averageDir, deluxel2, averageDir );
2708 if ( samples <= 0.0f ) {
2712 /* dark lightmap seams */
2714 if ( lightmapNum == 0 ) {
2715 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2721 if ( filterColor ) {
2722 VectorDivide( averageColor, samples, luxel );
2726 VectorDivide( averageDir, samples, deluxel );
2729 /* set cluster to -3 */
2730 if ( *cluster < 0 ) {
2731 *cluster = CLUSTER_FLOODED;
2740 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2743 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2746 for ( y = 0; y < lm->sh; y++ )
2747 for ( x = 0; x < lm->sw; x++ )
2750 cluster = SUPER_CLUSTER( x, y );
2751 luxel = SUPER_LUXEL( lightmapNum, x, y );
2752 deluxel = SUPER_DELUXEL( x, y );
2753 if ( !luxel || !deluxel || !cluster ) {
2754 Sys_FPrintf( SYS_VRB, "WARNING: I got NULL'd.\n" );
2757 else if ( *cluster < 0 ) {
2759 // should have neither deluxemap nor lightmap
2761 Sys_FPrintf( SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n" );
2767 // should have both deluxemap and lightmap
2769 Sys_FPrintf( SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n" );
2780 IlluminateVertexes()
2781 light the surface vertexes
2784 #define VERTEX_NUDGE 4.0f
2786 void IlluminateVertexes( int num ){
2787 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2788 int lightmapNum, numAvg;
2789 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2790 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2791 bspDrawSurface_t *ds;
2792 surfaceInfo_t *info;
2794 bspDrawVert_t *verts;
2796 float floodLightAmount;
2800 /* get surface, info, and raw lightmap */
2801 ds = &bspDrawSurfaces[ num ];
2802 info = &surfaceInfos[ num ];
2805 /* -----------------------------------------------------------------
2806 illuminate the vertexes
2807 ----------------------------------------------------------------- */
2809 /* calculate vertex lighting for surfaces without lightmaps */
2810 if ( lm == NULL || cpmaHack ) {
2812 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2813 trace.forceSunlight = info->si->forceSunlight;
2814 trace.recvShadows = info->recvShadows;
2815 trace.numSurfaces = 1;
2816 trace.surfaces = #
2817 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2819 /* twosided lighting */
2820 trace.twoSided = info->si->twoSided;
2822 /* make light list for this surface */
2823 CreateTraceLightsForSurface( num, &trace );
2826 verts = yDrawVerts + ds->firstVert;
2828 memset( avgColors, 0, sizeof( avgColors ) );
2830 /* walk the surface verts */
2831 for ( i = 0; i < ds->numVerts; i++ )
2833 /* get vertex luxel */
2834 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2836 /* color the luxel with raw lightmap num? */
2837 if ( debugSurfaces ) {
2838 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2841 /* color the luxel with luxel origin? */
2842 else if ( debugOrigin ) {
2843 VectorSubtract( info->maxs, info->mins, temp );
2844 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2845 VectorSubtract( origin, lm->mins, temp2 );
2846 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2847 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2848 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2851 /* color the luxel with the normal */
2852 else if ( normalmap ) {
2853 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2854 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2855 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2858 /* illuminate the vertex */
2861 /* clear vertex luxel */
2862 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2864 /* try at initial origin */
2865 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2866 if ( trace.cluster >= 0 ) {
2868 VectorCopy( verts[ i ].xyz, trace.origin );
2869 VectorCopy( verts[ i ].normal, trace.normal );
2872 if ( dirty && !bouncing ) {
2873 dirt = DirtForSample( &trace );
2879 /* jal: floodlight */
2880 floodLightAmount = 0.0f;
2881 VectorClear( floodColor );
2882 if ( floodlighty && !bouncing ) {
2883 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2884 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2888 LightingAtSample( &trace, ds->vertexStyles, colors );
2891 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2894 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2896 /* jal: floodlight */
2897 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2900 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2901 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2902 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2906 /* is this sample bright enough? */
2907 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2908 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2909 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2910 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2911 /* nudge the sample point around a bit */
2912 for ( x = 0; x < 5; x++ )
2914 /* two's complement 0, 1, -1, 2, -2, etc */
2915 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2917 for ( y = 0; y < 5; y++ )
2919 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2921 for ( z = 0; z < 5; z++ )
2923 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2926 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2927 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2928 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2930 /* try at nudged origin */
2931 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2932 if ( trace.cluster < 0 ) {
2937 if ( dirty && !bouncing ) {
2938 dirt = DirtForSample( &trace );
2944 /* jal: floodlight */
2945 floodLightAmount = 0.0f;
2946 VectorClear( floodColor );
2947 if ( floodlighty && !bouncing ) {
2948 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2949 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2953 LightingAtSample( &trace, ds->vertexStyles, colors );
2956 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2959 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2961 /* jal: floodlight */
2962 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2965 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2966 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2969 /* bright enough? */
2970 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2971 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2972 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2973 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2981 /* add to average? */
2982 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2983 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2984 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2985 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2987 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2989 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2990 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2995 /* another happy customer */
2996 numVertsIlluminated++;
2999 /* set average color */
3001 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3002 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
3006 VectorCopy( ambientColor, avgColors[ 0 ] );
3009 /* clean up and store vertex color */
3010 for ( i = 0; i < ds->numVerts; i++ )
3012 /* get vertex luxel */
3013 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
3015 /* store average in occluded vertexes */
3016 if ( radVertLuxel[ 0 ] < 0.0f ) {
3017 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3019 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3020 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
3023 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
3028 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3031 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3032 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3035 if ( bouncing || bounce == 0 || !bounceOnly ) {
3036 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3038 if ( !info->si->noVertexLight ) {
3039 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3044 /* free light list */
3045 FreeTraceLights( &trace );
3047 /* return to sender */
3051 /* -----------------------------------------------------------------
3052 reconstitute vertex lighting from the luxels
3053 ----------------------------------------------------------------- */
3055 /* set styles from lightmap */
3056 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3057 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3059 /* get max search radius */
3061 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3063 /* walk the surface verts */
3064 verts = yDrawVerts + ds->firstVert;
3065 for ( i = 0; i < ds->numVerts; i++ )
3067 /* do each lightmap */
3068 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3071 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3075 /* get luxel coords */
3076 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3077 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3081 else if ( x >= lm->sw ) {
3087 else if ( y >= lm->sh ) {
3091 /* get vertex luxels */
3092 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3093 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3095 /* color the luxel with the normal? */
3097 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3098 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3099 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3102 /* color the luxel with surface num? */
3103 else if ( debugSurfaces ) {
3104 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3107 /* divine color from the superluxels */
3110 /* increasing radius */
3111 VectorClear( radVertLuxel );
3113 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3115 /* sample within radius */
3116 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3118 if ( sy < 0 || sy >= lm->sh ) {
3122 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3124 if ( sx < 0 || sx >= lm->sw ) {
3128 /* get luxel particulars */
3129 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3130 cluster = SUPER_CLUSTER( sx, sy );
3131 if ( *cluster < 0 ) {
3135 /* testing: must be brigher than ambient color */
3136 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3139 /* add its distinctiveness to our own */
3140 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3141 samples += luxel[ 3 ];
3147 if ( samples > 0.0f ) {
3148 VectorDivide( radVertLuxel, samples, radVertLuxel );
3151 VectorCopy( ambientColor, radVertLuxel );
3155 /* store into floating point storage */
3156 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3157 numVertsIlluminated++;
3159 /* store into bytes (for vertex approximation) */
3160 if ( !info->si->noVertexLight ) {
3161 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3169 /* -------------------------------------------------------------------------------
3171 light optimization (-fast)
3173 creates a list of lights that will affect a surface and stores it in tw
3174 this is to optimize surface lighting by culling out as many of the
3175 lights in the world as possible from further calculation
3177 ------------------------------------------------------------------------------- */
3181 determines opaque brushes in the world and find sky shaders for sunlight calculations
3184 void SetupBrushesFlags( int mask_any, int test_any, int mask_all, int test_all ){
3186 unsigned int compileFlags, allCompileFlags;
3189 bspBrushSide_t *side;
3190 bspShader_t *shader;
3195 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3198 if ( opaqueBrushes == NULL ) {
3199 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3203 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3204 numOpaqueBrushes = 0;
3206 /* walk the list of worldspawn brushes */
3207 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3210 b = bspModels[ 0 ].firstBSPBrush + i;
3211 brush = &bspBrushes[ b ];
3213 /* check all sides */
3216 allCompileFlags = ~( 0u );
3217 for ( j = 0; j < brush->numSides && inside; j++ )
3219 /* do bsp shader calculations */
3220 side = &bspBrushSides[ brush->firstSide + j ];
3221 shader = &bspShaders[ side->shaderNum ];
3223 /* get shader info */
3224 si = ShaderInfoForShaderNull( shader->shader );
3229 /* or together compile flags */
3230 compileFlags |= si->compileFlags;
3231 allCompileFlags &= si->compileFlags;
3234 /* determine if this brush is opaque to light */
3235 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3236 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3242 /* emit some statistics */
3243 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3245 void SetupBrushes( void ){
3246 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3253 determines if two clusters are visible to each other using the PVS
3256 qboolean ClusterVisible( int a, int b ){
3262 if ( a < 0 || b < 0 ) {
3272 if ( numBSPVisBytes <= 8 ) {
3277 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3278 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3279 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3282 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3292 borrowed from vlight.c
3295 int PointInLeafNum_r( vec3_t point, int nodenum ){
3302 while ( nodenum >= 0 )
3304 node = &bspNodes[ nodenum ];
3305 plane = &bspPlanes[ node->planeNum ];
3306 dist = DotProduct( point, plane->normal ) - plane->dist;
3308 nodenum = node->children[ 0 ];
3310 else if ( dist < -0.1 ) {
3311 nodenum = node->children[ 1 ];
3315 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3316 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3319 nodenum = node->children[ 1 ];
3323 leafnum = -nodenum - 1;
3331 borrowed from vlight.c
3334 int PointInLeafNum( vec3_t point ){
3335 return PointInLeafNum_r( point, 0 );
3341 ClusterVisibleToPoint() - ydnar
3342 returns qtrue if point can "see" cluster
3345 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3349 /* get leafNum for point */
3350 pointCluster = ClusterForPoint( point );
3351 if ( pointCluster < 0 ) {
3356 return ClusterVisible( pointCluster, cluster );
3362 ClusterForPoint() - ydnar
3363 returns the pvs cluster for point
3366 int ClusterForPoint( vec3_t point ){
3370 /* get leafNum for point */
3371 leafNum = PointInLeafNum( point );
3372 if ( leafNum < 0 ) {
3376 /* return the cluster */
3377 return bspLeafs[ leafNum ].cluster;
3383 ClusterForPointExt() - ydnar
3384 also takes brushes into account for occlusion testing
3387 int ClusterForPointExt( vec3_t point, float epsilon ){
3388 int i, j, b, leafNum, cluster;
3391 int *brushes, numBSPBrushes;
3397 /* get leaf for point */
3398 leafNum = PointInLeafNum( point );
3399 if ( leafNum < 0 ) {
3402 leaf = &bspLeafs[ leafNum ];
3404 /* get the cluster */
3405 cluster = leaf->cluster;
3406 if ( cluster < 0 ) {
3410 /* transparent leaf, so check point against all brushes in the leaf */
3411 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3412 numBSPBrushes = leaf->numBSPLeafBrushes;
3413 for ( i = 0; i < numBSPBrushes; i++ )
3417 if ( b > maxOpaqueBrush ) {
3420 brush = &bspBrushes[ b ];
3421 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3425 /* check point against all planes */
3427 for ( j = 0; j < brush->numSides && inside; j++ )
3429 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3430 dot = DotProduct( point, plane->normal );
3432 if ( dot > epsilon ) {
3437 /* if inside, return bogus cluster */
3443 /* if the point made it this far, it's not inside any opaque brushes */
3450 ClusterForPointExtFilter() - ydnar
3451 adds cluster checking against a list of known valid clusters
3454 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3458 /* get cluster for point */
3459 cluster = ClusterForPointExt( point, epsilon );
3461 /* check if filtering is necessary */
3462 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3467 for ( i = 0; i < numClusters; i++ )
3469 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3481 ShaderForPointInLeaf() - ydnar
3482 checks a point against all brushes in a leaf, returning the shader of the brush
3483 also sets the cumulative surface and content flags for the brush hit
3486 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3490 int *brushes, numBSPBrushes;
3493 bspBrushSide_t *side;
3495 bspShader_t *shader;
3496 int allSurfaceFlags, allContentFlags;
3499 /* clear things out first */
3504 if ( leafNum < 0 ) {
3507 leaf = &bspLeafs[ leafNum ];
3509 /* transparent leaf, so check point against all brushes in the leaf */
3510 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3511 numBSPBrushes = leaf->numBSPLeafBrushes;
3512 for ( i = 0; i < numBSPBrushes; i++ )
3515 brush = &bspBrushes[ brushes[ i ] ];
3517 /* check point against all planes */
3519 allSurfaceFlags = 0;
3520 allContentFlags = 0;
3521 for ( j = 0; j < brush->numSides && inside; j++ )
3523 side = &bspBrushSides[ brush->firstSide + j ];
3524 plane = &bspPlanes[ side->planeNum ];
3525 dot = DotProduct( point, plane->normal );
3527 if ( dot > epsilon ) {
3532 shader = &bspShaders[ side->shaderNum ];
3533 allSurfaceFlags |= shader->surfaceFlags;
3534 allContentFlags |= shader->contentFlags;
3538 /* handle if inside */
3540 /* if there are desired flags, check for same and continue if they aren't matched */
3541 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3544 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3548 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3549 *surfaceFlags = allSurfaceFlags;
3550 *contentFlags = allContentFlags;
3551 return brush->shaderNum;
3555 /* if the point made it this far, it's not inside any brushes */
3563 chops a bounding box by the plane defined by origin and normal
3564 returns qfalse if the bounds is entirely clipped away
3566 this is not exactly the fastest way to do this...
3569 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3570 /* FIXME: rewrite this so it doesn't use bloody brushes */
3578 calculates each light's effective envelope,
3579 taking into account brightness, type, and pvs.
3582 #define LIGHT_EPSILON 0.125f
3583 #define LIGHT_NUDGE 2.0f
3585 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3586 int i, x, y, z, x1, y1, z1;
3587 light_t *light, *light2, **owner;
3589 vec3_t origin, dir, mins, maxs;
3590 float radius, intensity;
3591 light_t *buckets[ 256 ];
3594 /* early out for weird cases where there are no lights */
3595 if ( lights == NULL ) {
3600 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3604 numCulledLights = 0;
3606 while ( *owner != NULL )
3611 /* handle negative lights */
3612 if ( light->photons < 0.0f || light->add < 0.0f ) {
3613 light->photons *= -1.0f;
3614 light->add *= -1.0f;
3615 light->flags |= LIGHT_NEGATIVE;
3619 if ( light->type == EMIT_SUN ) {
3622 light->envelope = MAX_WORLD_COORD * 8.0f;
3623 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3624 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3627 /* everything else */
3630 /* get pvs cluster for light */
3631 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3633 /* invalid cluster? */
3634 if ( light->cluster < 0 ) {
3635 /* nudge the sample point around a bit */
3636 for ( x = 0; x < 4; x++ )
3638 /* two's complement 0, 1, -1, 2, -2, etc */
3639 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3641 for ( y = 0; y < 4; y++ )
3643 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3645 for ( z = 0; z < 4; z++ )
3647 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3650 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3651 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3652 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3654 /* try at nudged origin */
3655 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3656 if ( light->cluster < 0 ) {
3661 VectorCopy( origin, light->origin );
3667 /* only calculate for lights in pvs and outside of opaque brushes */
3668 if ( light->cluster >= 0 ) {
3669 /* set light fast flag */
3671 light->flags |= LIGHT_FAST_TEMP;
3674 light->flags &= ~LIGHT_FAST_TEMP;
3676 if ( fastpoint && ( light->flags != EMIT_AREA ) ) {
3677 light->flags |= LIGHT_FAST_TEMP;
3679 if ( light->si && light->si->noFast ) {
3680 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3683 /* clear light envelope */
3684 light->envelope = 0;
3686 /* handle area lights */
3687 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3688 light->envelope = MAX_WORLD_COORD * 8.0f;
3690 /* check for fast mode */
3691 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3692 /* ugly hack to calculate extent for area lights, but only done once */
3693 VectorScale( light->normal, -1.0f, dir );
3694 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3698 VectorMA( light->origin, radius, light->normal, origin );
3699 factor = PointToPolygonFormFactor( origin, dir, light->w );
3700 if ( factor < 0.0f ) {
3703 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3704 light->envelope = radius;
3710 intensity = light->photons; /* hopefully not used */
3715 intensity = light->photons;
3719 if ( light->envelope <= 0.0f ) {
3720 /* solve distance for non-distance lights */
3721 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3722 light->envelope = MAX_WORLD_COORD * 8.0f;
3725 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3726 /* solve distance for linear lights */
3727 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3728 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3732 add = angle * light->photons * linearScale - (dist * light->fade);
3733 T = (light->photons * linearScale) - (dist * light->fade);
3734 T + (dist * light->fade) = (light->photons * linearScale);
3735 dist * light->fade = (light->photons * linearScale) - T;
3736 dist = ((light->photons * linearScale) - T) / light->fade;
3739 /* solve for inverse square falloff */
3741 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3745 add = light->photons / (dist * dist);
3746 T = light->photons / (dist * dist);
3747 T * (dist * dist) = light->photons;
3748 dist = sqrt( light->photons / T );
3753 /* solve distance for linear lights */
3754 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3755 light->envelope = ( intensity * linearScale ) / light->fade;
3758 /* can't cull these */
3760 light->envelope = MAX_WORLD_COORD * 8.0f;
3765 /* chop radius against pvs */
3768 ClearBounds( mins, maxs );
3770 /* check all leaves */
3771 for ( i = 0; i < numBSPLeafs; i++ )
3774 leaf = &bspLeafs[ i ];
3777 if ( leaf->cluster < 0 ) {
3780 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3784 /* add this leafs bbox to the bounds */
3785 VectorCopy( leaf->mins, origin );
3786 AddPointToBounds( origin, mins, maxs );
3787 VectorCopy( leaf->maxs, origin );
3788 AddPointToBounds( origin, mins, maxs );
3791 /* test to see if bounds encompass light */
3792 for ( i = 0; i < 3; i++ )
3794 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3795 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3796 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3797 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3798 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3799 AddPointToBounds( light->origin, mins, maxs );
3803 /* chop the bounds by a plane for area lights and spotlights */
3804 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3805 ChopBounds( mins, maxs, light->origin, light->normal );
3809 VectorCopy( mins, light->mins );
3810 VectorCopy( maxs, light->maxs );
3812 /* reflect bounds around light origin */
3813 //% VectorMA( light->origin, -1.0f, origin, origin );
3814 VectorScale( light->origin, 2, origin );
3815 VectorSubtract( origin, maxs, origin );
3816 AddPointToBounds( origin, mins, maxs );
3817 //% VectorMA( light->origin, -1.0f, mins, origin );
3818 VectorScale( light->origin, 2, origin );
3819 VectorSubtract( origin, mins, origin );
3820 AddPointToBounds( origin, mins, maxs );
3822 /* calculate spherical bounds */
3823 VectorSubtract( maxs, light->origin, dir );
3824 radius = (float) VectorLength( dir );
3826 /* if this radius is smaller than the envelope, then set the envelope to it */
3827 if ( radius < light->envelope ) {
3828 light->envelope = radius;
3829 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3832 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3835 /* add grid/surface only check */
3837 if ( !( light->flags & LIGHT_GRID ) ) {
3838 light->envelope = 0.0f;
3843 if ( !( light->flags & LIGHT_SURFACES ) ) {
3844 light->envelope = 0.0f;
3850 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3852 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3854 /* delete the light */
3856 *owner = light->next;
3857 if ( light->w != NULL ) {
3865 /* square envelope */
3866 light->envelope2 = ( light->envelope * light->envelope );
3868 /* increment light count */
3871 /* set next light */
3872 owner = &( ( **owner ).next );
3875 /* bucket sort lights by style */
3876 memset( buckets, 0, sizeof( buckets ) );
3878 for ( light = lights; light != NULL; light = light2 )
3880 /* get next light */
3881 light2 = light->next;
3883 /* filter into correct bucket */
3884 light->next = buckets[ light->style ];
3885 buckets[ light->style ] = light;
3887 /* if any styled light is present, automatically set nocollapse */
3888 if ( light->style != LS_NORMAL ) {
3893 /* filter back into light list */
3895 for ( i = 255; i >= 0; i-- )
3898 for ( light = buckets[ i ]; light != NULL; light = light2 )
3900 light2 = light->next;
3901 light->next = lights;
3906 /* emit some statistics */
3907 Sys_Printf( "%9d total lights\n", numLights );
3908 Sys_Printf( "%9d culled lights\n", numCulledLights );
3914 CreateTraceLightsForBounds()
3915 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3918 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3921 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3922 float radius, dist, length;
3925 /* potential pre-setup */
3926 if ( numLights == 0 ) {
3927 SetupEnvelopes( qfalse, fast );
3931 //% 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 ] );
3933 /* allocate the light list */
3934 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3935 trace->numLights = 0;
3937 /* calculate spherical bounds */
3938 VectorAdd( mins, maxs, origin );
3939 VectorScale( origin, 0.5f, origin );
3940 VectorSubtract( maxs, origin, dir );
3941 radius = (float) VectorLength( dir );
3943 /* get length of normal vector */
3944 if ( normal != NULL ) {
3945 length = VectorLength( normal );
3949 normal = nullVector;
3953 /* test each light and see if it reaches the sphere */
3954 /* note: the attenuation code MUST match LightingAtSample() */
3955 for ( light = lights; light; light = light->next )
3957 /* check zero sized envelope */
3958 if ( light->envelope <= 0 ) {
3959 lightsEnvelopeCulled++;
3964 if ( !( light->flags & flags ) ) {
3968 /* sunlight skips all this nonsense */
3969 if ( light->type != EMIT_SUN ) {
3975 /* check against pvs cluster */
3976 if ( numClusters > 0 && clusters != NULL ) {
3977 for ( i = 0; i < numClusters; i++ )
3979 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3985 if ( i == numClusters ) {
3986 lightsClusterCulled++;
3991 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3992 VectorSubtract( light->origin, origin, dir );
3993 dist = VectorLength( dir );
3994 dist -= light->envelope;
3997 lightsEnvelopeCulled++;
4001 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
4004 for ( i = 0; i < 3; i++ )
4006 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
4011 lightsBoundsCulled++;
4017 /* planar surfaces (except twosided surfaces) have a couple more checks */
4018 if ( length > 0.0f && trace->twoSided == qfalse ) {
4019 /* lights coplanar with a surface won't light it */
4020 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
4021 lightsPlaneCulled++;
4025 /* check to see if light is behind the plane */
4026 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
4027 lightsPlaneCulled++;
4032 /* add this light */
4033 trace->lights[ trace->numLights++ ] = light;
4036 /* make last night null */
4037 trace->lights[ trace->numLights ] = NULL;
4042 void FreeTraceLights( trace_t *trace ){
4043 if ( trace->lights != NULL ) {
4044 free( trace->lights );
4051 CreateTraceLightsForSurface()
4052 creates a list of lights that can potentially affect a drawsurface
4055 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4057 vec3_t mins, maxs, normal;
4059 bspDrawSurface_t *ds;
4060 surfaceInfo_t *info;
4068 /* get drawsurface and info */
4069 ds = &bspDrawSurfaces[ num ];
4070 info = &surfaceInfos[ num ];
4072 /* get the mins/maxs for the dsurf */
4073 ClearBounds( mins, maxs );
4074 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4075 for ( i = 0; i < ds->numVerts; i++ )
4077 dv = &yDrawVerts[ ds->firstVert + i ];
4078 AddPointToBounds( dv->xyz, mins, maxs );
4079 if ( !VectorCompare( dv->normal, normal ) ) {
4080 VectorClear( normal );
4084 /* create the lights for the bounding box */
4085 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4088 /////////////////////////////////////////////////////////////
4090 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4091 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4092 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4093 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4095 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4096 static int numFloodVectors = 0;
4098 void SetupFloodLight( void ){
4100 float angle, elevation, angleStep, elevationStep;
4102 double v1,v2,v3,v4,v5,v6;
4105 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4107 /* calculate angular steps */
4108 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4109 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4113 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4115 /* iterate elevation */
4116 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4118 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4119 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4120 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4125 /* emit some statistics */
4126 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4129 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4131 if ( value[ 0 ] != '\0' ) {
4133 v4 = floodlightDistance;
4134 v5 = floodlightIntensity;
4135 v6 = floodlightDirectionScale;
4137 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4139 floodlightRGB[0] = v1;
4140 floodlightRGB[1] = v2;
4141 floodlightRGB[2] = v3;
4143 if ( VectorLength( floodlightRGB ) == 0 ) {
4144 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4157 floodlightDistance = v4;
4158 floodlightIntensity = v5;
4159 floodlightDirectionScale = v6;
4161 floodlighty = qtrue;
4162 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4166 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4169 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4170 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4171 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4173 ColorNormalize( floodlightRGB,floodlightRGB );
4177 FloodLightForSample()
4178 calculates floodlight value for a given sample
4179 once again, kudos to the dirtmapping coder
4182 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4187 float gatherLight, outLight;
4188 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4196 if ( trace == NULL || trace->cluster < 0 ) {
4202 dd = floodLightDistance;
4203 VectorCopy( trace->normal, normal );
4205 /* check if the normal is aligned to the world-up */
4206 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4207 if ( normal[ 2 ] == 1.0f ) {
4208 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4209 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4211 else if ( normal[ 2 ] == -1.0f ) {
4212 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4213 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4218 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4219 CrossProduct( normal, worldUp, myRt );
4220 VectorNormalize( myRt, myRt );
4221 CrossProduct( myRt, normal, myUp );
4222 VectorNormalize( myUp, myUp );
4225 /* vortex: optimise floodLightLowQuality a bit */
4226 if ( floodLightLowQuality == qtrue ) {
4227 /* iterate through ordered vectors */
4228 for ( i = 0; i < numFloodVectors; i++ )
4229 if ( rand() % 10 != 0 ) {
4235 /* iterate through ordered vectors */
4236 for ( i = 0; i < numFloodVectors; i++ )
4240 /* transform vector into tangent space */
4241 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4242 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4243 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4246 VectorMA( trace->origin, dd, direction, trace->end );
4248 //VectorMA( trace->origin, 1, direction, trace->origin );
4250 SetupTrace( trace );
4251 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4256 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4257 contribution = 1.0f;
4259 else if ( trace->opaque ) {
4260 VectorSubtract( trace->hit, trace->origin, displacement );
4261 d = VectorLength( displacement );
4263 // d=trace->distance;
4264 //if (d>256) gatherDirt+=1;
4265 contribution = d / dd;
4266 if ( contribution > 1 ) {
4267 contribution = 1.0f;
4270 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4273 gatherLight += contribution;
4278 if ( gatherLight <= 0.0f ) {
4287 gatherLight /= ( sub );
4289 outLight = gatherLight;
4290 if ( outLight > 1.0f ) {
4294 /* return to sender */
4299 FloodLightRawLightmap
4300 lighttracer style ambient occlusion light hack.
4301 Kudos to the dirtmapping author for most of this source.
4302 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4303 VorteX: fixed problems with deluxemapping
4306 // floodlight pass on a lightmap
4307 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4308 int i, x, y, *cluster;
4309 float *origin, *normal, *floodlight, floodLightAmount;
4310 surfaceInfo_t *info;
4313 // float samples, average, *floodlight2;
4315 memset( &trace,0,sizeof( trace_t ) );
4318 trace.testOcclusion = qtrue;
4319 trace.forceSunlight = qfalse;
4320 trace.twoSided = qtrue;
4321 trace.recvShadows = lm->recvShadows;
4322 trace.numSurfaces = lm->numLightSurfaces;
4323 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4324 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4325 trace.testAll = qfalse;
4326 trace.distance = 1024;
4328 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4329 //trace.twoSided = qfalse;
4330 for ( i = 0; i < trace.numSurfaces; i++ )
4333 info = &surfaceInfos[ trace.surfaces[ i ] ];
4335 /* check twosidedness */
4336 if ( info->si->twoSided ) {
4337 trace.twoSided = qtrue;
4342 /* gather floodlight */
4343 for ( y = 0; y < lm->sh; y++ )
4345 for ( x = 0; x < lm->sw; x++ )
4348 cluster = SUPER_CLUSTER( x, y );
4349 origin = SUPER_ORIGIN( x, y );
4350 normal = SUPER_NORMAL( x, y );
4351 floodlight = SUPER_FLOODLIGHT( x, y );
4353 /* set default dirt */
4356 /* only look at mapped luxels */
4357 if ( *cluster < 0 ) {
4362 trace.cluster = *cluster;
4363 VectorCopy( origin, trace.origin );
4364 VectorCopy( normal, trace.normal );
4366 /* get floodlight */
4367 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4369 /* add floodlight */
4370 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4371 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4372 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4373 floodlight[3] += floodlightDirectionScale;
4377 /* testing no filtering */
4383 for ( y = 0; y < lm->sh; y++ )
4385 for ( x = 0; x < lm->sw; x++ )
4388 cluster = SUPER_CLUSTER( x, y );
4389 floodlight = SUPER_FLOODLIGHT( x, y );
4391 /* filter dirt by adjacency to unmapped luxels */
4392 average = *floodlight;
4394 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4396 if ( sy < 0 || sy >= lm->sh ) {
4400 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4402 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4406 /* get neighboring luxel */
4407 cluster = SUPER_CLUSTER( sx, sy );
4408 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4409 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4414 average += *floodlight2;
4419 if ( samples <= 0.0f ) {
4425 if ( samples <= 0.0f ) {
4430 *floodlight = average / samples;
4436 void FloodLightRawLightmap( int rawLightmapNum ){
4439 /* bail if this number exceeds the number of raw lightmaps */
4440 if ( rawLightmapNum >= numRawLightmaps ) {
4444 lm = &rawLightmaps[ rawLightmapNum ];
4447 if ( floodlighty && floodlightIntensity ) {
4448 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4452 if ( lm->floodlightIntensity ) {
4453 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4454 numSurfacesFloodlighten += 1;
4458 void FloodlightRawLightmaps(){
4459 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4460 numSurfacesFloodlighten = 0;
4461 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4462 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4466 FloodLightIlluminate()
4467 illuminate floodlight into lightmap luxels
4470 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4471 float *luxel, *floodlight, *deluxel, *normal;
4474 int x, y, lightmapNum;
4476 /* walk lightmaps */
4477 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4480 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4484 /* apply floodlight to each luxel */
4485 for ( y = 0; y < lm->sh; y++ )
4487 for ( x = 0; x < lm->sw; x++ )
4489 /* get floodlight */
4490 floodlight = SUPER_FLOODLIGHT( x, y );
4491 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4496 cluster = SUPER_CLUSTER( x, y );
4498 /* only process mapped luxels */
4499 if ( *cluster < 0 ) {
4503 /* get particulars */
4504 luxel = SUPER_LUXEL( lightmapNum, x, y );
4505 deluxel = SUPER_DELUXEL( x, y );
4507 /* add to lightmap */
4508 luxel[0] += floodlight[0];
4509 luxel[1] += floodlight[1];
4510 luxel[2] += floodlight[2];
4512 if ( luxel[3] == 0 ) {
4516 /* add to deluxemap */
4517 if ( deluxemap && floodlight[3] > 0 ) {
4520 normal = SUPER_NORMAL( x, y );
4521 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4523 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4524 if ( brightness < 0.00390625f ) {
4525 brightness = 0.00390625f;
4528 VectorScale( normal, brightness, lightvector );
4529 VectorAdd( deluxel, lightvector, deluxel );