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 ------------------------------------------------------------------------------- */
45 ydnar: moved to here 2001-02-04
48 void ColorToBytes( const float *color, byte *colorBytes, float scale ){
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
56 if ( scale <= 0.0f ) {
60 /* make a local copy */
61 VectorScale( color, scale, sample );
64 gamma = 1.0f / lightmapGamma;
65 for ( i = 0; i < 3; i++ )
67 /* handle negative light */
68 if ( sample[ i ] < 0.0f ) {
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if ( lightmapExposure == 0 ) {
78 /* clamp with color normalization */
80 if ( sample[ 1 ] > max ) {
83 if ( sample[ 2 ] > max ) {
87 VectorScale( sample, ( 255.0f / max ), sample );
92 inv = 1.f / lightmapExposure;
96 if ( sample[ 1 ] > max ) {
99 if ( sample[ 2 ] > max ) {
103 dif = ( 1 - exp( -max * inv ) ) * 255;
113 for ( i = 0; i < 3; i++ )
120 /* compensate for ingame overbrighting/bitshifting */
121 VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );
124 if ( lightmapsRGB ) {
125 sample[0] = floor( Image_sRGBFloatFromLinearFloat( sample[0] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
126 sample[1] = floor( Image_sRGBFloatFromLinearFloat( sample[1] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
127 sample[2] = floor( Image_sRGBFloatFromLinearFloat( sample[2] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
131 colorBytes[ 0 ] = sample[ 0 ];
132 colorBytes[ 1 ] = sample[ 1 ];
133 colorBytes[ 2 ] = sample[ 2 ];
138 /* -------------------------------------------------------------------------------
140 this section deals with phong shading (normal interpolation across brush faces)
142 ------------------------------------------------------------------------------- */
146 smooths together coincident vertex normals across the bsp
149 #define MAX_SAMPLES 256
150 #define THETA_EPSILON 0.000001
151 #define EQUAL_NORMAL_EPSILON 0.01
153 void SmoothNormals( void ){
154 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
155 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
156 bspDrawSurface_t *ds;
160 vec3_t average, diff;
161 int indexes[ MAX_SAMPLES ];
162 vec3_t votes[ MAX_SAMPLES ];
165 /* allocate shade angle table */
166 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
167 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
169 /* allocate smoothed table */
170 cs = ( numBSPDrawVerts / 8 ) + 1;
171 smoothed = safe_malloc( cs );
172 memset( smoothed, 0, cs );
174 /* set default shade angle */
175 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
178 /* run through every surface and flag verts belonging to non-lightmapped surfaces
179 and set per-vertex smoothing angle */
180 for ( i = 0; i < numBSPDrawSurfaces; i++ )
183 ds = &bspDrawSurfaces[ i ];
185 /* get shader for shade angle */
186 si = surfaceInfos[ i ].si;
187 if ( si->shadeAngleDegrees ) {
188 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
191 shadeAngle = defaultShadeAngle;
193 if ( shadeAngle > maxShadeAngle ) {
194 maxShadeAngle = shadeAngle;
198 for ( j = 0; j < ds->numVerts; j++ )
200 f = ds->firstVert + j;
201 shadeAngles[ f ] = shadeAngle;
202 if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
203 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
207 /* ydnar: optional force-to-trisoup */
208 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
209 ds->surfaceType = MST_TRIANGLE_SOUP;
210 ds->lightmapNum[ 0 ] = -3;
214 /* bail if no surfaces have a shade angle */
215 if ( maxShadeAngle == 0 ) {
223 start = I_FloatTime();
225 /* go through the list of vertexes */
226 for ( i = 0; i < numBSPDrawVerts; i++ )
229 f = 10 * i / numBSPDrawVerts;
232 Sys_Printf( "%i...", f );
235 /* already smoothed? */
236 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
241 VectorClear( average );
245 /* build a table of coincident vertexes */
246 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
248 /* already smoothed? */
249 if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
254 if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
258 /* use smallest shade angle */
259 shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
261 /* check shade angle */
262 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
266 else if ( dot < -1.0 ) {
269 testAngle = acos( dot ) + THETA_EPSILON;
270 if ( testAngle >= shadeAngle ) {
271 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
274 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
276 /* add to the list */
277 indexes[ numVerts++ ] = j;
280 smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
282 /* see if this normal has already been voted */
283 for ( k = 0; k < numVotes; k++ )
285 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
286 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
287 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
288 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
293 /* add a new vote? */
294 if ( k == numVotes && numVotes < MAX_SAMPLES ) {
295 VectorAdd( average, bspDrawVerts[ j ].normal, average );
296 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
301 /* don't average for less than 2 verts */
302 if ( numVerts < 2 ) {
307 if ( VectorNormalize( average, average ) > 0 ) {
309 for ( j = 0; j < numVerts; j++ )
310 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
314 /* free the tables */
319 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
324 /* -------------------------------------------------------------------------------
326 this section deals with phong shaded lightmap tracing
328 ------------------------------------------------------------------------------- */
330 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
334 calculates the st tangent vectors for normalmapping
337 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
343 /* calculate barycentric basis for the triangle */
344 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 ] );
345 if ( fabs( bb ) < 0.00000001f ) {
350 for ( i = 0; i < numVerts; i++ )
352 /* calculate s tangent vector */
353 s = dv[ i ]->st[ 0 ] + 10.0f;
354 t = dv[ i ]->st[ 1 ];
355 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
356 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
357 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
359 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
360 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
361 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
363 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
364 VectorNormalize( stv[ i ], stv[ i ] );
366 /* calculate t tangent vector */
367 s = dv[ i ]->st[ 0 ];
368 t = dv[ i ]->st[ 1 ] + 10.0f;
369 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
370 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
371 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
373 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
374 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
375 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
377 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
378 VectorNormalize( ttv[ i ], ttv[ i ] );
381 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
382 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
385 /* return to caller */
394 perterbs the normal by the shader's normalmap in tangent space
397 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
403 VectorCopy( dv->normal, pNormal );
405 /* sample normalmap */
406 if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
410 /* remap sampled normal from [0,255] to [-1,-1] */
411 for ( i = 0; i < 3; i++ )
412 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
414 /* scale tangent vectors and add to original normal */
415 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
416 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
417 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
419 /* renormalize and return */
420 VectorNormalize( pNormal, pNormal );
427 maps a luxel for triangle bv at
431 #define BOGUS_NUDGE -99999.0f
433 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 ] ){
434 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
435 float *luxel, *origin, *normal, d, lightmapSampleOffset;
442 vec4_t sideplane, hostplane;
447 static float nudges[][ 2 ] =
449 //%{ 0, 0 }, /* try center first */
450 { -NUDGE, 0 }, /* left */
451 { NUDGE, 0 }, /* right */
452 { 0, NUDGE }, /* up */
453 { 0, -NUDGE }, /* down */
454 { -NUDGE, NUDGE }, /* left/up */
455 { NUDGE, -NUDGE }, /* right/down */
456 { NUDGE, NUDGE }, /* right/up */
457 { -NUDGE, -NUDGE }, /* left/down */
458 { BOGUS_NUDGE, BOGUS_NUDGE }
462 /* find luxel xy coords (fixme: subtract 0.5?) */
463 x = dv->lightmap[ 0 ][ 0 ];
464 y = dv->lightmap[ 0 ][ 1 ];
468 else if ( x >= lm->sw ) {
474 else if ( y >= lm->sh ) {
478 /* set shader and cluster list */
479 if ( info != NULL ) {
481 numClusters = info->numSurfaceClusters;
482 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
491 /* get luxel, origin, cluster, and normal */
492 luxel = SUPER_LUXEL( 0, x, y );
493 origin = SUPER_ORIGIN( x, y );
494 normal = SUPER_NORMAL( x, y );
495 cluster = SUPER_CLUSTER( x, y );
497 /* don't attempt to remap occluded luxels for planar surfaces */
498 if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
502 /* only average the normal for premapped luxels */
503 else if ( ( *cluster ) >= 0 ) {
504 /* do bumpmap calculations */
506 PerturbNormal( dv, si, pNormal, stv, ttv );
509 VectorCopy( dv->normal, pNormal );
512 /* add the additional normal data */
513 VectorAdd( normal, pNormal, normal );
518 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
522 /* axial lightmap projection */
523 if ( lm->vecs != NULL ) {
524 /* calculate an origin for the sample from the lightmap vectors */
525 VectorCopy( lm->origin, origin );
526 for ( i = 0; i < 3; i++ )
528 /* add unless it's the axis, which is taken care of later */
529 if ( i == lm->axisNum ) {
532 origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
535 /* project the origin onto the plane */
536 d = DotProduct( origin, plane ) - plane[ 3 ];
537 d /= plane[ lm->axisNum ];
538 origin[ lm->axisNum ] -= d;
541 /* non axial lightmap projection (explicit xyz) */
543 VectorCopy( dv->xyz, origin );
546 //////////////////////
547 //27's test to make sure samples stay within the triangle boundaries
548 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
549 //2) if it does, nudge it onto the correct side.
551 if ( worldverts != NULL && lightmapTriangleCheck ) {
552 for ( j = 0; j < 3; j++ )
554 VectorCopy( worldverts[j],cverts[j] );
556 PlaneFromPoints( hostplane,cverts[0],cverts[1],cverts[2] );
558 for ( j = 0; j < 3; j++ )
560 for ( i = 0; i < 3; i++ )
562 //build plane using 2 edges and a normal
563 next = ( i + 1 ) % 3;
565 VectorCopy( cverts[next],temp );
566 VectorAdd( temp,hostplane,temp );
567 PlaneFromPoints( sideplane,cverts[i],cverts[ next ], temp );
569 //planetest sample point
570 e = DotProduct( origin,sideplane );
571 e = e - sideplane[3];
574 //VectorClear(origin);
575 //Move the sample point back inside triangle bounds
576 origin[0] -= sideplane[0] * ( e + 1 );
577 origin[1] -= sideplane[1] * ( e + 1 );
578 origin[2] -= sideplane[2] * ( e + 1 );
580 VectorClear( origin );
587 ////////////////////////
589 /* planar surfaces have precalculated lightmap vectors for nudging */
590 if ( lm->plane != NULL ) {
591 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
592 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
593 VectorCopy( lm->plane, vecs[ 2 ] );
596 /* non-planar surfaces must calculate them */
599 if ( plane != NULL ) {
600 VectorCopy( plane, vecs[ 2 ] );
603 VectorCopy( dv->normal, vecs[ 2 ] );
605 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
608 /* push the origin off the surface a bit */
610 lightmapSampleOffset = si->lightmapSampleOffset;
613 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
615 if ( lm->axisNum < 0 ) {
616 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
618 else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
619 origin[ lm->axisNum ] -= lightmapSampleOffset;
622 origin[ lm->axisNum ] += lightmapSampleOffset;
625 VectorCopy( origin,origintwo );
626 if ( lightmapExtraVisClusterNudge ) {
627 origintwo[0] += vecs[2][0];
628 origintwo[1] += vecs[2][1];
629 origintwo[2] += vecs[2][2];
633 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
635 /* another retarded hack, storing nudge count in luxel[ 1 ] */
638 /* point in solid? (except in dark mode) */
639 if ( pointCluster < 0 && dark == qfalse ) {
640 /* nudge the the location around */
642 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
644 /* nudge the vector around a bit */
645 for ( i = 0; i < 3; i++ )
647 /* set nudged point*/
648 nudged[ i ] = origintwo[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
652 /* get pvs cluster */
653 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
654 if ( pointCluster >= 0 ) {
655 VectorCopy( nudged, origin );
661 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
662 if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
663 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
664 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
665 if ( pointCluster >= 0 ) {
666 VectorCopy( nudged, origin );
672 if ( pointCluster < 0 ) {
673 ( *cluster ) = CLUSTER_OCCLUDED;
674 VectorClear( origin );
675 VectorClear( normal );
681 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
683 /* do bumpmap calculations */
685 PerturbNormal( dv, si, pNormal, stv, ttv );
688 VectorCopy( dv->normal, pNormal );
691 /* store the cluster and normal */
692 ( *cluster ) = pointCluster;
693 VectorCopy( pNormal, normal );
695 /* store explicit mapping pass and implicit mapping pass */
710 recursively subdivides a triangle until its edges are shorter
711 than the distance between two luxels (thanks jc :)
714 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 ] ){
715 bspDrawVert_t mid, *dv2[ 3 ];
719 /* map the vertexes */
721 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
722 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
723 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
729 float *a, *b, dx, dy, dist, maxDist;
732 /* find the longest edge and split it */
735 for ( i = 0; i < 3; i++ )
738 a = dv[ i ]->lightmap[ 0 ];
739 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
742 dx = a[ 0 ] - b[ 0 ];
743 dy = a[ 1 ] - b[ 1 ];
744 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
747 if ( dist > maxDist ) {
753 /* try to early out */
754 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
759 /* split the longest edge and map it */
760 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
761 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
763 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
764 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
766 /* recurse to first triangle */
767 VectorCopy( dv, dv2 );
769 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
771 /* recurse to second triangle */
772 VectorCopy( dv, dv2 );
773 dv2[ ( max + 1 ) % 3 ] = ∣
774 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
781 seed function for MapTriangle_r()
782 requires a cw ordered triangle
785 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
788 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
789 vec3_t worldverts[ 3 ];
792 /* get plane if possible */
793 if ( lm->plane != NULL ) {
794 VectorCopy( lm->plane, plane );
795 plane[ 3 ] = lm->plane[ 3 ];
798 /* otherwise make one from the points */
799 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
803 /* check to see if we need to calculate texture->world tangent vectors */
804 if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
814 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
815 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
816 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
818 /* map the vertexes */
819 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
820 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
821 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
823 /* 2002-11-20: prefer axial triangle edges */
825 /* subdivide the triangle */
826 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
830 for ( i = 0; i < 3; i++ )
833 bspDrawVert_t *dv2[ 3 ];
837 a = dv[ i ]->lightmap[ 0 ];
838 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
840 /* make degenerate triangles for mapping edges */
841 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
843 dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
844 dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
846 /* map the degenerate triangle */
847 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
858 recursively subdivides a quad until its edges are shorter
859 than the distance between two luxels
862 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 ] ){
863 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
870 float *a, *b, dx, dy, dist, maxDist;
873 /* find the longest edge and split it */
876 for ( i = 0; i < 4; i++ )
879 a = dv[ i ]->lightmap[ 0 ];
880 b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
883 dx = a[ 0 ] - b[ 0 ];
884 dy = a[ 1 ] - b[ 1 ];
885 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
888 if ( dist > maxDist ) {
894 /* try to early out */
895 if ( max < 0 || maxDist <= subdivideThreshold ) {
900 /* we only care about even/odd edges */
903 /* split the longest edges */
904 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
905 LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
907 /* map the vertexes */
908 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
909 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
913 /* recurse to first quad */
915 dv2[ 1 ] = &mid[ 0 ];
916 dv2[ 2 ] = &mid[ 1 ];
918 MapQuad_r( lm, info, dv2, plane, stv, ttv );
920 /* recurse to second quad */
921 dv2[ 0 ] = &mid[ 0 ];
924 dv2[ 3 ] = &mid[ 1 ];
925 MapQuad_r( lm, info, dv2, plane, stv, ttv );
931 /* recurse to first quad */
934 dv2[ 2 ] = &mid[ 0 ];
935 dv2[ 3 ] = &mid[ 1 ];
936 MapQuad_r( lm, info, dv2, plane, stv, ttv );
938 /* recurse to second quad */
939 dv2[ 0 ] = &mid[ 1 ];
940 dv2[ 1 ] = &mid[ 0 ];
943 MapQuad_r( lm, info, dv2, plane, stv, ttv );
951 seed function for MapQuad_r()
952 requires a cw ordered triangle quad
955 #define QUAD_PLANAR_EPSILON 0.5f
957 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
960 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
963 /* get plane if possible */
964 if ( lm->plane != NULL ) {
965 VectorCopy( lm->plane, plane );
966 plane[ 3 ] = lm->plane[ 3 ];
969 /* otherwise make one from the points */
970 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
974 /* 4th point must fall on the plane */
975 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
976 if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
980 /* check to see if we need to calculate texture->world tangent vectors */
981 if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
991 /* map the vertexes */
992 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
993 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
994 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
995 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
997 /* subdivide the quad */
998 MapQuad_r( lm, info, dv, plane, stv, ttv );
1006 maps the locations, normals, and pvs clusters for a raw lightmap
1009 #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)
1011 void MapRawLightmap( int rawLightmapNum ){
1012 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1013 float *luxel, *origin, *normal, samples, radius, pass;
1015 bspDrawSurface_t *ds;
1016 surfaceInfo_t *info;
1017 mesh_t src, *subdivided, *mesh;
1018 bspDrawVert_t *verts, *dv[ 4 ], fake;
1021 /* bail if this number exceeds the number of raw lightmaps */
1022 if ( rawLightmapNum >= numRawLightmaps ) {
1027 lm = &rawLightmaps[ rawLightmapNum ];
1029 /* -----------------------------------------------------------------
1030 map referenced surfaces onto the raw lightmap
1031 ----------------------------------------------------------------- */
1033 /* walk the list of surfaces on this raw lightmap */
1034 for ( n = 0; n < lm->numLightSurfaces; n++ )
1036 /* with > 1 surface per raw lightmap, clear occluded */
1038 for ( y = 0; y < lm->sh; y++ )
1040 for ( x = 0; x < lm->sw; x++ )
1043 cluster = SUPER_CLUSTER( x, y );
1044 if ( *cluster < 0 ) {
1045 *cluster = CLUSTER_UNMAPPED;
1052 num = lightSurfaces[ lm->firstLightSurface + n ];
1053 ds = &bspDrawSurfaces[ num ];
1054 info = &surfaceInfos[ num ];
1056 /* bail if no lightmap to calculate */
1057 if ( info->lm != lm ) {
1062 /* map the surface onto the lightmap origin/cluster/normal buffers */
1063 switch ( ds->surfaceType )
1067 verts = yDrawVerts + ds->firstVert;
1069 /* map the triangles */
1070 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1072 for ( i = 0; i < ds->numIndexes; i += 3 )
1074 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1075 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1076 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1077 MapTriangle( lm, info, dv, mapNonAxial );
1083 /* make a mesh from the drawsurf */
1084 src.width = ds->patchWidth;
1085 src.height = ds->patchHeight;
1086 src.verts = &yDrawVerts[ ds->firstVert ];
1087 //% subdivided = SubdivideMesh( src, 8, 512 );
1088 subdivided = SubdivideMesh2( src, info->patchIterations );
1090 /* fit it to the curve and remove colinear verts on rows/columns */
1091 PutMeshOnCurve( *subdivided );
1092 mesh = RemoveLinearMeshColumnsRows( subdivided );
1093 FreeMesh( subdivided );
1096 verts = mesh->verts;
1101 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1102 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1103 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1104 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1108 /* map the mesh quads */
1111 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1113 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1115 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1118 pw[ 0 ] = x + ( y * mesh->width );
1119 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1120 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1121 pw[ 3 ] = x + 1 + ( y * mesh->width );
1122 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1127 /* get drawverts and map first triangle */
1128 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1129 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1130 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1131 MapTriangle( lm, info, dv, mapNonAxial );
1133 /* get drawverts and map second triangle */
1134 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1135 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1136 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1137 MapTriangle( lm, info, dv, mapNonAxial );
1144 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1146 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1149 pw[ 0 ] = x + ( y * mesh->width );
1150 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1151 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1152 pw[ 3 ] = x + 1 + ( y * mesh->width );
1158 /* attempt to map quad first */
1159 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1160 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1161 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1162 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1163 if ( MapQuad( lm, info, dv ) ) {
1167 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1169 /* get drawverts and map first triangle */
1170 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1171 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1172 MapTriangle( lm, info, dv, mapNonAxial );
1174 /* get drawverts and map second triangle */
1175 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1176 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1177 MapTriangle( lm, info, dv, mapNonAxial );
1193 /* -----------------------------------------------------------------
1194 average and clean up luxel normals
1195 ----------------------------------------------------------------- */
1197 /* walk the luxels */
1198 for ( y = 0; y < lm->sh; y++ )
1200 for ( x = 0; x < lm->sw; x++ )
1203 luxel = SUPER_LUXEL( 0, x, y );
1204 normal = SUPER_NORMAL( x, y );
1205 cluster = SUPER_CLUSTER( x, y );
1207 /* only look at mapped luxels */
1208 if ( *cluster < 0 ) {
1212 /* the normal data could be the sum of multiple samples */
1213 if ( luxel[ 3 ] > 1.0f ) {
1214 VectorNormalize( normal, normal );
1217 /* mark this luxel as having only one normal */
1222 /* non-planar surfaces stop here */
1223 if ( lm->plane == NULL ) {
1227 /* -----------------------------------------------------------------
1228 map occluded or unuxed luxels
1229 ----------------------------------------------------------------- */
1231 /* walk the luxels */
1232 radius = floor( superSample / 2 );
1233 radius = radius > 0 ? radius : 1.0f;
1235 for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1237 for ( y = 0; y < lm->sh; y++ )
1239 for ( x = 0; x < lm->sw; x++ )
1242 luxel = SUPER_LUXEL( 0, x, y );
1243 normal = SUPER_NORMAL( x, y );
1244 cluster = SUPER_CLUSTER( x, y );
1246 /* only look at unmapped luxels */
1247 if ( *cluster != CLUSTER_UNMAPPED ) {
1251 /* divine a normal and origin from neighboring luxels */
1252 VectorClear( fake.xyz );
1253 VectorClear( fake.normal );
1254 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1255 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1257 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1259 if ( sy < 0 || sy >= lm->sh ) {
1263 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1265 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1269 /* get neighboring luxel */
1270 luxel = SUPER_LUXEL( 0, sx, sy );
1271 origin = SUPER_ORIGIN( sx, sy );
1272 normal = SUPER_NORMAL( sx, sy );
1273 cluster = SUPER_CLUSTER( sx, sy );
1275 /* only consider luxels mapped in previous passes */
1276 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1280 /* add its distinctiveness to our own */
1281 VectorAdd( fake.xyz, origin, fake.xyz );
1282 VectorAdd( fake.normal, normal, fake.normal );
1283 samples += luxel[ 3 ];
1288 if ( samples == 0.0f ) {
1293 VectorDivide( fake.xyz, samples, fake.xyz );
1294 //% VectorDivide( fake.normal, samples, fake.normal );
1295 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1299 /* map the fake vert */
1300 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1305 /* -----------------------------------------------------------------
1306 average and clean up luxel normals
1307 ----------------------------------------------------------------- */
1309 /* walk the luxels */
1310 for ( y = 0; y < lm->sh; y++ )
1312 for ( x = 0; x < lm->sw; x++ )
1315 luxel = SUPER_LUXEL( 0, x, y );
1316 normal = SUPER_NORMAL( x, y );
1317 cluster = SUPER_CLUSTER( x, y );
1319 /* only look at mapped luxels */
1320 if ( *cluster < 0 ) {
1324 /* the normal data could be the sum of multiple samples */
1325 if ( luxel[ 3 ] > 1.0f ) {
1326 VectorNormalize( normal, normal );
1329 /* mark this luxel as having only one normal */
1337 for ( y = 0; y < lm->sh; y++ )
1339 for ( x = 0; x < lm->sw; x++ )
1344 cluster = SUPER_CLUSTER( x, y );
1345 origin = SUPER_ORIGIN( x, y );
1346 normal = SUPER_NORMAL( x, y );
1347 luxel = SUPER_LUXEL( x, y );
1349 if ( *cluster < 0 ) {
1353 /* check if within the bounding boxes of all surfaces referenced */
1354 ClearBounds( mins, maxs );
1355 for ( n = 0; n < lm->numLightSurfaces; n++ )
1358 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1359 TOL = info->sampleSize + 2;
1360 AddPointToBounds( info->mins, mins, maxs );
1361 AddPointToBounds( info->maxs, mins, maxs );
1362 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1363 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1364 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1370 if ( n < lm->numLightSurfaces ) {
1374 /* report bogus origin */
1375 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",
1376 rawLightmapNum, x, y, *cluster,
1377 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1378 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1379 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1390 sets up dirtmap (ambient occlusion)
1393 #define DIRT_CONE_ANGLE 88 /* degrees */
1394 #define DIRT_NUM_ANGLE_STEPS 16
1395 #define DIRT_NUM_ELEVATION_STEPS 3
1396 #define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1398 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1399 static int numDirtVectors = 0;
1401 void SetupDirt( void ){
1403 float angle, elevation, angleStep, elevationStep;
1407 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1409 /* calculate angular steps */
1410 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1411 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1415 for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1417 /* iterate elevation */
1418 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1420 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1421 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1422 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1427 /* emit some statistics */
1428 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1434 calculates dirt value for a given sample
1437 float DirtForSample( trace_t *trace ){
1439 float gatherDirt, outDirt, angle, elevation, ooDepth;
1440 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1447 if ( trace == NULL || trace->cluster < 0 ) {
1453 ooDepth = 1.0f / dirtDepth;
1454 VectorCopy( trace->normal, normal );
1456 /* check if the normal is aligned to the world-up */
1457 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
1458 if ( normal[ 2 ] == 1.0f ) {
1459 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1460 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1462 else if ( normal[ 2 ] == -1.0f ) {
1463 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1464 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1469 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1470 CrossProduct( normal, worldUp, myRt );
1471 VectorNormalize( myRt, myRt );
1472 CrossProduct( myRt, normal, myUp );
1473 VectorNormalize( myUp, myUp );
1476 /* 1 = random mode, 0 (well everything else) = non-random mode */
1477 if ( dirtMode == 1 ) {
1479 for ( i = 0; i < numDirtVectors; i++ )
1481 /* get random vector */
1482 angle = Random() * DEG2RAD( 360.0f );
1483 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1484 temp[ 0 ] = cos( angle ) * sin( elevation );
1485 temp[ 1 ] = sin( angle ) * sin( elevation );
1486 temp[ 2 ] = cos( elevation );
1488 /* transform into tangent space */
1489 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1490 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1491 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1494 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1495 SetupTrace( trace );
1496 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1500 if ( trace->opaque && !( trace->compileFlags & C_SKY ) ) {
1501 VectorSubtract( trace->hit, trace->origin, displacement );
1502 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1508 /* iterate through ordered vectors */
1509 for ( i = 0; i < numDirtVectors; i++ )
1511 /* transform vector into tangent space */
1512 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1513 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1514 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1517 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1518 SetupTrace( trace );
1519 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1523 if ( trace->opaque ) {
1524 VectorSubtract( trace->hit, trace->origin, displacement );
1525 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1531 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1532 SetupTrace( trace );
1533 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1537 if ( trace->opaque ) {
1538 VectorSubtract( trace->hit, trace->origin, displacement );
1539 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1543 if ( gatherDirt <= 0.0f ) {
1547 /* apply gain (does this even do much? heh) */
1548 outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1549 if ( outDirt > 1.0f ) {
1554 outDirt *= dirtScale;
1555 if ( outDirt > 1.0f ) {
1559 /* return to sender */
1560 return 1.0f - outDirt;
1567 calculates dirty fraction for each luxel
1570 void DirtyRawLightmap( int rawLightmapNum ){
1571 int i, x, y, sx, sy, *cluster;
1572 float *origin, *normal, *dirt, *dirt2, average, samples;
1574 surfaceInfo_t *info;
1579 /* bail if this number exceeds the number of raw lightmaps */
1580 if ( rawLightmapNum >= numRawLightmaps ) {
1585 lm = &rawLightmaps[ rawLightmapNum ];
1588 trace.testOcclusion = qtrue;
1589 trace.forceSunlight = qfalse;
1590 trace.recvShadows = lm->recvShadows;
1591 trace.numSurfaces = lm->numLightSurfaces;
1592 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1593 trace.inhibitRadius = 0.0f;
1594 trace.testAll = qfalse;
1596 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1597 trace.twoSided = qfalse;
1598 for ( i = 0; i < trace.numSurfaces; i++ )
1601 info = &surfaceInfos[ trace.surfaces[ i ] ];
1603 /* check twosidedness */
1604 if ( info->si->twoSided ) {
1605 trace.twoSided = qtrue;
1611 for ( i = 0; i < trace.numSurfaces; i++ )
1614 info = &surfaceInfos[ trace.surfaces[ i ] ];
1616 /* check twosidedness */
1617 if ( info->si->noDirty ) {
1624 for ( y = 0; y < lm->sh; y++ )
1626 for ( x = 0; x < lm->sw; x++ )
1629 cluster = SUPER_CLUSTER( x, y );
1630 origin = SUPER_ORIGIN( x, y );
1631 normal = SUPER_NORMAL( x, y );
1632 dirt = SUPER_DIRT( x, y );
1634 /* set default dirt */
1637 /* only look at mapped luxels */
1638 if ( *cluster < 0 ) {
1642 /* don't apply dirty on this surface */
1649 trace.cluster = *cluster;
1650 VectorCopy( origin, trace.origin );
1651 VectorCopy( normal, trace.normal );
1654 *dirt = DirtForSample( &trace );
1658 /* testing no filtering */
1662 for ( y = 0; y < lm->sh; y++ )
1664 for ( x = 0; x < lm->sw; x++ )
1667 cluster = SUPER_CLUSTER( x, y );
1668 dirt = SUPER_DIRT( x, y );
1670 /* filter dirt by adjacency to unmapped luxels */
1673 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1675 if ( sy < 0 || sy >= lm->sh ) {
1679 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1681 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1685 /* get neighboring luxel */
1686 cluster = SUPER_CLUSTER( sx, sy );
1687 dirt2 = SUPER_DIRT( sx, sy );
1688 if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1698 if ( samples <= 0.0f ) {
1704 if ( samples <= 0.0f ) {
1709 *dirt = average / samples;
1718 calculates the pvs cluster, origin, normal of a sub-luxel
1721 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1722 int i, *cluster, *cluster2;
1723 float *origin = NULL, *origin2 = NULL, *normal; //% , *normal2;
1724 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1727 /* calulate x vector */
1728 if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1729 cluster = SUPER_CLUSTER( x, y );
1730 origin = SUPER_ORIGIN( x, y );
1731 //% normal = SUPER_NORMAL( x, y );
1732 cluster2 = SUPER_CLUSTER( x + 1, y );
1733 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1734 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1736 else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1737 cluster = SUPER_CLUSTER( x - 1, y );
1738 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1739 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1740 cluster2 = SUPER_CLUSTER( x, y );
1741 origin2 = SUPER_ORIGIN( x, y );
1742 //% normal2 = SUPER_NORMAL( x, y );
1745 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap S vector\n" );
1749 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1750 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1752 /* calulate y vector */
1753 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1754 cluster = SUPER_CLUSTER( x, y );
1755 origin = SUPER_ORIGIN( x, y );
1756 //% normal = SUPER_NORMAL( x, y );
1757 cluster2 = SUPER_CLUSTER( x, y + 1 );
1758 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1759 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1761 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1762 cluster = SUPER_CLUSTER( x, y - 1 );
1763 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1764 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1765 cluster2 = SUPER_CLUSTER( x, y );
1766 origin2 = SUPER_ORIGIN( x, y );
1767 //% normal2 = SUPER_NORMAL( x, y );
1770 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap T vector\n" );
1774 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1776 /* calculate new origin */
1777 for ( i = 0; i < 3; i++ )
1778 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1781 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1782 if ( *sampleCluster < 0 ) {
1786 /* calculate new normal */
1787 normal = SUPER_NORMAL( x, y );
1788 VectorCopy( normal, sampleNormal );
1796 SubsampleRawLuxel_r()
1797 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1800 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1801 int b, samples, mapped, lighted;
1804 vec3_t deluxel[ 3 ];
1805 vec3_t origin[ 4 ], normal[ 4 ];
1806 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1807 vec3_t color, direction = { 0, 0, 0 }, total;
1811 if ( lightLuxel[ 3 ] >= lightSamples ) {
1816 VectorClear( total );
1820 /* make 2x2 subsample stamp */
1821 for ( b = 0; b < 4; b++ )
1824 VectorCopy( sampleOrigin, origin[ b ] );
1826 /* calculate position */
1827 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1833 /* increment sample count */
1834 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1837 trace->cluster = *cluster;
1838 VectorCopy( origin[ b ], trace->origin );
1839 VectorCopy( normal[ b ], trace->normal );
1843 LightContributionToSample( trace );
1844 if ( trace->forceSubsampling > 1.0f ) {
1845 /* alphashadow: we subsample as deep as we can */
1851 /* add to totals (fixme: make contrast function) */
1852 VectorCopy( trace->color, luxel[ b ] );
1853 if ( lightDeluxel ) {
1854 VectorCopy( trace->directionContribution, deluxel[ b ] );
1856 VectorAdd( total, trace->color, total );
1857 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1862 /* subsample further? */
1863 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1864 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1865 lighted != 0 && lighted != mapped ) {
1866 for ( b = 0; b < 4; b++ )
1868 if ( cluster[ b ] < 0 ) {
1871 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1876 //% VectorClear( color );
1878 VectorCopy( lightLuxel, color );
1879 if ( lightDeluxel ) {
1880 VectorCopy( lightDeluxel, direction );
1883 for ( b = 0; b < 4; b++ )
1885 if ( cluster[ b ] < 0 ) {
1888 VectorAdd( color, luxel[ b ], color );
1889 if ( lightDeluxel ) {
1890 VectorAdd( direction, deluxel[ b ], direction );
1896 if ( samples > 0 ) {
1898 color[ 0 ] /= samples;
1899 color[ 1 ] /= samples;
1900 color[ 2 ] /= samples;
1903 VectorCopy( color, lightLuxel );
1904 lightLuxel[ 3 ] += 1.0f;
1906 if ( lightDeluxel ) {
1907 direction[ 0 ] /= samples;
1908 direction[ 1 ] /= samples;
1909 direction[ 2 ] /= samples;
1910 VectorCopy( direction, lightDeluxel );
1915 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1916 static void GaussLikeRandom( float sigma, float *x, float *y ){
1918 r = Random() * 2 * Q_PI;
1919 *x = sigma * 2.73861278752581783822 * cos( r );
1920 *y = sigma * 2.73861278752581783822 * sin( r );
1927 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1930 vec3_t origin, normal;
1931 vec3_t total, totaldirection;
1934 VectorClear( total );
1935 VectorClear( totaldirection );
1937 for ( b = 0; b < lightSamples; ++b )
1940 VectorCopy( sampleOrigin, origin );
1941 GaussLikeRandom( bias, &dx, &dy );
1943 /* calculate position */
1944 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1950 trace->cluster = cluster;
1951 VectorCopy( origin, trace->origin );
1952 VectorCopy( normal, trace->normal );
1954 LightContributionToSample( trace );
1955 VectorAdd( total, trace->color, total );
1956 if ( lightDeluxel ) {
1957 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1964 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1965 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1966 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1968 if ( lightDeluxel ) {
1969 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1970 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1971 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1979 IlluminateRawLightmap()
1980 illuminates the luxels
1983 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
1984 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
1985 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
1987 void IlluminateRawLightmap( int rawLightmapNum ){
1988 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1989 int *cluster, *cluster2, mapped, lighted, totalLighted;
1990 size_t llSize, ldSize;
1992 surfaceInfo_t *info;
1993 qboolean filterColor, filterDir;
1995 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1996 unsigned char *flag;
1997 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
1998 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
1999 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2001 float stackLightLuxels[ STACK_LL_SIZE ];
2004 /* bail if this number exceeds the number of raw lightmaps */
2005 if ( rawLightmapNum >= numRawLightmaps ) {
2010 lm = &rawLightmaps[ rawLightmapNum ];
2013 trace.testOcclusion = !noTrace;
2014 trace.forceSunlight = qfalse;
2015 trace.recvShadows = lm->recvShadows;
2016 trace.numSurfaces = lm->numLightSurfaces;
2017 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2018 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2020 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2021 trace.twoSided = qfalse;
2022 for ( i = 0; i < trace.numSurfaces; i++ )
2025 info = &surfaceInfos[ trace.surfaces[ i ] ];
2027 /* check twosidedness */
2028 if ( info->si->twoSided ) {
2029 trace.twoSided = qtrue;
2034 /* create a culled light list for this raw lightmap */
2035 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2037 /* -----------------------------------------------------------------
2039 ----------------------------------------------------------------- */
2042 numLuxelsIlluminated += ( lm->sw * lm->sh );
2044 /* test debugging state */
2045 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2046 /* debug fill the luxels */
2047 for ( y = 0; y < lm->sh; y++ )
2049 for ( x = 0; x < lm->sw; x++ )
2052 cluster = SUPER_CLUSTER( x, y );
2054 /* only fill mapped luxels */
2055 if ( *cluster < 0 ) {
2059 /* get particulars */
2060 luxel = SUPER_LUXEL( 0, x, y );
2061 origin = SUPER_ORIGIN( x, y );
2062 normal = SUPER_NORMAL( x, y );
2064 /* color the luxel with raw lightmap num? */
2065 if ( debugSurfaces ) {
2066 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2069 /* color the luxel with lightmap axis? */
2070 else if ( debugAxis ) {
2071 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2072 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2073 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2076 /* color the luxel with luxel cluster? */
2077 else if ( debugCluster ) {
2078 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2081 /* color the luxel with luxel origin? */
2082 else if ( debugOrigin ) {
2083 VectorSubtract( lm->maxs, lm->mins, temp );
2084 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2085 VectorSubtract( origin, lm->mins, temp2 );
2086 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2087 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2088 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2091 /* color the luxel with the normal */
2092 else if ( normalmap ) {
2093 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2094 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2095 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2098 /* otherwise clear it */
2100 VectorClear( luxel );
2110 /* allocate temporary per-light luxel storage */
2111 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2112 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2113 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2114 lightLuxels = stackLightLuxels;
2117 lightLuxels = safe_malloc( llSize );
2120 lightDeluxels = safe_malloc( ldSize );
2123 lightDeluxels = NULL;
2127 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2129 /* set ambient color */
2130 for ( y = 0; y < lm->sh; y++ )
2132 for ( x = 0; x < lm->sw; x++ )
2135 cluster = SUPER_CLUSTER( x, y );
2136 luxel = SUPER_LUXEL( 0, x, y );
2137 normal = SUPER_NORMAL( x, y );
2138 deluxel = SUPER_DELUXEL( x, y );
2140 /* blacken unmapped clusters */
2141 if ( *cluster < 0 ) {
2142 VectorClear( luxel );
2148 VectorCopy( ambientColor, luxel );
2150 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2152 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2153 if ( brightness < 0.00390625f ) {
2154 brightness = 0.00390625f;
2157 VectorScale( normal, brightness, deluxel );
2164 /* clear styled lightmaps */
2165 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2166 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2168 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2169 memset( lm->superLuxels[ lightmapNum ], 0, size );
2173 /* debugging code */
2174 //% if( trace.numLights <= 0 )
2175 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2177 /* walk light list */
2178 for ( i = 0; i < trace.numLights; i++ )
2181 trace.light = trace.lights[ i ];
2184 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2186 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2187 lm->styles[ lightmapNum ] == LS_NONE ) {
2192 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2193 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2194 Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2199 memset( lightLuxels, 0, llSize );
2201 memset( lightDeluxels, 0, ldSize );
2205 /* determine filter radius */
2206 filterRadius = lm->filterRadius > trace.light->filterRadius
2208 : trace.light->filterRadius;
2209 if ( filterRadius < 0.0f ) {
2210 filterRadius = 0.0f;
2213 /* set luxel filter radius */
2214 luxelFilterRadius = lm->sampleSize != 0 ? superSample * filterRadius / lm->sampleSize : 0;
2215 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2216 luxelFilterRadius = 1;
2219 /* allocate sampling flags storage */
2220 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2221 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2222 if ( lm->superFlags == NULL ) {
2223 lm->superFlags = safe_malloc( size );
2225 memset( (void *) lm->superFlags, 0, size );
2228 /* initial pass, one sample per luxel */
2229 for ( y = 0; y < lm->sh; y++ )
2231 for ( x = 0; x < lm->sw; x++ )
2234 cluster = SUPER_CLUSTER( x, y );
2235 if ( *cluster < 0 ) {
2239 /* get particulars */
2240 lightLuxel = LIGHT_LUXEL( x, y );
2241 lightDeluxel = LIGHT_DELUXEL( x, y );
2242 origin = SUPER_ORIGIN( x, y );
2243 normal = SUPER_NORMAL( x, y );
2244 flag = SUPER_FLAG( x, y );
2246 /* set contribution count */
2247 lightLuxel[ 3 ] = 1.0f;
2250 trace.cluster = *cluster;
2251 VectorCopy( origin, trace.origin );
2252 VectorCopy( normal, trace.normal );
2254 /* get light for this sample */
2255 LightContributionToSample( &trace );
2256 VectorCopy( trace.color, lightLuxel );
2258 /* add the contribution to the deluxemap */
2260 VectorCopy( trace.directionContribution, lightDeluxel );
2263 /* check for evilness */
2264 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2266 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2269 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2275 /* don't even bother with everything else if nothing was lit */
2276 if ( totalLighted == 0 ) {
2280 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2281 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2282 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2284 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2286 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2291 VectorClear( total );
2293 /* test 2x2 stamp */
2294 for ( t = 0; t < 4; t++ )
2296 /* set sample coords */
2297 sx = x + tests[ t ][ 0 ];
2298 sy = y + tests[ t ][ 1 ];
2301 cluster = SUPER_CLUSTER( sx, sy );
2302 if ( *cluster < 0 ) {
2308 flag = SUPER_FLAG( sx, sy );
2309 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2310 /* force a lighted/mapped discrepancy so we subsample */
2315 lightLuxel = LIGHT_LUXEL( sx, sy );
2316 VectorAdd( total, lightLuxel, total );
2317 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2322 /* if total color is under a certain amount, then don't bother subsampling */
2323 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2327 /* if all 4 pixels are either in shadow or light, then don't subsample */
2328 if ( lighted != 0 && lighted != mapped ) {
2329 for ( t = 0; t < 4; t++ )
2331 /* set sample coords */
2332 sx = x + tests[ t ][ 0 ];
2333 sy = y + tests[ t ][ 1 ];
2336 cluster = SUPER_CLUSTER( sx, sy );
2337 if ( *cluster < 0 ) {
2340 flag = SUPER_FLAG( sx, sy );
2341 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2344 lightLuxel = LIGHT_LUXEL( sx, sy );
2345 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2346 origin = SUPER_ORIGIN( sx, sy );
2348 /* only subsample shadowed luxels */
2349 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2353 if ( lightRandomSamples ) {
2354 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2357 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2360 *flag |= FLAG_ALREADY_SUBSAMPLED;
2362 /* debug code to colorize subsampled areas to yellow */
2363 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2364 //% VectorSet( luxel, 255, 204, 0 );
2371 /* tertiary pass, apply dirt map (ambient occlusion) */
2374 for ( y = 0; y < lm->sh; y++ )
2376 for ( x = 0; x < lm->sw; x++ )
2379 cluster = SUPER_CLUSTER( x, y );
2380 if ( *cluster < 0 ) {
2384 /* get particulars */
2385 lightLuxel = LIGHT_LUXEL( x, y );
2386 dirt = SUPER_DIRT( x, y );
2388 /* scale light value */
2389 VectorScale( lightLuxel, *dirt, lightLuxel );
2394 /* allocate sampling lightmap storage */
2395 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2396 /* allocate sampling lightmap storage */
2397 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2398 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2399 memset( lm->superLuxels[ lightmapNum ], 0, size );
2403 if ( lightmapNum > 0 ) {
2404 lm->styles[ lightmapNum ] = trace.light->style;
2405 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2408 /* copy to permanent luxels */
2409 for ( y = 0; y < lm->sh; y++ )
2411 for ( x = 0; x < lm->sw; x++ )
2413 /* get cluster and origin */
2414 cluster = SUPER_CLUSTER( x, y );
2415 if ( *cluster < 0 ) {
2418 origin = SUPER_ORIGIN( x, y );
2421 if ( luxelFilterRadius ) {
2423 VectorClear( averageColor );
2424 VectorClear( averageDir );
2427 /* cheaper distance-based filtering */
2428 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2430 if ( sy < 0 || sy >= lm->sh ) {
2434 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2436 if ( sx < 0 || sx >= lm->sw ) {
2440 /* get particulars */
2441 cluster = SUPER_CLUSTER( sx, sy );
2442 if ( *cluster < 0 ) {
2445 lightLuxel = LIGHT_LUXEL( sx, sy );
2446 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2449 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2450 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2452 /* scale luxel by filter weight */
2453 VectorScale( lightLuxel, weight, color );
2454 VectorAdd( averageColor, color, averageColor );
2456 VectorScale( lightDeluxel, weight, direction );
2457 VectorAdd( averageDir, direction, averageDir );
2464 if ( samples <= 0.0f ) {
2468 /* scale into luxel */
2469 luxel = SUPER_LUXEL( lightmapNum, x, y );
2472 /* handle negative light */
2473 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2474 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2475 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2476 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2479 /* handle normal light */
2482 luxel[ 0 ] += averageColor[ 0 ] / samples;
2483 luxel[ 1 ] += averageColor[ 1 ] / samples;
2484 luxel[ 2 ] += averageColor[ 2 ] / samples;
2488 /* scale into luxel */
2489 deluxel = SUPER_DELUXEL( x, y );
2490 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2491 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2492 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2499 /* get particulars */
2500 lightLuxel = LIGHT_LUXEL( x, y );
2501 lightDeluxel = LIGHT_DELUXEL( x, y );
2502 luxel = SUPER_LUXEL( lightmapNum, x, y );
2503 deluxel = SUPER_DELUXEL( x, y );
2505 /* handle negative light */
2506 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2507 VectorScale( averageColor, -1.0f, averageColor );
2513 /* handle negative light */
2514 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2515 VectorSubtract( luxel, lightLuxel, luxel );
2518 /* handle normal light */
2520 VectorAdd( luxel, lightLuxel, luxel );
2524 VectorAdd( deluxel, lightDeluxel, deluxel );
2531 /* free temporary luxels */
2532 if ( lightLuxels != stackLightLuxels ) {
2533 free( lightLuxels );
2537 free( lightDeluxels );
2541 /* free light list */
2542 FreeTraceLights( &trace );
2544 /* floodlight pass */
2545 if ( floodlighty ) {
2546 FloodlightIlluminateLightmap( lm );
2549 if ( debugnormals ) {
2550 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2553 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2557 for ( y = 0; y < lm->sh; y++ )
2559 for ( x = 0; x < lm->sw; x++ )
2562 cluster = SUPER_CLUSTER( x, y );
2563 //% if( *cluster < 0 )
2566 /* get particulars */
2567 luxel = SUPER_LUXEL( lightmapNum, x, y );
2568 normal = SUPER_NORMAL( x, y );
2570 luxel[0] = ( normal[0] * 127 ) + 127;
2571 luxel[1] = ( normal[1] * 127 ) + 127;
2572 luxel[2] = ( normal[2] * 127 ) + 127;
2578 /* -----------------------------------------------------------------
2580 ----------------------------------------------------------------- */
2583 /* walk lightmaps */
2584 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2587 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2591 /* apply dirt to each luxel */
2592 for ( y = 0; y < lm->sh; y++ )
2594 for ( x = 0; x < lm->sw; x++ )
2597 cluster = SUPER_CLUSTER( x, y );
2599 /* get particulars */
2600 luxel = SUPER_LUXEL( lightmapNum, x, y );
2601 dirt = SUPER_DIRT( x, y );
2604 VectorScale( luxel, *dirt, luxel );
2608 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2615 /* -----------------------------------------------------------------
2617 ----------------------------------------------------------------- */
2619 /* walk lightmaps */
2620 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2623 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2627 /* average occluded luxels from neighbors */
2628 for ( y = 0; y < lm->sh; y++ )
2630 for ( x = 0; x < lm->sw; x++ )
2632 /* get particulars */
2633 cluster = SUPER_CLUSTER( x, y );
2634 luxel = SUPER_LUXEL( lightmapNum, x, y );
2635 deluxel = SUPER_DELUXEL( x, y );
2636 normal = SUPER_NORMAL( x, y );
2638 /* determine if filtering is necessary */
2639 filterColor = qfalse;
2641 if ( *cluster < 0 ||
2642 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2643 filterColor = qtrue;
2646 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2650 if ( !filterColor && !filterDir ) {
2654 /* choose seed amount */
2655 VectorClear( averageColor );
2656 VectorClear( averageDir );
2659 /* walk 3x3 matrix */
2660 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2662 if ( sy < 0 || sy >= lm->sh ) {
2666 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2668 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2672 /* get neighbor's particulars */
2673 cluster2 = SUPER_CLUSTER( sx, sy );
2674 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2675 deluxel2 = SUPER_DELUXEL( sx, sy );
2677 /* ignore unmapped/unlit luxels */
2678 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2679 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2683 /* add its distinctiveness to our own */
2684 VectorAdd( averageColor, luxel2, averageColor );
2685 samples += luxel2[ 3 ];
2687 VectorAdd( averageDir, deluxel2, averageDir );
2693 if ( samples <= 0.0f ) {
2697 /* dark lightmap seams */
2699 if ( lightmapNum == 0 ) {
2700 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2706 if ( filterColor ) {
2707 VectorDivide( averageColor, samples, luxel );
2711 VectorDivide( averageDir, samples, deluxel );
2714 /* set cluster to -3 */
2715 if ( *cluster < 0 ) {
2716 *cluster = CLUSTER_FLOODED;
2726 IlluminateVertexes()
2727 light the surface vertexes
2730 #define VERTEX_NUDGE 4.0f
2732 void IlluminateVertexes( int num ){
2733 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2734 int lightmapNum, numAvg;
2735 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2736 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2737 bspDrawSurface_t *ds;
2738 surfaceInfo_t *info;
2740 bspDrawVert_t *verts;
2742 float floodLightAmount;
2746 /* get surface, info, and raw lightmap */
2747 ds = &bspDrawSurfaces[ num ];
2748 info = &surfaceInfos[ num ];
2751 /* -----------------------------------------------------------------
2752 illuminate the vertexes
2753 ----------------------------------------------------------------- */
2755 /* calculate vertex lighting for surfaces without lightmaps */
2756 if ( lm == NULL || cpmaHack ) {
2758 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2759 trace.forceSunlight = info->si->forceSunlight;
2760 trace.recvShadows = info->recvShadows;
2761 trace.numSurfaces = 1;
2762 trace.surfaces = #
2763 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2765 /* twosided lighting */
2766 trace.twoSided = info->si->twoSided;
2768 /* make light list for this surface */
2769 CreateTraceLightsForSurface( num, &trace );
2772 verts = yDrawVerts + ds->firstVert;
2774 memset( avgColors, 0, sizeof( avgColors ) );
2776 /* walk the surface verts */
2777 for ( i = 0; i < ds->numVerts; i++ )
2779 /* get vertex luxel */
2780 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2782 /* color the luxel with raw lightmap num? */
2783 if ( debugSurfaces ) {
2784 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2787 /* color the luxel with luxel origin? */
2788 else if ( debugOrigin ) {
2789 VectorSubtract( info->maxs, info->mins, temp );
2790 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2791 VectorSubtract( origin, lm->mins, temp2 );
2792 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2793 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2794 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2797 /* color the luxel with the normal */
2798 else if ( normalmap ) {
2799 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2800 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2801 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2804 /* illuminate the vertex */
2807 /* clear vertex luxel */
2808 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2810 /* try at initial origin */
2811 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2812 if ( trace.cluster >= 0 ) {
2814 VectorCopy( verts[ i ].xyz, trace.origin );
2815 VectorCopy( verts[ i ].normal, trace.normal );
2818 if ( dirty && !bouncing ) {
2819 dirt = DirtForSample( &trace );
2825 /* jal: floodlight */
2826 floodLightAmount = 0.0f;
2827 VectorClear( floodColor );
2828 if ( floodlighty && !bouncing ) {
2829 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2830 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2834 LightingAtSample( &trace, ds->vertexStyles, colors );
2837 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2840 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2842 /* jal: floodlight */
2843 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2846 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2847 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2848 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2852 /* is this sample bright enough? */
2853 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2854 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2855 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2856 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2857 /* nudge the sample point around a bit */
2858 for ( x = 0; x < 5; x++ )
2860 /* two's complement 0, 1, -1, 2, -2, etc */
2861 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2863 for ( y = 0; y < 5; y++ )
2865 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2867 for ( z = 0; z < 5; z++ )
2869 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2872 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2873 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2874 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2876 /* try at nudged origin */
2877 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2878 if ( trace.cluster < 0 ) {
2883 if ( dirty && !bouncing ) {
2884 dirt = DirtForSample( &trace );
2890 /* jal: floodlight */
2891 floodLightAmount = 0.0f;
2892 VectorClear( floodColor );
2893 if ( floodlighty && !bouncing ) {
2894 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2895 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2899 LightingAtSample( &trace, ds->vertexStyles, colors );
2902 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2905 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2907 /* jal: floodlight */
2908 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2911 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2912 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2915 /* bright enough? */
2916 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2917 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2918 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2919 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2927 /* add to average? */
2928 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2929 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2930 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2931 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2933 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2935 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2936 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2941 /* another happy customer */
2942 numVertsIlluminated++;
2945 /* set average color */
2947 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2948 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2952 VectorCopy( ambientColor, avgColors[ 0 ] );
2955 /* clean up and store vertex color */
2956 for ( i = 0; i < ds->numVerts; i++ )
2958 /* get vertex luxel */
2959 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2961 /* store average in occluded vertexes */
2962 if ( radVertLuxel[ 0 ] < 0.0f ) {
2963 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2965 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2966 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2969 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2974 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2977 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2978 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2981 if ( bouncing || bounce == 0 || !bounceOnly ) {
2982 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2984 if ( !info->si->noVertexLight ) {
2985 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2990 /* free light list */
2991 FreeTraceLights( &trace );
2993 /* return to sender */
2997 /* -----------------------------------------------------------------
2998 reconstitute vertex lighting from the luxels
2999 ----------------------------------------------------------------- */
3001 /* set styles from lightmap */
3002 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3003 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3005 /* get max search radius */
3007 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3009 /* walk the surface verts */
3010 verts = yDrawVerts + ds->firstVert;
3011 for ( i = 0; i < ds->numVerts; i++ )
3013 /* do each lightmap */
3014 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3017 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3021 /* get luxel coords */
3022 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3023 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3027 else if ( x >= lm->sw ) {
3033 else if ( y >= lm->sh ) {
3037 /* get vertex luxels */
3038 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3039 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3041 /* color the luxel with the normal? */
3043 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3044 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3045 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3048 /* color the luxel with surface num? */
3049 else if ( debugSurfaces ) {
3050 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3053 /* divine color from the superluxels */
3056 /* increasing radius */
3057 VectorClear( radVertLuxel );
3059 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3061 /* sample within radius */
3062 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3064 if ( sy < 0 || sy >= lm->sh ) {
3068 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3070 if ( sx < 0 || sx >= lm->sw ) {
3074 /* get luxel particulars */
3075 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3076 cluster = SUPER_CLUSTER( sx, sy );
3077 if ( *cluster < 0 ) {
3081 /* testing: must be brigher than ambient color */
3082 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3085 /* add its distinctiveness to our own */
3086 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3087 samples += luxel[ 3 ];
3093 if ( samples > 0.0f ) {
3094 VectorDivide( radVertLuxel, samples, radVertLuxel );
3097 VectorCopy( ambientColor, radVertLuxel );
3101 /* store into floating point storage */
3102 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3103 numVertsIlluminated++;
3105 /* store into bytes (for vertex approximation) */
3106 if ( !info->si->noVertexLight ) {
3107 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3115 /* -------------------------------------------------------------------------------
3117 light optimization (-fast)
3119 creates a list of lights that will affect a surface and stores it in tw
3120 this is to optimize surface lighting by culling out as many of the
3121 lights in the world as possible from further calculation
3123 ------------------------------------------------------------------------------- */
3127 determines opaque brushes in the world and find sky shaders for sunlight calculations
3130 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3132 unsigned int compileFlags, allCompileFlags;
3135 bspBrushSide_t *side;
3136 bspShader_t *shader;
3141 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3144 if ( opaqueBrushes == NULL ) {
3145 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3149 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3150 numOpaqueBrushes = 0;
3152 /* walk the list of worldspawn brushes */
3153 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3156 b = bspModels[ 0 ].firstBSPBrush + i;
3157 brush = &bspBrushes[ b ];
3159 /* check all sides */
3162 allCompileFlags = ~( 0u );
3163 for ( j = 0; j < brush->numSides && inside; j++ )
3165 /* do bsp shader calculations */
3166 side = &bspBrushSides[ brush->firstSide + j ];
3167 shader = &bspShaders[ side->shaderNum ];
3169 /* get shader info */
3170 si = ShaderInfoForShaderNull( shader->shader );
3175 /* or together compile flags */
3176 compileFlags |= si->compileFlags;
3177 allCompileFlags &= si->compileFlags;
3180 /* determine if this brush is opaque to light */
3181 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3182 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3188 /* emit some statistics */
3189 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3191 void SetupBrushes( void ){
3192 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3199 determines if two clusters are visible to each other using the PVS
3202 qboolean ClusterVisible( int a, int b ){
3208 if ( a < 0 || b < 0 ) {
3218 if ( numBSPVisBytes <= 8 ) {
3223 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3224 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3225 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3228 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3238 borrowed from vlight.c
3241 int PointInLeafNum_r( vec3_t point, int nodenum ){
3248 while ( nodenum >= 0 )
3250 node = &bspNodes[ nodenum ];
3251 plane = &bspPlanes[ node->planeNum ];
3252 dist = DotProduct( point, plane->normal ) - plane->dist;
3254 nodenum = node->children[ 0 ];
3256 else if ( dist < -0.1 ) {
3257 nodenum = node->children[ 1 ];
3261 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3262 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3265 nodenum = node->children[ 1 ];
3269 leafnum = -nodenum - 1;
3277 borrowed from vlight.c
3280 int PointInLeafNum( vec3_t point ){
3281 return PointInLeafNum_r( point, 0 );
3287 ClusterVisibleToPoint() - ydnar
3288 returns qtrue if point can "see" cluster
3291 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3295 /* get leafNum for point */
3296 pointCluster = ClusterForPoint( point );
3297 if ( pointCluster < 0 ) {
3302 return ClusterVisible( pointCluster, cluster );
3308 ClusterForPoint() - ydnar
3309 returns the pvs cluster for point
3312 int ClusterForPoint( vec3_t point ){
3316 /* get leafNum for point */
3317 leafNum = PointInLeafNum( point );
3318 if ( leafNum < 0 ) {
3322 /* return the cluster */
3323 return bspLeafs[ leafNum ].cluster;
3329 ClusterForPointExt() - ydnar
3330 also takes brushes into account for occlusion testing
3333 int ClusterForPointExt( vec3_t point, float epsilon ){
3334 int i, j, b, leafNum, cluster;
3337 int *brushes, numBSPBrushes;
3343 /* get leaf for point */
3344 leafNum = PointInLeafNum( point );
3345 if ( leafNum < 0 ) {
3348 leaf = &bspLeafs[ leafNum ];
3350 /* get the cluster */
3351 cluster = leaf->cluster;
3352 if ( cluster < 0 ) {
3356 /* transparent leaf, so check point against all brushes in the leaf */
3357 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3358 numBSPBrushes = leaf->numBSPLeafBrushes;
3359 for ( i = 0; i < numBSPBrushes; i++ )
3363 if ( b > maxOpaqueBrush ) {
3366 brush = &bspBrushes[ b ];
3367 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3371 /* check point against all planes */
3373 for ( j = 0; j < brush->numSides && inside; j++ )
3375 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3376 dot = DotProduct( point, plane->normal );
3378 if ( dot > epsilon ) {
3383 /* if inside, return bogus cluster */
3389 /* if the point made it this far, it's not inside any opaque brushes */
3396 ClusterForPointExtFilter() - ydnar
3397 adds cluster checking against a list of known valid clusters
3400 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3404 /* get cluster for point */
3405 cluster = ClusterForPointExt( point, epsilon );
3407 /* check if filtering is necessary */
3408 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3413 for ( i = 0; i < numClusters; i++ )
3415 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3427 ShaderForPointInLeaf() - ydnar
3428 checks a point against all brushes in a leaf, returning the shader of the brush
3429 also sets the cumulative surface and content flags for the brush hit
3432 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3436 int *brushes, numBSPBrushes;
3439 bspBrushSide_t *side;
3441 bspShader_t *shader;
3442 int allSurfaceFlags, allContentFlags;
3445 /* clear things out first */
3450 if ( leafNum < 0 ) {
3453 leaf = &bspLeafs[ leafNum ];
3455 /* transparent leaf, so check point against all brushes in the leaf */
3456 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3457 numBSPBrushes = leaf->numBSPLeafBrushes;
3458 for ( i = 0; i < numBSPBrushes; i++ )
3461 brush = &bspBrushes[ brushes[ i ] ];
3463 /* check point against all planes */
3465 allSurfaceFlags = 0;
3466 allContentFlags = 0;
3467 for ( j = 0; j < brush->numSides && inside; j++ )
3469 side = &bspBrushSides[ brush->firstSide + j ];
3470 plane = &bspPlanes[ side->planeNum ];
3471 dot = DotProduct( point, plane->normal );
3473 if ( dot > epsilon ) {
3478 shader = &bspShaders[ side->shaderNum ];
3479 allSurfaceFlags |= shader->surfaceFlags;
3480 allContentFlags |= shader->contentFlags;
3484 /* handle if inside */
3486 /* if there are desired flags, check for same and continue if they aren't matched */
3487 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3490 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3494 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3495 *surfaceFlags = allSurfaceFlags;
3496 *contentFlags = allContentFlags;
3497 return brush->shaderNum;
3501 /* if the point made it this far, it's not inside any brushes */
3509 chops a bounding box by the plane defined by origin and normal
3510 returns qfalse if the bounds is entirely clipped away
3512 this is not exactly the fastest way to do this...
3515 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3516 /* FIXME: rewrite this so it doesn't use bloody brushes */
3524 calculates each light's effective envelope,
3525 taking into account brightness, type, and pvs.
3528 #define LIGHT_EPSILON 0.125f
3529 #define LIGHT_NUDGE 2.0f
3531 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3532 int i, x, y, z, x1, y1, z1;
3533 light_t *light, *light2, **owner;
3535 vec3_t origin, dir, mins, maxs;
3536 float radius, intensity;
3537 light_t *buckets[ 256 ];
3540 /* early out for weird cases where there are no lights */
3541 if ( lights == NULL ) {
3546 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3550 numCulledLights = 0;
3552 while ( *owner != NULL )
3557 /* handle negative lights */
3558 if ( light->photons < 0.0f || light->add < 0.0f ) {
3559 light->photons *= -1.0f;
3560 light->add *= -1.0f;
3561 light->flags |= LIGHT_NEGATIVE;
3565 if ( light->type == EMIT_SUN ) {
3568 light->envelope = MAX_WORLD_COORD * 8.0f;
3569 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3570 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3573 /* everything else */
3576 /* get pvs cluster for light */
3577 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3579 /* invalid cluster? */
3580 if ( light->cluster < 0 ) {
3581 /* nudge the sample point around a bit */
3582 for ( x = 0; x < 4; x++ )
3584 /* two's complement 0, 1, -1, 2, -2, etc */
3585 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3587 for ( y = 0; y < 4; y++ )
3589 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3591 for ( z = 0; z < 4; z++ )
3593 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3596 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3597 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3598 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3600 /* try at nudged origin */
3601 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3602 if ( light->cluster < 0 ) {
3607 VectorCopy( origin, light->origin );
3613 /* only calculate for lights in pvs and outside of opaque brushes */
3614 if ( light->cluster >= 0 ) {
3615 /* set light fast flag */
3617 light->flags |= LIGHT_FAST_TEMP;
3620 light->flags &= ~LIGHT_FAST_TEMP;
3622 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3623 light->flags |= LIGHT_FAST_TEMP;
3625 if ( light->si && light->si->noFast ) {
3626 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3629 /* clear light envelope */
3630 light->envelope = 0;
3632 /* handle area lights */
3633 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3634 light->envelope = MAX_WORLD_COORD * 8.0f;
3636 /* check for fast mode */
3637 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3638 /* ugly hack to calculate extent for area lights, but only done once */
3639 VectorScale( light->normal, -1.0f, dir );
3640 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3644 VectorMA( light->origin, radius, light->normal, origin );
3645 factor = PointToPolygonFormFactor( origin, dir, light->w );
3646 if ( factor < 0.0f ) {
3649 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3650 light->envelope = radius;
3656 intensity = light->photons; /* hopefully not used */
3661 intensity = light->photons;
3665 if ( light->envelope <= 0.0f ) {
3666 /* solve distance for non-distance lights */
3667 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3668 light->envelope = MAX_WORLD_COORD * 8.0f;
3671 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3672 /* solve distance for linear lights */
3673 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3674 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3678 add = angle * light->photons * linearScale - (dist * light->fade);
3679 T = (light->photons * linearScale) - (dist * light->fade);
3680 T + (dist * light->fade) = (light->photons * linearScale);
3681 dist * light->fade = (light->photons * linearScale) - T;
3682 dist = ((light->photons * linearScale) - T) / light->fade;
3685 /* solve for inverse square falloff */
3687 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3691 add = light->photons / (dist * dist);
3692 T = light->photons / (dist * dist);
3693 T * (dist * dist) = light->photons;
3694 dist = sqrt( light->photons / T );
3699 /* solve distance for linear lights */
3700 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3701 light->envelope = ( intensity * linearScale ) / light->fade;
3704 /* can't cull these */
3706 light->envelope = MAX_WORLD_COORD * 8.0f;
3711 /* chop radius against pvs */
3714 ClearBounds( mins, maxs );
3716 /* check all leaves */
3717 for ( i = 0; i < numBSPLeafs; i++ )
3720 leaf = &bspLeafs[ i ];
3723 if ( leaf->cluster < 0 ) {
3726 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3730 /* add this leafs bbox to the bounds */
3731 VectorCopy( leaf->mins, origin );
3732 AddPointToBounds( origin, mins, maxs );
3733 VectorCopy( leaf->maxs, origin );
3734 AddPointToBounds( origin, mins, maxs );
3737 /* test to see if bounds encompass light */
3738 for ( i = 0; i < 3; i++ )
3740 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3741 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3742 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3743 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3744 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3745 AddPointToBounds( light->origin, mins, maxs );
3749 /* chop the bounds by a plane for area lights and spotlights */
3750 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3751 ChopBounds( mins, maxs, light->origin, light->normal );
3755 VectorCopy( mins, light->mins );
3756 VectorCopy( maxs, light->maxs );
3758 /* reflect bounds around light origin */
3759 //% VectorMA( light->origin, -1.0f, origin, origin );
3760 VectorScale( light->origin, 2, origin );
3761 VectorSubtract( origin, maxs, origin );
3762 AddPointToBounds( origin, mins, maxs );
3763 //% VectorMA( light->origin, -1.0f, mins, origin );
3764 VectorScale( light->origin, 2, origin );
3765 VectorSubtract( origin, mins, origin );
3766 AddPointToBounds( origin, mins, maxs );
3768 /* calculate spherical bounds */
3769 VectorSubtract( maxs, light->origin, dir );
3770 radius = (float) VectorLength( dir );
3772 /* if this radius is smaller than the envelope, then set the envelope to it */
3773 if ( radius < light->envelope ) {
3774 light->envelope = radius;
3775 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3778 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3781 /* add grid/surface only check */
3783 if ( !( light->flags & LIGHT_GRID ) ) {
3784 light->envelope = 0.0f;
3789 if ( !( light->flags & LIGHT_SURFACES ) ) {
3790 light->envelope = 0.0f;
3796 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3798 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3800 /* delete the light */
3802 *owner = light->next;
3803 if ( light->w != NULL ) {
3811 /* square envelope */
3812 light->envelope2 = ( light->envelope * light->envelope );
3814 /* increment light count */
3817 /* set next light */
3818 owner = &( ( **owner ).next );
3821 /* bucket sort lights by style */
3822 memset( buckets, 0, sizeof( buckets ) );
3824 for ( light = lights; light != NULL; light = light2 )
3826 /* get next light */
3827 light2 = light->next;
3829 /* filter into correct bucket */
3830 light->next = buckets[ light->style ];
3831 buckets[ light->style ] = light;
3833 /* if any styled light is present, automatically set nocollapse */
3834 if ( light->style != LS_NORMAL ) {
3839 /* filter back into light list */
3841 for ( i = 255; i >= 0; i-- )
3844 for ( light = buckets[ i ]; light != NULL; light = light2 )
3846 light2 = light->next;
3847 light->next = lights;
3852 /* emit some statistics */
3853 Sys_Printf( "%9d total lights\n", numLights );
3854 Sys_Printf( "%9d culled lights\n", numCulledLights );
3860 CreateTraceLightsForBounds()
3861 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3864 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3867 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3868 float radius, dist, length;
3871 /* potential pre-setup */
3872 if ( numLights == 0 ) {
3873 SetupEnvelopes( qfalse, fast );
3877 //% 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 ] );
3879 /* allocate the light list */
3880 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3881 trace->numLights = 0;
3883 /* calculate spherical bounds */
3884 VectorAdd( mins, maxs, origin );
3885 VectorScale( origin, 0.5f, origin );
3886 VectorSubtract( maxs, origin, dir );
3887 radius = (float) VectorLength( dir );
3889 /* get length of normal vector */
3890 if ( normal != NULL ) {
3891 length = VectorLength( normal );
3895 normal = nullVector;
3899 /* test each light and see if it reaches the sphere */
3900 /* note: the attenuation code MUST match LightingAtSample() */
3901 for ( light = lights; light; light = light->next )
3903 /* check zero sized envelope */
3904 if ( light->envelope <= 0 ) {
3905 lightsEnvelopeCulled++;
3910 if ( !( light->flags & flags ) ) {
3914 /* sunlight skips all this nonsense */
3915 if ( light->type != EMIT_SUN ) {
3921 /* check against pvs cluster */
3922 if ( numClusters > 0 && clusters != NULL ) {
3923 for ( i = 0; i < numClusters; i++ )
3925 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3931 if ( i == numClusters ) {
3932 lightsClusterCulled++;
3937 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3938 VectorSubtract( light->origin, origin, dir );
3939 dist = VectorLength( dir );
3940 dist -= light->envelope;
3943 lightsEnvelopeCulled++;
3947 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3950 for ( i = 0; i < 3; i++ )
3952 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3957 lightsBoundsCulled++;
3963 /* planar surfaces (except twosided surfaces) have a couple more checks */
3964 if ( length > 0.0f && trace->twoSided == qfalse ) {
3965 /* lights coplanar with a surface won't light it */
3966 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
3967 lightsPlaneCulled++;
3971 /* check to see if light is behind the plane */
3972 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
3973 lightsPlaneCulled++;
3978 /* add this light */
3979 trace->lights[ trace->numLights++ ] = light;
3982 /* make last night null */
3983 trace->lights[ trace->numLights ] = NULL;
3988 void FreeTraceLights( trace_t *trace ){
3989 if ( trace->lights != NULL ) {
3990 free( trace->lights );
3997 CreateTraceLightsForSurface()
3998 creates a list of lights that can potentially affect a drawsurface
4001 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4003 vec3_t mins, maxs, normal;
4005 bspDrawSurface_t *ds;
4006 surfaceInfo_t *info;
4014 /* get drawsurface and info */
4015 ds = &bspDrawSurfaces[ num ];
4016 info = &surfaceInfos[ num ];
4018 /* get the mins/maxs for the dsurf */
4019 ClearBounds( mins, maxs );
4020 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4021 for ( i = 0; i < ds->numVerts; i++ )
4023 dv = &yDrawVerts[ ds->firstVert + i ];
4024 AddPointToBounds( dv->xyz, mins, maxs );
4025 if ( !VectorCompare( dv->normal, normal ) ) {
4026 VectorClear( normal );
4030 /* create the lights for the bounding box */
4031 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4034 /////////////////////////////////////////////////////////////
4036 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4037 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4038 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4039 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4041 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4042 static int numFloodVectors = 0;
4044 void SetupFloodLight( void ){
4046 float angle, elevation, angleStep, elevationStep;
4048 double v1,v2,v3,v4,v5,v6;
4051 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4053 /* calculate angular steps */
4054 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4055 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4059 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4061 /* iterate elevation */
4062 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4064 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4065 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4066 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4071 /* emit some statistics */
4072 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4075 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4077 if ( value[ 0 ] != '\0' ) {
4079 v4 = floodlightDistance;
4080 v5 = floodlightIntensity;
4081 v6 = floodlightDirectionScale;
4083 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4085 floodlightRGB[0] = v1;
4086 floodlightRGB[1] = v2;
4087 floodlightRGB[2] = v3;
4089 if ( VectorLength( floodlightRGB ) == 0 ) {
4090 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4103 floodlightDistance = v4;
4104 floodlightIntensity = v5;
4105 floodlightDirectionScale = v6;
4107 floodlighty = qtrue;
4108 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4112 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4115 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4116 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4117 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4119 ColorNormalize( floodlightRGB,floodlightRGB );
4123 FloodLightForSample()
4124 calculates floodlight value for a given sample
4125 once again, kudos to the dirtmapping coder
4128 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4133 float gatherLight, outLight;
4134 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4142 if ( trace == NULL || trace->cluster < 0 ) {
4148 dd = floodLightDistance;
4149 VectorCopy( trace->normal, normal );
4151 /* check if the normal is aligned to the world-up */
4152 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4153 if ( normal[ 2 ] == 1.0f ) {
4154 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4155 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4157 else if ( normal[ 2 ] == -1.0f ) {
4158 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4159 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4164 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4165 CrossProduct( normal, worldUp, myRt );
4166 VectorNormalize( myRt, myRt );
4167 CrossProduct( myRt, normal, myUp );
4168 VectorNormalize( myUp, myUp );
4171 /* vortex: optimise floodLightLowQuality a bit */
4172 if ( floodLightLowQuality == qtrue ) {
4173 /* iterate through ordered vectors */
4174 for ( i = 0; i < numFloodVectors; i++ )
4175 if ( rand() % 10 != 0 ) {
4181 /* iterate through ordered vectors */
4182 for ( i = 0; i < numFloodVectors; i++ )
4186 /* transform vector into tangent space */
4187 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4188 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4189 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4192 VectorMA( trace->origin, dd, direction, trace->end );
4194 //VectorMA( trace->origin, 1, direction, trace->origin );
4196 SetupTrace( trace );
4197 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4202 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4203 contribution = 1.0f;
4205 else if ( trace->opaque ) {
4206 VectorSubtract( trace->hit, trace->origin, displacement );
4207 d = VectorLength( displacement );
4209 // d=trace->distance;
4210 //if (d>256) gatherDirt+=1;
4211 contribution = d / dd;
4212 if ( contribution > 1 ) {
4213 contribution = 1.0f;
4216 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4219 gatherLight += contribution;
4224 if ( gatherLight <= 0.0f ) {
4233 gatherLight /= ( sub );
4235 outLight = gatherLight;
4236 if ( outLight > 1.0f ) {
4240 /* return to sender */
4245 FloodLightRawLightmap
4246 lighttracer style ambient occlusion light hack.
4247 Kudos to the dirtmapping author for most of this source.
4248 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4249 VorteX: fixed problems with deluxemapping
4252 // floodlight pass on a lightmap
4253 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4254 int i, x, y, *cluster;
4255 float *origin, *normal, *floodlight, floodLightAmount;
4256 surfaceInfo_t *info;
4259 // float samples, average, *floodlight2;
4261 memset( &trace,0,sizeof( trace_t ) );
4264 trace.testOcclusion = qtrue;
4265 trace.forceSunlight = qfalse;
4266 trace.twoSided = qtrue;
4267 trace.recvShadows = lm->recvShadows;
4268 trace.numSurfaces = lm->numLightSurfaces;
4269 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4270 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4271 trace.testAll = qfalse;
4272 trace.distance = 1024;
4274 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4275 //trace.twoSided = qfalse;
4276 for ( i = 0; i < trace.numSurfaces; i++ )
4279 info = &surfaceInfos[ trace.surfaces[ i ] ];
4281 /* check twosidedness */
4282 if ( info->si->twoSided ) {
4283 trace.twoSided = qtrue;
4288 /* gather floodlight */
4289 for ( y = 0; y < lm->sh; y++ )
4291 for ( x = 0; x < lm->sw; x++ )
4294 cluster = SUPER_CLUSTER( x, y );
4295 origin = SUPER_ORIGIN( x, y );
4296 normal = SUPER_NORMAL( x, y );
4297 floodlight = SUPER_FLOODLIGHT( x, y );
4299 /* set default dirt */
4302 /* only look at mapped luxels */
4303 if ( *cluster < 0 ) {
4308 trace.cluster = *cluster;
4309 VectorCopy( origin, trace.origin );
4310 VectorCopy( normal, trace.normal );
4312 /* get floodlight */
4313 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4315 /* add floodlight */
4316 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4317 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4318 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4319 floodlight[3] += floodlightDirectionScale;
4323 /* testing no filtering */
4329 for ( y = 0; y < lm->sh; y++ )
4331 for ( x = 0; x < lm->sw; x++ )
4334 cluster = SUPER_CLUSTER( x, y );
4335 floodlight = SUPER_FLOODLIGHT( x, y );
4337 /* filter dirt by adjacency to unmapped luxels */
4338 average = *floodlight;
4340 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4342 if ( sy < 0 || sy >= lm->sh ) {
4346 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4348 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4352 /* get neighboring luxel */
4353 cluster = SUPER_CLUSTER( sx, sy );
4354 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4355 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4360 average += *floodlight2;
4365 if ( samples <= 0.0f ) {
4371 if ( samples <= 0.0f ) {
4376 *floodlight = average / samples;
4382 void FloodLightRawLightmap( int rawLightmapNum ){
4385 /* bail if this number exceeds the number of raw lightmaps */
4386 if ( rawLightmapNum >= numRawLightmaps ) {
4390 lm = &rawLightmaps[ rawLightmapNum ];
4393 if ( floodlighty && floodlightIntensity ) {
4394 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4398 if ( lm->floodlightIntensity ) {
4399 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4400 numSurfacesFloodlighten += 1;
4404 void FloodlightRawLightmaps(){
4405 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4406 numSurfacesFloodlighten = 0;
4407 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4408 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4412 FloodLightIlluminate()
4413 illuminate floodlight into lightmap luxels
4416 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4417 float *luxel, *floodlight, *deluxel, *normal;
4420 int x, y, lightmapNum;
4422 /* walk lightmaps */
4423 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4426 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4430 /* apply floodlight to each luxel */
4431 for ( y = 0; y < lm->sh; y++ )
4433 for ( x = 0; x < lm->sw; x++ )
4435 /* get floodlight */
4436 floodlight = SUPER_FLOODLIGHT( x, y );
4437 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4442 cluster = SUPER_CLUSTER( x, y );
4444 /* only process mapped luxels */
4445 if ( *cluster < 0 ) {
4449 /* get particulars */
4450 luxel = SUPER_LUXEL( lightmapNum, x, y );
4451 deluxel = SUPER_DELUXEL( x, y );
4453 /* add to lightmap */
4454 luxel[0] += floodlight[0];
4455 luxel[1] += floodlight[1];
4456 luxel[2] += floodlight[2];
4458 if ( luxel[3] == 0 ) {
4462 /* add to deluxemap */
4463 if ( deluxemap && floodlight[3] > 0 ) {
4466 normal = SUPER_NORMAL( x, y );
4467 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4469 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4470 if ( brightness < 0.00390625f ) {
4471 brightness = 0.00390625f;
4474 VectorScale( normal, brightness, lightvector );
4475 VectorAdd( deluxel, lightvector, deluxel );