-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
-\r
-----------------------------------------------------------------------------------\r
-\r
-This code has been altered significantly from its original form, to support\r
-several games based on the Quake III Arena engine, in the form of "Q3Map2."\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-\r
-\r
-/* marker */\r
-#define LIGHT_YDNAR_C\r
-\r
-\r
-\r
-/* dependencies */\r
-#include "q3map2.h"\r
-\r
-\r
-\r
-\r
-/*\r
-ColorToBytes()\r
-ydnar: moved to here 2001-02-04\r
-*/\r
-\r
-void ColorToBytes( const float *color, byte *colorBytes, float scale )\r
-{\r
- float max;\r
- vec3_t sample;\r
- \r
- \r
- /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */\r
- if( scale <= 0.0f )\r
- scale = 1.0f;\r
- \r
- /* make a local copy */\r
- VectorScale( color, scale, sample );\r
- \r
- /* handle negative light */\r
- if( sample[ 0 ] < 0.0f )\r
- sample[ 0 ] = 0.0f;\r
- if( sample[ 1 ] < 0.0f )\r
- sample[ 1 ] = 0.0f;\r
- if( sample[ 2 ] < 0.0f )\r
- sample[ 2 ] = 0.0f;\r
- \r
- /* clamp with color normalization */\r
- max = sample[ 0 ];\r
- if( sample[ 1 ] > max )\r
- max = sample[ 1 ];\r
- if( sample[ 2 ] > max )\r
- max = sample[ 2 ];\r
- if( max > 255.0f )\r
- VectorScale( sample, (255.0f / max), sample );\r
- \r
- /* store it off */\r
- colorBytes[ 0 ] = sample[ 0 ];\r
- colorBytes[ 1 ] = sample[ 1 ];\r
- colorBytes[ 2 ] = sample[ 2 ];\r
-}\r
-\r
-\r
-\r
-/* -------------------------------------------------------------------------------\r
-\r
-this section deals with phong shading (normal interpolation across brush faces)\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-/*\r
-SmoothNormals()\r
-smooths together coincident vertex normals across the bsp\r
-*/\r
-\r
-#define MAX_SAMPLES 256\r
-#define THETA_EPSILON 0.000001\r
-#define EQUAL_NORMAL_EPSILON 0.01\r
-\r
-void SmoothNormals( void )\r
-{\r
- int i, j, k, f, cs, numVerts, numVotes, fOld, start;\r
- float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;\r
- bspDrawSurface_t *ds;\r
- shaderInfo_t *si;\r
- float *shadeAngles;\r
- byte *smoothed;\r
- vec3_t average, diff;\r
- int indexes[ MAX_SAMPLES ];\r
- vec3_t votes[ MAX_SAMPLES ];\r
- \r
- \r
- /* allocate shade angle table */\r
- shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );\r
- memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );\r
- \r
- /* allocate smoothed table */\r
- cs = (numBSPDrawVerts / 8) + 1;\r
- smoothed = safe_malloc( cs );\r
- memset( smoothed, 0, cs );\r
- \r
- /* set default shade angle */\r
- defaultShadeAngle = DEG2RAD( shadeAngleDegrees );\r
- maxShadeAngle = 0;\r
- \r
- /* run through every surface and flag verts belonging to non-lightmapped surfaces\r
- and set per-vertex smoothing angle */\r
- for( i = 0; i < numBSPDrawSurfaces; i++ )\r
- {\r
- /* get drawsurf */\r
- ds = &bspDrawSurfaces[ i ];\r
- \r
- /* get shader for shade angle */\r
- si = surfaceInfos[ i ].si;\r
- if( si->shadeAngleDegrees )\r
- shadeAngle = DEG2RAD( si->shadeAngleDegrees );\r
- else\r
- shadeAngle = defaultShadeAngle;\r
- if( shadeAngle > maxShadeAngle )\r
- maxShadeAngle = shadeAngle;\r
- \r
- /* flag its verts */\r
- for( j = 0; j < ds->numVerts; j++ )\r
- {\r
- f = ds->firstVert + j;\r
- shadeAngles[ f ] = shadeAngle;\r
- if( ds->surfaceType == MST_TRIANGLE_SOUP )\r
- smoothed[ f >> 3 ] |= (1 << (f & 7));\r
- }\r
- \r
- /* ydnar: optional force-to-trisoup */\r
- if( trisoup && ds->surfaceType == MST_PLANAR )\r
- {\r
- ds->surfaceType = MST_TRIANGLE_SOUP;\r
- ds->lightmapNum[ 0 ] = -3;\r
- }\r
- }\r
- \r
- /* bail if no surfaces have a shade angle */\r
- if( maxShadeAngle == 0 )\r
- {\r
- free( shadeAngles );\r
- free( smoothed );\r
- return;\r
- }\r
- \r
- /* init pacifier */\r
- fOld = -1;\r
- start = I_FloatTime();\r
- \r
- /* go through the list of vertexes */\r
- for( i = 0; i < numBSPDrawVerts; i++ )\r
- {\r
- /* print pacifier */\r
- f = 10 * i / numBSPDrawVerts;\r
- if( f != fOld )\r
- {\r
- fOld = f;\r
- Sys_Printf( "%i...", f );\r
- }\r
- \r
- /* already smoothed? */\r
- if( smoothed[ i >> 3 ] & (1 << (i & 7)) )\r
- continue;\r
- \r
- /* clear */\r
- VectorClear( average );\r
- numVerts = 0;\r
- numVotes = 0;\r
- \r
- /* build a table of coincident vertexes */\r
- for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )\r
- {\r
- /* already smoothed? */\r
- if( smoothed[ j >> 3 ] & (1 << (j & 7)) )\r
- continue;\r
- \r
- /* test vertexes */\r
- if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )\r
- continue;\r
- \r
- /* use smallest shade angle */\r
- shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);\r
- \r
- /* check shade angle */\r
- dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );\r
- if( dot > 1.0 )\r
- dot = 1.0;\r
- else if( dot < -1.0 )\r
- dot = -1.0;\r
- testAngle = acos( dot ) + THETA_EPSILON;\r
- if( testAngle >= shadeAngle )\r
- {\r
- //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );\r
- continue;\r
- }\r
- //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );\r
- \r
- /* add to the list */\r
- indexes[ numVerts++ ] = j;\r
- \r
- /* flag vertex */\r
- smoothed[ j >> 3 ] |= (1 << (j & 7));\r
- \r
- /* see if this normal has already been voted */\r
- for( k = 0; k < numVotes; k++ )\r
- {\r
- VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );\r
- if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&\r
- fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&\r
- fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )\r
- break;\r
- }\r
- \r
- /* add a new vote? */\r
- if( k == numVotes && numVotes < MAX_SAMPLES )\r
- {\r
- VectorAdd( average, bspDrawVerts[ j ].normal, average );\r
- VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );\r
- numVotes++;\r
- }\r
- }\r
- \r
- /* don't average for less than 2 verts */\r
- if( numVerts < 2 )\r
- continue;\r
- \r
- /* average normal */\r
- if( VectorNormalize( average, average ) > 0 )\r
- {\r
- /* smooth */\r
- for( j = 0; j < numVerts; j++ )\r
- VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );\r
- }\r
- }\r
- \r
- /* free the tables */\r
- free( shadeAngles );\r
- free( smoothed );\r
- \r
- /* print time */\r
- Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );\r
-}\r
-\r
-\r
-\r
-/* -------------------------------------------------------------------------------\r
-\r
-this section deals with phong shaded lightmap tracing\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-/* 9th rewrite (recursive subdivision of a lightmap triangle) */\r
-\r
-/*\r
-CalcTangentVectors()\r
-calculates the st tangent vectors for normalmapping\r
-*/\r
-\r
-static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )\r
-{\r
- int i;\r
- float bb, s, t;\r
- vec3_t bary;\r
- \r
- \r
- /* calculate barycentric basis for the triangle */\r
- 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 ]);\r
- if( fabs( bb ) < 0.00000001f )\r
- return qfalse;\r
- \r
- /* do each vertex */\r
- for( i = 0; i < numVerts; i++ )\r
- {\r
- /* calculate s tangent vector */\r
- s = dv[ i ]->st[ 0 ] + 10.0f;\r
- t = dv[ i ]->st[ 1 ];\r
- bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;\r
- bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;\r
- bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;\r
- \r
- stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];\r
- stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];\r
- stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];\r
- \r
- VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );\r
- VectorNormalize( stv[ i ], stv[ i ] );\r
- \r
- /* calculate t tangent vector */\r
- s = dv[ i ]->st[ 0 ];\r
- t = dv[ i ]->st[ 1 ] + 10.0f;\r
- bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;\r
- bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;\r
- bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;\r
- \r
- ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];\r
- ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];\r
- ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];\r
- \r
- VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );\r
- VectorNormalize( ttv[ i ], ttv[ i ] );\r
- \r
- /* debug code */\r
- //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,\r
- //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );\r
- }\r
- \r
- /* return to caller */\r
- return qtrue;\r
-}\r
-\r
-\r
-\r
-\r
-/*\r
-PerturbNormal()\r
-perterbs the normal by the shader's normalmap in tangent space\r
-*/\r
-\r
-static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )\r
-{\r
- int i;\r
- vec4_t bump;\r
- \r
- \r
- /* passthrough */\r
- VectorCopy( dv->normal, pNormal );\r
- \r
- /* sample normalmap */\r
- if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )\r
- return;\r
- \r
- /* remap sampled normal from [0,255] to [-1,-1] */\r
- for( i = 0; i < 3; i++ )\r
- bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);\r
- \r
- /* scale tangent vectors and add to original normal */\r
- VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );\r
- VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );\r
- VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );\r
- \r
- /* renormalize and return */\r
- VectorNormalize( pNormal, pNormal );\r
-}\r
-\r
-\r
-\r
-/*\r
-MapSingleLuxel()\r
-maps a luxel for triangle bv at\r
-*/\r
-\r
-#define NUDGE 0.5f\r
-#define BOGUS_NUDGE -99999.0f\r
-\r
-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 ] )\r
-{\r
- int i, x, y, numClusters, *clusters, pointCluster, *cluster;\r
- float *luxel, *origin, *normal, d, lightmapSampleOffset;\r
- shaderInfo_t *si;\r
- vec3_t pNormal;\r
- vec3_t vecs[ 3 ];\r
- vec3_t nudged;\r
- float *nudge;\r
- static float nudges[][ 2 ] =\r
- {\r
- //%{ 0, 0 }, /* try center first */\r
- { -NUDGE, 0 }, /* left */\r
- { NUDGE, 0 }, /* right */\r
- { 0, NUDGE }, /* up */\r
- { 0, -NUDGE }, /* down */\r
- { -NUDGE, NUDGE }, /* left/up */\r
- { NUDGE, -NUDGE }, /* right/down */\r
- { NUDGE, NUDGE }, /* right/up */\r
- { -NUDGE, -NUDGE }, /* left/down */\r
- { BOGUS_NUDGE, BOGUS_NUDGE }\r
- };\r
- \r
- \r
- /* find luxel xy coords (fixme: subtract 0.5?) */\r
- x = dv->lightmap[ 0 ][ 0 ];\r
- y = dv->lightmap[ 0 ][ 1 ];\r
- if( x < 0 )\r
- x = 0;\r
- else if( x >= lm->sw )\r
- x = lm->sw - 1;\r
- if( y < 0 )\r
- y = 0;\r
- else if( y >= lm->sh )\r
- y = lm->sh - 1;\r
- \r
- /* set shader and cluster list */\r
- if( info != NULL )\r
- {\r
- si = info->si;\r
- numClusters = info->numSurfaceClusters;\r
- clusters = &surfaceClusters[ info->firstSurfaceCluster ];\r
- }\r
- else\r
- {\r
- si = NULL;\r
- numClusters = 0;\r
- clusters = NULL;\r
- }\r
- \r
- /* get luxel, origin, cluster, and normal */\r
- luxel = SUPER_LUXEL( 0, x, y );\r
- origin = SUPER_ORIGIN( x, y );\r
- normal = SUPER_NORMAL( x, y );\r
- cluster = SUPER_CLUSTER( x, y );\r
- \r
- /* don't attempt to remap occluded luxels for planar surfaces */\r
- if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )\r
- return (*cluster);\r
- \r
- /* only average the normal for premapped luxels */\r
- else if( (*cluster) >= 0 )\r
- {\r
- /* do bumpmap calculations */\r
- if( stv != NULL )\r
- PerturbNormal( dv, si, pNormal, stv, ttv );\r
- else\r
- VectorCopy( dv->normal, pNormal );\r
- \r
- /* add the additional normal data */\r
- VectorAdd( normal, pNormal, normal );\r
- luxel[ 3 ] += 1.0f;\r
- return (*cluster);\r
- }\r
- \r
- /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */\r
- \r
- /* get origin */\r
- \r
- /* axial lightmap projection */\r
- if( lm->vecs != NULL )\r
- {\r
- /* calculate an origin for the sample from the lightmap vectors */\r
- VectorCopy( lm->origin, origin );\r
- for( i = 0; i < 3; i++ )\r
- {\r
- /* add unless it's the axis, which is taken care of later */\r
- if( i == lm->axisNum )\r
- continue;\r
- origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);\r
- }\r
- \r
- /* project the origin onto the plane */\r
- d = DotProduct( origin, plane ) - plane[ 3 ];\r
- d /= plane[ lm->axisNum ];\r
- origin[ lm->axisNum ] -= d;\r
- }\r
- \r
- /* non axial lightmap projection (explicit xyz) */\r
- else\r
- VectorCopy( dv->xyz, origin );\r
- \r
- /* planar surfaces have precalculated lightmap vectors for nudging */\r
- if( lm->plane != NULL )\r
- {\r
- VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );\r
- VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );\r
- VectorCopy( lm->plane, vecs[ 2 ] );\r
- }\r
- \r
- /* non-planar surfaces must calculate them */\r
- else\r
- {\r
- if( plane != NULL )\r
- VectorCopy( plane, vecs[ 2 ] );\r
- else\r
- VectorCopy( dv->normal, vecs[ 2 ] );\r
- MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );\r
- }\r
- \r
- /* push the origin off the surface a bit */\r
- if( si != NULL )\r
- lightmapSampleOffset = si->lightmapSampleOffset;\r
- else\r
- lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;\r
- if( lm->axisNum < 0 )\r
- VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );\r
- else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )\r
- origin[ lm->axisNum ] -= lightmapSampleOffset;\r
- else\r
- origin[ lm->axisNum ] += lightmapSampleOffset;\r
- \r
- /* get cluster */\r
- pointCluster = ClusterForPointExtFilter( origin, LUXEL_EPSILON, numClusters, clusters );\r
- \r
- /* another retarded hack, storing nudge count in luxel[ 1 ] */\r
- luxel[ 1 ] = 0.0f; \r
- \r
- /* point in solid? */\r
- if( pointCluster < 0 )\r
- {\r
- /* nudge the the location around */\r
- nudge = nudges[ 0 ];\r
- while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )\r
- {\r
- /* nudge the vector around a bit */\r
- for( i = 0; i < 3; i++ )\r
- {\r
- /* set nudged point*/\r
- nudged[ i ] = origin[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);\r
- }\r
- nudge += 2;\r
- \r
- /* get pvs cluster */\r
- pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );\r
- if( pointCluster >= 0 ) \r
- VectorCopy( nudged, origin );\r
- luxel[ 1 ] += 1.0f;\r
- }\r
- }\r
- \r
- /* as a last resort, if still in solid, try drawvert origin offset by normal */\r
- if( pointCluster < 0 && si != NULL )\r
- {\r
- VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );\r
- pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );\r
- if( pointCluster >= 0 )\r
- VectorCopy( nudged, origin );\r
- luxel[ 1 ] += 1.0f;\r
- }\r
- \r
- /* valid? */\r
- if( pointCluster < 0 )\r
- {\r
- (*cluster) = CLUSTER_OCCLUDED;\r
- VectorClear( origin );\r
- VectorClear( normal );\r
- numLuxelsOccluded++;\r
- return (*cluster);\r
- }\r
- \r
- /* debug code */\r
- //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );\r
- \r
- /* do bumpmap calculations */\r
- if( stv )\r
- PerturbNormal( dv, si, pNormal, stv, ttv );\r
- else\r
- VectorCopy( dv->normal, pNormal );\r
- \r
- /* store the cluster and normal */\r
- (*cluster) = pointCluster;\r
- VectorCopy( pNormal, normal );\r
- \r
- /* store explicit mapping pass and implicit mapping pass */\r
- luxel[ 0 ] = pass;\r
- luxel[ 3 ] = 1.0f;\r
- \r
- /* add to count */\r
- numLuxelsMapped++;\r
- \r
- /* return ok */\r
- return (*cluster);\r
-}\r
-\r
-\r
-\r
-/*\r
-MapTriangle_r()\r
-recursively subdivides a triangle until its edges are shorter\r
-than the distance between two luxels (thanks jc :)\r
-*/\r
-\r
-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 ] )\r
-{\r
- bspDrawVert_t mid, *dv2[ 3 ];\r
- int max;\r
- \r
- \r
- /* map the vertexes */\r
- #if 0\r
- MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );\r
- MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );\r
- MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );\r
- #endif\r
- \r
- /* subdivide calc */\r
- {\r
- int i;\r
- float *a, *b, dx, dy, dist, maxDist;\r
- \r
- \r
- /* find the longest edge and split it */\r
- max = -1;\r
- maxDist = 0;\r
- for( i = 0; i < 3; i++ )\r
- {\r
- /* get verts */\r
- a = dv[ i ]->lightmap[ 0 ];\r
- b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];\r
- \r
- /* get dists */\r
- dx = a[ 0 ] - b[ 0 ];\r
- dy = a[ 1 ] - b[ 1 ];\r
- dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );\r
- \r
- /* longer? */\r
- if( dist > maxDist )\r
- {\r
- maxDist = dist;\r
- max = i;\r
- }\r
- }\r
- \r
- /* try to early out */\r
- if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */\r
- return;\r
- }\r
- \r
- /* split the longest edge and map it */\r
- LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );\r
- MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv );\r
- \r
- /* push the point up a little bit to account for fp creep (fixme: revisit this) */\r
- //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );\r
- \r
- /* recurse to first triangle */\r
- VectorCopy( dv, dv2 );\r
- dv2[ max ] = ∣\r
- MapTriangle_r( lm, info, dv2, plane, stv, ttv );\r
- \r
- /* recurse to second triangle */\r
- VectorCopy( dv, dv2 );\r
- dv2[ (max + 1) % 3 ] = ∣\r
- MapTriangle_r( lm, info, dv2, plane, stv, ttv );\r
-}\r
-\r
-\r
-\r
-/*\r
-MapTriangle()\r
-seed function for MapTriangle_r()\r
-requires a cw ordered triangle\r
-*/\r
-\r
-static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )\r
-{\r
- int i;\r
- vec4_t plane;\r
- vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];\r
- \r
- \r
- /* get plane if possible */\r
- if( lm->plane != NULL )\r
- {\r
- VectorCopy( lm->plane, plane );\r
- plane[ 3 ] = lm->plane[ 3 ];\r
- }\r
- \r
- /* otherwise make one from the points */\r
- else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )\r
- return qfalse;\r
- \r
- /* check to see if we need to calculate texture->world tangent vectors */\r
- if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )\r
- {\r
- stv = stvStatic;\r
- ttv = ttvStatic;\r
- }\r
- else\r
- {\r
- stv = NULL;\r
- ttv = NULL;\r
- }\r
- \r
- /* map the vertexes */\r
- MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );\r
- MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );\r
- MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );\r
- \r
- /* 2002-11-20: prefer axial triangle edges */\r
- if( mapNonAxial )\r
- {\r
- /* subdivide the triangle */\r
- MapTriangle_r( lm, info, dv, plane, stv, ttv );\r
- return qtrue;\r
- }\r
- \r
- for( i = 0; i < 3; i++ )\r
- {\r
- float *a, *b;\r
- bspDrawVert_t *dv2[ 3 ];\r
- \r
- \r
- /* get verts */\r
- a = dv[ i ]->lightmap[ 0 ];\r
- b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];\r
- \r
- /* make degenerate triangles for mapping edges */\r
- if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )\r
- {\r
- dv2[ 0 ] = dv[ i ];\r
- dv2[ 1 ] = dv[ (i + 1) % 3 ];\r
- dv2[ 2 ] = dv[ (i + 1) % 3 ];\r
- \r
- /* map the degenerate triangle */\r
- MapTriangle_r( lm, info, dv2, plane, stv, ttv );\r
- }\r
- }\r
- \r
- return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-MapQuad_r()\r
-recursively subdivides a quad until its edges are shorter\r
-than the distance between two luxels\r
-*/\r
-\r
-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 ] )\r
-{\r
- bspDrawVert_t mid[ 2 ], *dv2[ 4 ];\r
- int max;\r
- \r
- \r
- /* subdivide calc */\r
- {\r
- int i;\r
- float *a, *b, dx, dy, dist, maxDist;\r
- \r
- \r
- /* find the longest edge and split it */\r
- max = -1;\r
- maxDist = 0;\r
- for( i = 0; i < 4; i++ )\r
- {\r
- /* get verts */\r
- a = dv[ i ]->lightmap[ 0 ];\r
- b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];\r
- \r
- /* get dists */\r
- dx = a[ 0 ] - b[ 0 ];\r
- dy = a[ 1 ] - b[ 1 ];\r
- dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );\r
- \r
- /* longer? */\r
- if( dist > maxDist )\r
- {\r
- maxDist = dist;\r
- max = i;\r
- }\r
- }\r
- \r
- /* try to early out */\r
- if( max < 0 || maxDist <= subdivideThreshold )\r
- return;\r
- }\r
- \r
- /* we only care about even/odd edges */\r
- max &= 1;\r
- \r
- /* split the longest edges */\r
- LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );\r
- LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );\r
- \r
- /* map the vertexes */\r
- MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv );\r
- MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv );\r
- \r
- /* 0 and 2 */\r
- if( max == 0 )\r
- {\r
- /* recurse to first quad */\r
- dv2[ 0 ] = dv[ 0 ];\r
- dv2[ 1 ] = &mid[ 0 ];\r
- dv2[ 2 ] = &mid[ 1 ];\r
- dv2[ 3 ] = dv[ 3 ];\r
- MapQuad_r( lm, info, dv2, plane, stv, ttv );\r
- \r
- /* recurse to second quad */\r
- dv2[ 0 ] = &mid[ 0 ];\r
- dv2[ 1 ] = dv[ 1 ];\r
- dv2[ 2 ] = dv[ 2 ];\r
- dv2[ 3 ] = &mid[ 1 ];\r
- MapQuad_r( lm, info, dv2, plane, stv, ttv );\r
- }\r
- \r
- /* 1 and 3 */\r
- else\r
- {\r
- /* recurse to first quad */\r
- dv2[ 0 ] = dv[ 0 ];\r
- dv2[ 1 ] = dv[ 1 ];\r
- dv2[ 2 ] = &mid[ 0 ];\r
- dv2[ 3 ] = &mid[ 1 ];\r
- MapQuad_r( lm, info, dv2, plane, stv, ttv );\r
- \r
- /* recurse to second quad */\r
- dv2[ 0 ] = &mid[ 1 ];\r
- dv2[ 1 ] = &mid[ 0 ];\r
- dv2[ 2 ] = dv[ 2 ];\r
- dv2[ 3 ] = dv[ 3 ];\r
- MapQuad_r( lm, info, dv2, plane, stv, ttv );\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-MapQuad()\r
-seed function for MapQuad_r()\r
-requires a cw ordered triangle quad\r
-*/\r
-\r
-#define QUAD_PLANAR_EPSILON 0.5f\r
-\r
-static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )\r
-{\r
- float dist;\r
- vec4_t plane;\r
- vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];\r
- \r
- \r
- /* get plane if possible */\r
- if( lm->plane != NULL )\r
- {\r
- VectorCopy( lm->plane, plane );\r
- plane[ 3 ] = lm->plane[ 3 ];\r
- }\r
- \r
- /* otherwise make one from the points */\r
- else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )\r
- return qfalse;\r
- \r
- /* 4th point must fall on the plane */\r
- dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];\r
- if( fabs( dist ) > QUAD_PLANAR_EPSILON )\r
- return qfalse;\r
- \r
- /* check to see if we need to calculate texture->world tangent vectors */\r
- if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )\r
- {\r
- stv = stvStatic;\r
- ttv = ttvStatic;\r
- }\r
- else\r
- {\r
- stv = NULL;\r
- ttv = NULL;\r
- }\r
- \r
- /* map the vertexes */\r
- MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );\r
- MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );\r
- MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );\r
- MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv );\r
- \r
- /* subdivide the quad */\r
- MapQuad_r( lm, info, dv, plane, stv, ttv );\r
- return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-MapRawLightmap()\r
-maps the locations, normals, and pvs clusters for a raw lightmap\r
-*/\r
-\r
-#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)\r
-\r
-void MapRawLightmap( int rawLightmapNum )\r
-{\r
- int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;\r
- float *luxel, *origin, *normal, samples, radius, pass;\r
- rawLightmap_t *lm;\r
- bspDrawSurface_t *ds;\r
- surfaceInfo_t *info;\r
- mesh_t src, *subdivided, *mesh;\r
- bspDrawVert_t *verts, *dv[ 4 ], fake;\r
- \r
- \r
- /* bail if this number exceeds the number of raw lightmaps */\r
- if( rawLightmapNum >= numRawLightmaps )\r
- return;\r
- \r
- /* get lightmap */\r
- lm = &rawLightmaps[ rawLightmapNum ];\r
- \r
- /* -----------------------------------------------------------------\r
- map referenced surfaces onto the raw lightmap\r
- ----------------------------------------------------------------- */\r
- \r
- /* walk the list of surfaces on this raw lightmap */\r
- for( n = 0; n < lm->numLightSurfaces; n++ )\r
- {\r
- /* with > 1 surface per raw lightmap, clear occluded */\r
- if( n > 0 )\r
- {\r
- for( y = 0; y < lm->sh; y++ )\r
- {\r
- for( x = 0; x < lm->sw; x++ )\r
- {\r
- /* get cluster */\r
- cluster = SUPER_CLUSTER( x, y );\r
- if( *cluster < 0 )\r
- *cluster = CLUSTER_UNMAPPED;\r
- }\r
- }\r
- }\r
- \r
- /* get surface */\r
- num = lightSurfaces[ lm->firstLightSurface + n ];\r
- ds = &bspDrawSurfaces[ num ];\r
- info = &surfaceInfos[ num ];\r
- \r
- /* bail if no lightmap to calculate */\r
- if( info->lm != lm )\r
- {\r
- Sys_Printf( "!" );\r
- continue;\r
- }\r
- \r
- /* map the surface onto the lightmap origin/cluster/normal buffers */\r
- switch( ds->surfaceType )\r
- {\r
- case MST_PLANAR:\r
- /* get verts */\r
- verts = yDrawVerts + ds->firstVert;\r
- \r
- /* map the triangles */\r
- for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )\r
- {\r
- for( i = 0; i < ds->numIndexes; i += 3 )\r
- {\r
- dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];\r
- dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];\r
- dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];\r
- MapTriangle( lm, info, dv, mapNonAxial );\r
- }\r
- }\r
- break;\r
- \r
- case MST_PATCH:\r
- /* make a mesh from the drawsurf */ \r
- src.width = ds->patchWidth;\r
- src.height = ds->patchHeight;\r
- src.verts = &yDrawVerts[ ds->firstVert ];\r
- //% subdivided = SubdivideMesh( src, 8, 512 );\r
- subdivided = SubdivideMesh2( src, info->patchIterations );\r
- \r
- /* fit it to the curve and remove colinear verts on rows/columns */\r
- PutMeshOnCurve( *subdivided );\r
- mesh = RemoveLinearMeshColumnsRows( subdivided );\r
- FreeMesh( subdivided );\r
- \r
- /* get verts */\r
- verts = mesh->verts;\r
- \r
- /* debug code */\r
- #if 0\r
- if( lm->plane )\r
- {\r
- Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",\r
- lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],\r
- lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],\r
- lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );\r
- }\r
- #endif\r
- \r
- /* map the mesh quads */\r
- #if 0\r
-\r
- for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )\r
- {\r
- for( y = 0; y < (mesh->height - 1); y++ )\r
- {\r
- for( x = 0; x < (mesh->width - 1); x++ )\r
- {\r
- /* set indexes */\r
- pw[ 0 ] = x + (y * mesh->width);\r
- pw[ 1 ] = x + ((y + 1) * mesh->width);\r
- pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);\r
- pw[ 3 ] = x + 1 + (y * mesh->width);\r
- pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */\r
- \r
- /* set radix */\r
- r = (x + y) & 1;\r
- \r
- /* get drawverts and map first triangle */\r
- dv[ 0 ] = &verts[ pw[ r + 0 ] ];\r
- dv[ 1 ] = &verts[ pw[ r + 1 ] ];\r
- dv[ 2 ] = &verts[ pw[ r + 2 ] ];\r
- MapTriangle( lm, info, dv, mapNonAxial );\r
- \r
- /* get drawverts and map second triangle */\r
- dv[ 0 ] = &verts[ pw[ r + 0 ] ];\r
- dv[ 1 ] = &verts[ pw[ r + 2 ] ];\r
- dv[ 2 ] = &verts[ pw[ r + 3 ] ];\r
- MapTriangle( lm, info, dv, mapNonAxial );\r
- }\r
- }\r
- }\r
- \r
- #else\r
- \r
- for( y = 0; y < (mesh->height - 1); y++ )\r
- {\r
- for( x = 0; x < (mesh->width - 1); x++ )\r
- {\r
- /* set indexes */\r
- pw[ 0 ] = x + (y * mesh->width);\r
- pw[ 1 ] = x + ((y + 1) * mesh->width);\r
- pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);\r
- pw[ 3 ] = x + 1 + (y * mesh->width);\r
- pw[ 4 ] = pw[ 0 ];\r
- \r
- /* set radix */\r
- r = (x + y) & 1;\r
- \r
- /* attempt to map quad first */\r
- dv[ 0 ] = &verts[ pw[ r + 0 ] ];\r
- dv[ 1 ] = &verts[ pw[ r + 1 ] ];\r
- dv[ 2 ] = &verts[ pw[ r + 2 ] ];\r
- dv[ 3 ] = &verts[ pw[ r + 3 ] ];\r
- if( MapQuad( lm, info, dv ) )\r
- continue;\r
- \r
- /* get drawverts and map first triangle */\r
- MapTriangle( lm, info, dv, mapNonAxial );\r
- \r
- /* get drawverts and map second triangle */\r
- dv[ 1 ] = &verts[ pw[ r + 2 ] ];\r
- dv[ 2 ] = &verts[ pw[ r + 3 ] ];\r
- MapTriangle( lm, info, dv, mapNonAxial );\r
- }\r
- }\r
- \r
- #endif\r
- \r
- /* free the mesh */\r
- FreeMesh( mesh );\r
- break;\r
- \r
- default:\r
- break;\r
- }\r
- }\r
- \r
- /* -----------------------------------------------------------------\r
- average and clean up luxel normals\r
- ----------------------------------------------------------------- */\r
- \r
- /* walk the luxels */\r
- for( y = 0; y < lm->sh; y++ )\r
- {\r
- for( x = 0; x < lm->sw; x++ )\r
- {\r
- /* get luxel */\r
- luxel = SUPER_LUXEL( 0, x, y );\r
- normal = SUPER_NORMAL( x, y );\r
- cluster = SUPER_CLUSTER( x, y );\r
-\r
- /* only look at mapped luxels */\r
- if( *cluster < 0 )\r
- continue;\r
- \r
- /* the normal data could be the sum of multiple samples */\r
- if( luxel[ 3 ] > 1.0f )\r
- VectorNormalize( normal, normal );\r
- \r
- /* mark this luxel as having only one normal */\r
- luxel[ 3 ] = 1.0f;\r
- }\r
- }\r
- \r
- /* non-planar surfaces stop here */\r
- if( lm->plane == NULL )\r
- return;\r
- \r
- /* -----------------------------------------------------------------\r
- map occluded or unuxed luxels\r
- ----------------------------------------------------------------- */\r
- \r
- /* walk the luxels */\r
- radius = floor( superSample / 2 );\r
- radius = radius > 0 ? radius : 1.0f;\r
- radius += 1.0f;\r
- for( pass = 2.0f; pass <= radius; pass += 1.0f )\r
- {\r
- for( y = 0; y < lm->sh; y++ )\r
- {\r
- for( x = 0; x < lm->sw; x++ )\r
- {\r
- /* get luxel */\r
- luxel = SUPER_LUXEL( 0, x, y );\r
- normal = SUPER_NORMAL( x, y );\r
- cluster = SUPER_CLUSTER( x, y );\r
- \r
- /* only look at unmapped luxels */\r
- if( *cluster != CLUSTER_UNMAPPED )\r
- continue;\r
- \r
- /* divine a normal and origin from neighboring luxels */\r
- VectorClear( fake.xyz );\r
- VectorClear( fake.normal );\r
- fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;\r
- fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;\r
- samples = 0.0f;\r
- for( sy = (y - 1); sy <= (y + 1); sy++ )\r
- {\r
- if( sy < 0 || sy >= lm->sh )\r
- continue;\r
- \r
- for( sx = (x - 1); sx <= (x + 1); sx++ )\r
- {\r
- if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )\r
- continue;\r
- \r
- /* get neighboring luxel */\r
- luxel = SUPER_LUXEL( 0, sx, sy );\r
- origin = SUPER_ORIGIN( sx, sy );\r
- normal = SUPER_NORMAL( sx, sy );\r
- cluster = SUPER_CLUSTER( sx, sy );\r
- \r
- /* only consider luxels mapped in previous passes */\r
- if( *cluster < 0 || luxel[ 0 ] >= pass )\r
- continue;\r
- \r
- /* add its distinctiveness to our own */\r
- VectorAdd( fake.xyz, origin, fake.xyz );\r
- VectorAdd( fake.normal, normal, fake.normal );\r
- samples += luxel[ 3 ];\r
- }\r
- }\r
- \r
- /* any samples? */\r
- if( samples == 0.0f )\r
- continue;\r
- \r
- /* average */\r
- VectorDivide( fake.xyz, samples, fake.xyz );\r
- //% VectorDivide( fake.normal, samples, fake.normal );\r
- if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )\r
- continue;\r
- \r
- /* map the fake vert */\r
- MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL );\r
- }\r
- }\r
- }\r
- \r
- /* -----------------------------------------------------------------\r
- average and clean up luxel normals\r
- ----------------------------------------------------------------- */\r
- \r
- /* walk the luxels */\r
- for( y = 0; y < lm->sh; y++ )\r
- {\r
- for( x = 0; x < lm->sw; x++ )\r
- {\r
- /* get luxel */\r
- luxel = SUPER_LUXEL( 0, x, y );\r
- normal = SUPER_NORMAL( x, y );\r
- cluster = SUPER_CLUSTER( x, y );\r
- \r
- /* only look at mapped luxels */\r
- if( *cluster < 0 )\r
- continue;\r
- \r
- /* the normal data could be the sum of multiple samples */\r
- if( luxel[ 3 ] > 1.0f )\r
- VectorNormalize( normal, normal );\r
- \r
- /* mark this luxel as having only one normal */\r
- luxel[ 3 ] = 1.0f;\r
- }\r
- }\r
- \r
- /* debug code */\r
- #if 0\r
- Sys_Printf( "\n" );\r
- for( y = 0; y < lm->sh; y++ )\r
- {\r
- for( x = 0; x < lm->sw; x++ )\r
- {\r
- vec3_t mins, maxs;\r
- \r
-\r
- cluster = SUPER_CLUSTER( x, y );\r
- origin = SUPER_ORIGIN( x, y );\r
- normal = SUPER_NORMAL( x, y );\r
- luxel = SUPER_LUXEL( x, y );\r
- \r
- if( *cluster < 0 )\r
- continue;\r
- \r
- /* check if within the bounding boxes of all surfaces referenced */\r
- ClearBounds( mins, maxs );\r
- for( n = 0; n < lm->numLightSurfaces; n++ )\r
- {\r
- int TOL;\r
- info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];\r
- TOL = info->sampleSize + 2;\r
- AddPointToBounds( info->mins, mins, maxs );\r
- AddPointToBounds( info->maxs, mins, maxs );\r
- if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&\r
- origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&\r
- origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )\r
- break;\r
- }\r
- \r
- /* inside? */\r
- if( n < lm->numLightSurfaces )\r
- continue;\r
- \r
- /* report bogus origin */\r
- 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",\r
- rawLightmapNum, x, y, *cluster,\r
- origin[ 0 ], origin[ 1 ], origin[ 2 ],\r
- mins[ 0 ], mins[ 1 ], mins[ 2 ],\r
- maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],\r
- luxel[ 3 ] );\r
- }\r
- }\r
- #endif\r
-}\r
-\r
-\r
-\r
-/*\r
-SubmapRawLuxel()\r
-calculates the pvs cluster, origin, normal of a sub-luxel\r
-*/\r
-\r
-static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )\r
-{\r
- int i, *cluster, *cluster2;\r
- float *origin, *origin2, *normal; //% , *normal2;\r
- vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];\r
- \r
- \r
- /* calulate x vector */\r
- if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )\r
- {\r
- cluster = SUPER_CLUSTER( x, y );\r
- origin = SUPER_ORIGIN( x, y );\r
- //% normal = SUPER_NORMAL( x, y );\r
- cluster2 = SUPER_CLUSTER( x + 1, y );\r
- origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );\r
- //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );\r
- }\r
- else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )\r
- {\r
- cluster = SUPER_CLUSTER( x - 1, y );\r
- origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );\r
- //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );\r
- cluster2 = SUPER_CLUSTER( x, y );\r
- origin2 = SUPER_ORIGIN( x, y );\r
- //% normal2 = SUPER_NORMAL( x, y );\r
- }\r
- else\r
- Sys_Printf( "WARNING: Spurious lightmap S vector\n" );\r
- \r
- VectorSubtract( origin2, origin, originVecs[ 0 ] );\r
- //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );\r
- \r
- /* calulate y vector */\r
- if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )\r
- {\r
- cluster = SUPER_CLUSTER( x, y );\r
- origin = SUPER_ORIGIN( x, y );\r
- //% normal = SUPER_NORMAL( x, y );\r
- cluster2 = SUPER_CLUSTER( x, y + 1 );\r
- origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );\r
- //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );\r
- }\r
- else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )\r
- {\r
- cluster = SUPER_CLUSTER( x, y - 1 );\r
- origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );\r
- //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );\r
- cluster2 = SUPER_CLUSTER( x, y );\r
- origin2 = SUPER_ORIGIN( x, y );\r
- //% normal2 = SUPER_NORMAL( x, y );\r
- }\r
- else\r
- Sys_Printf( "WARNING: Spurious lightmap T vector\n" );\r
- \r
- VectorSubtract( origin2, origin, originVecs[ 1 ] );\r
- //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );\r
- \r
- /* calculate new origin */\r
- //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );\r
- //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );\r
- for( i = 0; i < 3; i++ )\r
- sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);\r
- \r
- /* get cluster */\r
- *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );\r
- if( *sampleCluster < 0 )\r
- return qfalse;\r
- \r
- /* calculate new normal */\r
- //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );\r
- //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );\r
- //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )\r
- //% return qfalse;\r
- normal = SUPER_NORMAL( x, y );\r
- VectorCopy( normal, sampleNormal );\r
- \r
- /* return ok */\r
- return qtrue;\r
-}\r
-\r
-\r
-/*\r
-SubsampleRawLuxel_r()\r
-recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached\r
-*/\r
-\r
-void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )\r
-{\r
- int b, samples, mapped, lighted;\r
- int cluster[ 4 ];\r
- vec4_t luxel[ 4 ];\r
- vec3_t origin[ 4 ], normal[ 4 ];\r
- float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };\r
- vec3_t color, total;\r
- \r
- \r
- /* limit check */\r
- if( lightLuxel[ 3 ] >= lightSamples )\r
- return;\r
- \r
- /* setup */\r
- VectorClear( total );\r
- mapped = 0;\r
- lighted = 0;\r
- \r
- /* make 2x2 subsample stamp */\r
- for( b = 0; b < 4; b++ )\r
- {\r
- /* set origin */\r
- VectorCopy( sampleOrigin, origin[ b ] );\r
- \r
- /* calculate position */\r
- if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )\r
- {\r
- cluster[ b ] = -1;\r
- continue;\r
- }\r
- mapped++;\r
- \r
- /* increment sample count */\r
- luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;\r
- \r
- /* setup trace */\r
- trace->cluster = *cluster;\r
- VectorCopy( origin[ b ], trace->origin );\r
- VectorCopy( normal[ b ], trace->normal );\r
- \r
- /* sample light */\r
- //% LightContributionToSample( light, cluster[ b ], origin[ b ], normal[ b ], luxel[ b ], qtrue, qfalse, lm->numLightSurfaces, &lightSurfaces[ lm->firstLightSurface ] );\r
- LightContributionToSample( trace );\r
- \r
- /* add to totals (fixme: make contrast function) */\r
- VectorCopy( trace->color, luxel[ b ] );\r
- VectorAdd( total, trace->color, total );\r
- if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )\r
- lighted++;\r
- }\r
- \r
- /* subsample further? */\r
- if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&\r
- (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&\r
- lighted != 0 && lighted != mapped )\r
- {\r
- for( b = 0; b < 4; b++ )\r
- {\r
- if( cluster[ b ] < 0 )\r
- continue;\r
- SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.25f), luxel[ b ] );\r
- }\r
- }\r
- \r
- /* average */\r
- //% VectorClear( color );\r
- //% samples = 0;\r
- VectorCopy( lightLuxel, color );\r
- samples = 1;\r
- for( b = 0; b < 4; b++ )\r
- {\r
- if( cluster[ b ] < 0 )\r
- continue;\r
- VectorAdd( color, luxel[ b ], color );\r
- samples++;\r
- }\r
- \r
- /* add to luxel */\r
- if( samples > 0 )\r
- {\r
- /* average */\r
- color[ 0 ] /= samples;\r
- color[ 1 ] /= samples;\r
- color[ 2 ] /= samples;\r
- \r
- /* add to color */\r
- VectorCopy( color, lightLuxel );\r
- lightLuxel[ 3 ] += 1.0f;\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-IlluminateRawLightmap()\r
-illuminates the luxels\r
-*/\r
-\r
-#define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))\r
-\r
-void IlluminateRawLightmap( int rawLightmapNum )\r
-{\r
- int i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;\r
- int *cluster, *cluster2, mapped, lighted, totalLighted;\r
- rawLightmap_t *lm;\r
- surfaceInfo_t *info;\r
- qboolean filterColor, filterDir;\r
- float brightness;\r
- float *origin, *normal, *luxel, *luxel2, *deluxel, *deluxel2;\r
- float *lightLuxels, *lightLuxel, samples, filterRadius, weight;\r
- vec3_t color, averageColor, averageDir, total, temp, temp2;\r
- float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };\r
- trace_t trace;\r
- \r
- \r
- /* bail if this number exceeds the number of raw lightmaps */\r
- if( rawLightmapNum >= numRawLightmaps )\r
- return;\r
- \r
- /* get lightmap */\r
- lm = &rawLightmaps[ rawLightmapNum ];\r
- \r
- /* setup trace */\r
- trace.testOcclusion = !noTrace;\r
- trace.forceSunlight = qfalse;\r
- trace.recvShadows = lm->recvShadows;\r
- trace.numSurfaces = lm->numLightSurfaces;\r
- trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];\r
- trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;\r
- \r
- /* twosided lighting (may or may not be a good idea for lightmapped stuff) */\r
- trace.twoSided = qfalse;\r
- for( i = 0; i < trace.numSurfaces; i++ )\r
- {\r
- /* get surface */\r
- info = &surfaceInfos[ trace.surfaces[ i ] ];\r
- \r
- /* check twosidedness */\r
- if( info->si->twoSided )\r
- {\r
- trace.twoSided = qtrue;\r
- break;\r
- }\r
- }\r
- \r
- /* create a culled light list for this raw lightmap */\r
- CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );\r
- \r
- /* -----------------------------------------------------------------\r
- fill pass\r
- ----------------------------------------------------------------- */\r
- \r
- /* test debugging state */\r
- if( debugSurfaces || debugAxis || debugCluster || debugOrigin || normalmap )\r
- {\r
- /* debug fill the luxels */\r
- for( y = 0; y < lm->sh; y++ )\r
- {\r
- for( x = 0; x < lm->sw; x++ )\r
- {\r
- /* get cluster */\r
- cluster = SUPER_CLUSTER( x, y );\r
-\r
- /* only fill mapped luxels */\r
- if( *cluster < 0 )\r
- continue;\r
- \r
- /* get particulars */\r
- luxel = SUPER_LUXEL( 0, x, y );\r
- origin = SUPER_ORIGIN( x, y );\r
- normal = SUPER_NORMAL( x, y );\r
- \r
- /* color the luxel with raw lightmap num? */\r
- if( debugSurfaces )\r
- VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );\r
- \r
- /* color the luxel with lightmap axis? */\r
- else if( debugAxis )\r
- {\r
- luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;\r
- luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;\r
- luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;\r
- }\r
- \r
- /* color the luxel with luxel cluster? */\r
- else if( debugCluster )\r
- VectorCopy( debugColors[ *cluster % 12 ], luxel );\r
- \r
- /* color the luxel with luxel origin? */\r
- else if( debugOrigin )\r
- {\r
- VectorSubtract( lm->maxs, lm->mins, temp );\r
- VectorScale( temp, (1.0f / 255.0f), temp );\r
- VectorSubtract( origin, lm->mins, temp2 );\r
- luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);\r
- luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);\r
- luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);\r
- }\r
- \r
- /* color the luxel with the normal */\r
- else if( normalmap )\r
- {\r
- luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;\r
- luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;\r
- luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;\r
- }\r
- \r
- /* add to counts */\r
- numLuxelsIlluminated++;\r
- luxel[ 3 ] = 1.0f;\r
- }\r
- }\r
- }\r
- else\r
- {\r
- /* allocate temporary per-light luxel storage */\r
- llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );\r
- lightLuxels = safe_malloc( llSize );\r
- \r
- /* clear luxels */\r
- //% memset( lm->superLuxels[ 0 ], 0, llSize );\r
- \r
- /* set ambient color */\r
- for( y = 0; y < lm->sh; y++ )\r
- {\r
- for( x = 0; x < lm->sw; x++ )\r
- {\r
- /* get cluster */\r
- cluster = SUPER_CLUSTER( x, y );\r
- luxel = SUPER_LUXEL( 0, x, y );\r
- normal = SUPER_NORMAL( x, y );\r
- deluxel = SUPER_DELUXEL( x, y );\r
- \r
- /* blacken unmapped clusters */\r
- if( *cluster < 0 )\r
- VectorClear( luxel );\r
- \r
- /* set ambient */\r
- else\r
- {\r
- VectorCopy( ambientColor, luxel );\r
- if( deluxemap )\r
- VectorScale( normal, 0.00390625f, deluxel );\r
- luxel[ 3 ] = 1.0f;\r
- }\r
- }\r
- }\r
- \r
- /* clear styled lightmaps */\r
- size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );\r
- for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- if( lm->superLuxels[ lightmapNum ] != NULL )\r
- memset( lm->superLuxels[ lightmapNum ], 0, size );\r
- }\r
- \r
- /* debugging code */\r
- //% if( trace.numLights <= 0 )\r
- //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );\r
- \r
- /* walk light list */\r
- for( i = 0; i < trace.numLights; i++ )\r
- {\r
- /* setup trace */\r
- trace.light = trace.lights[ i ];\r
- \r
- /* style check */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- if( lm->styles[ lightmapNum ] == trace.light->style ||\r
- lm->styles[ lightmapNum ] == LS_NONE )\r
- break;\r
- }\r
- \r
- /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */\r
- if( lightmapNum >= MAX_LIGHTMAPS )\r
- {\r
- Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );\r
- continue;\r
- }\r
- \r
- /* setup */\r
- memset( lightLuxels, 0, llSize );\r
- totalLighted = 0;\r
- \r
- /* initial pass, one sample per luxel */\r
- for( y = 0; y < lm->sh; y++ )\r
- {\r
- for( x = 0; x < lm->sw; x++ )\r
- {\r
- /* get cluster */\r
- cluster = SUPER_CLUSTER( x, y );\r
- if( *cluster < 0 )\r
- continue;\r
- \r
- /* get particulars */\r
- lightLuxel = LIGHT_LUXEL( x, y );\r
- deluxel = SUPER_DELUXEL( x, y );\r
- origin = SUPER_ORIGIN( x, y );\r
- normal = SUPER_NORMAL( x, y );\r
- \r
- /* set contribution count */\r
- lightLuxel[ 3 ] = 1.0f;\r
- \r
- /* setup trace */\r
- trace.cluster = *cluster;\r
- VectorCopy( origin, trace.origin );\r
- VectorCopy( normal, trace.normal );\r
- \r
- /* get light for this sample */\r
- LightContributionToSample( &trace );\r
- VectorCopy( trace.color, lightLuxel );\r
- \r
- /* add to count */\r
- if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )\r
- totalLighted++;\r
- \r
- /* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */\r
- if( deluxemap )\r
- {\r
- /* color to grayscale (photoshop rgb weighting) */\r
- brightness = trace.color[ 0 ] * 0.3f + trace.color[ 1 ] * 0.59f + trace.color[ 2 ] * 0.11f;\r
- brightness *= (1.0 / 255.0);\r
- VectorScale( trace.direction, brightness, trace.direction );\r
- VectorAdd( deluxel, trace.direction, deluxel );\r
- }\r
- }\r
- }\r
- \r
- /* don't even bother with everything else if nothing was lit */\r
- if( totalLighted == 0 )\r
- continue;\r
- \r
- /* determine filter radius */\r
- filterRadius = lm->filterRadius > trace.light->filterRadius\r
- ? lm->filterRadius\r
- : trace.light->filterRadius;\r
- if( filterRadius < 0.0f )\r
- filterRadius = 0.0f;\r
- \r
- /* set luxel filter radius */\r
- luxelFilterRadius = superSample * filterRadius / lm->sampleSize;\r
- if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )\r
- luxelFilterRadius = 1;\r
- \r
- /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */\r
- /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */\r
- if( lightSamples > 1 && luxelFilterRadius == 0 )\r
- {\r
- /* walk luxels */\r
- for( y = 0; y < (lm->sh - 1); y++ )\r
- {\r
- for( x = 0; x < (lm->sw - 1); x++ )\r
- {\r
- /* setup */\r
- mapped = 0;\r
- lighted = 0;\r
- VectorClear( total );\r
- \r
- /* test 2x2 stamp */\r
- for( t = 0; t < 4; t++ )\r
- {\r
- /* set sample coords */\r
- sx = x + tests[ t ][ 0 ];\r
- sy = y + tests[ t ][ 1 ];\r
- \r
- /* get cluster */\r
- cluster = SUPER_CLUSTER( sx, sy );\r
- if( *cluster < 0 )\r
- continue;\r
- mapped++;\r
- \r
- /* get luxel */\r
- lightLuxel = LIGHT_LUXEL( sx, sy );\r
- VectorAdd( total, lightLuxel, total );\r
- if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )\r
- lighted++;\r
- }\r
- \r
- /* if total color is under a certain amount, then don't bother subsampling */\r
- if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )\r
- continue;\r
- \r
- /* if all 4 pixels are either in shadow or light, then don't subsample */\r
- if( lighted != 0 && lighted != mapped )\r
- {\r
- for( t = 0; t < 4; t++ )\r
- {\r
- /* set sample coords */\r
- sx = x + tests[ t ][ 0 ];\r
- sy = y + tests[ t ][ 1 ];\r
- \r
- /* get luxel */\r
- cluster = SUPER_CLUSTER( sx, sy );\r
- if( *cluster < 0 )\r
- continue;\r
- lightLuxel = LIGHT_LUXEL( sx, sy );\r
- origin = SUPER_ORIGIN( sx, sy );\r
- \r
- /* only subsample shadowed luxels */\r
- //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )\r
- //% continue;\r
- \r
- /* subsample it */\r
- SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f, lightLuxel );\r
- \r
- /* debug code to colorize subsampled areas to yellow */\r
- //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );\r
- //% VectorSet( luxel, 255, 204, 0 );\r
- }\r
- }\r
- }\r
- }\r
- }\r
- \r
- /* allocate sampling lightmap storage */\r
- if( lm->superLuxels[ lightmapNum ] == NULL )\r
- {\r
- /* allocate sampling lightmap storage */\r
- size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );\r
- lm->superLuxels[ lightmapNum ] = safe_malloc( size );\r
- memset( lm->superLuxels[ lightmapNum ], 0, size );\r
- }\r
- \r
- /* set style */\r
- if( lightmapNum > 0 )\r
- {\r
- lm->styles[ lightmapNum ] = trace.light->style;\r
- //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );\r
- }\r
- \r
- /* copy to permanent luxels */\r
- for( y = 0; y < lm->sh; y++ )\r
- {\r
- for( x = 0; x < lm->sw; x++ )\r
- {\r
- /* get cluster and origin */\r
- cluster = SUPER_CLUSTER( x, y );\r
- if( *cluster < 0 )\r
- continue;\r
- origin = SUPER_ORIGIN( x, y );\r
- \r
- /* filter? */\r
- if( luxelFilterRadius )\r
- {\r
- /* setup */\r
- VectorClear( averageColor );\r
- samples = 0.0f;\r
- \r
- /* cheaper distance-based filtering */\r
- for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )\r
- {\r
- if( sy < 0 || sy >= lm->sh )\r
- continue;\r
- \r
- for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )\r
- {\r
- if( sx < 0 || sx >= lm->sw )\r
- continue;\r
- \r
- /* get particulars */\r
- cluster = SUPER_CLUSTER( sx, sy );\r
- if( *cluster < 0 )\r
- continue;\r
- lightLuxel = LIGHT_LUXEL( sx, sy );\r
- \r
- /* create weight */\r
- weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);\r
- weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);\r
- \r
- /* scale luxel by filter weight */\r
- VectorScale( lightLuxel, weight, color );\r
- VectorAdd( averageColor, color, averageColor );\r
- samples += weight;\r
- }\r
- }\r
- \r
- /* any samples? */\r
- if( samples <= 0.0f )\r
- continue;\r
- \r
- /* scale into luxel */\r
- luxel = SUPER_LUXEL( lightmapNum, x, y );\r
- luxel[ 3 ] = 1.0f;\r
- \r
- /* handle negative light */\r
- if( trace.light->flags & LIGHT_NEGATIVE )\r
- { \r
- luxel[ 0 ] -= averageColor[ 0 ] / samples;\r
- luxel[ 1 ] -= averageColor[ 1 ] / samples;\r
- luxel[ 2 ] -= averageColor[ 2 ] / samples;\r
- }\r
- \r
- /* handle normal light */\r
- else\r
- { \r
- luxel[ 0 ] += averageColor[ 0 ] / samples;\r
- luxel[ 1 ] += averageColor[ 1 ] / samples;\r
- luxel[ 2 ] += averageColor[ 2 ] / samples;\r
- }\r
- }\r
- \r
- /* single sample */\r
- else\r
- {\r
- /* get particulars */\r
- lightLuxel = LIGHT_LUXEL( x, y );\r
- luxel = SUPER_LUXEL( lightmapNum, x, y );\r
- \r
- /* handle negative light */\r
- if( trace.light->flags & LIGHT_NEGATIVE )\r
- VectorScale( averageColor, -1.0f, averageColor );\r
-\r
- /* add color */\r
- luxel[ 3 ] = 1.0f;\r
- \r
- /* handle negative light */\r
- if( trace.light->flags & LIGHT_NEGATIVE )\r
- VectorSubtract( luxel, lightLuxel, luxel );\r
- \r
- /* handle normal light */\r
- else\r
- VectorAdd( luxel, lightLuxel, luxel );\r
- }\r
- }\r
- }\r
- }\r
- \r
- /* free temporary luxels */\r
- free( lightLuxels );\r
- }\r
- \r
- /* free light list */\r
- FreeTraceLights( &trace );\r
- \r
- /* -----------------------------------------------------------------\r
- filter pass\r
- ----------------------------------------------------------------- */\r
- \r
- /* walk lightmaps */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- /* early out */\r
- if( lm->superLuxels[ lightmapNum ] == NULL )\r
- continue;\r
- \r
- /* average occluded luxels from neighbors */\r
- for( y = 0; y < lm->sh; y++ )\r
- {\r
- for( x = 0; x < lm->sw; x++ )\r
- {\r
- /* get particulars */\r
- cluster = SUPER_CLUSTER( x, y );\r
- luxel = SUPER_LUXEL( lightmapNum, x, y );\r
- deluxel = SUPER_DELUXEL( x, y );\r
- normal = SUPER_NORMAL( x, y );\r
- \r
- /* determine if filtering is necessary */\r
- filterColor = qfalse;\r
- filterDir = qfalse;\r
- if( *cluster < 0 ||\r
- (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )\r
- filterColor = qtrue;\r
- if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )\r
- filterDir = qtrue;\r
- \r
- if( !filterColor && !filterDir )\r
- continue;\r
- \r
- /* choose seed amount */\r
- VectorClear( averageColor );\r
- VectorClear( averageDir );\r
- samples = 0;\r
- \r
- /* walk 3x3 matrix */\r
- for( sy = (y - 1); sy <= (y + 1); sy++ )\r
- {\r
- if( sy < 0 || sy >= lm->sh )\r
- continue;\r
- \r
- for( sx = (x - 1); sx <= (x + 1); sx++ )\r
- {\r
- if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )\r
- continue;\r
- \r
- /* get neighbor's particulars */\r
- cluster2 = SUPER_CLUSTER( sx, sy );\r
- luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );\r
- deluxel2 = SUPER_DELUXEL( sx, sy );\r
- \r
- /* ignore unmapped/unlit luxels */\r
- if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||\r
- (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )\r
- continue;\r
- \r
- /* add its distinctiveness to our own */\r
- VectorAdd( averageColor, luxel2, averageColor );\r
- samples += luxel2[ 3 ];\r
- if( filterDir )\r
- VectorAdd( averageDir, deluxel2, averageDir );\r
- }\r
- }\r
- \r
- /* fall through */\r
- if( samples == 0.0f )\r
- continue;\r
- \r
- /* average it */\r
- if( filterColor )\r
- {\r
- VectorDivide( averageColor, samples, luxel );\r
- luxel[ 3 ] = 1.0f;\r
- }\r
- if( filterDir )\r
- VectorDivide( averageDir, samples, deluxel );\r
- \r
- /* set cluster to -3 */\r
- if( *cluster < 0 )\r
- *cluster = CLUSTER_FLOODED;\r
- }\r
- }\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-IlluminateVertexes()\r
-light the surface vertexes\r
-*/\r
-\r
-#define VERTEX_NUDGE 2.0f\r
-\r
-void IlluminateVertexes( int num )\r
-{\r
- int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;\r
- int lightmapNum;\r
- float samples, *vertLuxel, *radVertLuxel, *luxel;\r
- vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ];\r
- bspDrawSurface_t *ds;\r
- surfaceInfo_t *info;\r
- rawLightmap_t *lm;\r
- bspDrawVert_t *verts;\r
- trace_t trace;\r
- \r
- \r
- /* der... */\r
- if( noVertexLighting )\r
- return;\r
- \r
- /* get surface, info, and raw lightmap */\r
- ds = &bspDrawSurfaces[ num ];\r
- info = &surfaceInfos[ num ];\r
- lm = info->lm;\r
- \r
- /* -----------------------------------------------------------------\r
- illuminate the vertexes\r
- ----------------------------------------------------------------- */\r
- \r
- /* calculate vertex lighting for surfaces without lightmaps */\r
- if( lm == NULL )\r
- {\r
- /* setup trace */\r
- trace.testOcclusion = !noTrace;\r
- trace.forceSunlight = info->si->forceSunlight;\r
- trace.recvShadows = info->recvShadows;\r
- trace.numSurfaces = 1;\r
- trace.surfaces = #\r
- trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;\r
- \r
- /* twosided lighting */\r
- trace.twoSided = info->si->twoSided;\r
- \r
- /* make light list for this surface */\r
- CreateTraceLightsForSurface( num, &trace );\r
- \r
- /* walk the surface verts */\r
- verts = yDrawVerts + ds->firstVert;\r
- for( i = 0; i < ds->numVerts; i++ )\r
- {\r
- /* get vertex luxel */\r
- radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );\r
- \r
- /* color the luxel with raw lightmap num? */\r
- if( debugSurfaces )\r
- VectorCopy( debugColors[ num % 12 ], radVertLuxel );\r
- \r
- /* color the luxel with luxel origin? */\r
- else if( debugOrigin )\r
- {\r
- VectorSubtract( info->maxs, info->mins, temp );\r
- VectorScale( temp, (1.0f / 255.0f), temp );\r
- VectorSubtract( origin, lm->mins, temp2 );\r
- radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);\r
- radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);\r
- radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);\r
- }\r
- \r
- /* color the luxel with the normal */\r
- else if( normalmap )\r
- {\r
- radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;\r
- radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;\r
- radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;\r
- }\r
- \r
- /* illuminate the vertex */\r
- else\r
- {\r
- /* clear vertex luxel */\r
- VectorCopy( ambientColor, radVertLuxel );\r
- \r
- /* try at initial origin */\r
- trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );\r
- if( trace.cluster >= 0 )\r
- {\r
- /* setup trace */\r
- VectorCopy( verts[ i ].xyz, trace.origin );\r
- VectorCopy( verts[ i ].normal, trace.normal );\r
- \r
- /* trace */\r
- LightingAtSample( &trace, ds->vertexStyles, colors );\r
- \r
- /* store */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );\r
- VectorCopy( colors[ lightmapNum ], radVertLuxel );\r
- }\r
- }\r
- \r
- /* is this sample bright enough? */\r
- if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&\r
- radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&\r
- radVertLuxel[ 2 ] <= ambientColor[ 2 ] )\r
- {\r
- /* nudge the sample point around a bit */\r
- for( x = 0; x < 4; x++ )\r
- {\r
- /* two's complement 0, 1, -1, 2, -2, etc */\r
- x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);\r
- \r
- for( y = 0; y < 4; y++ )\r
- {\r
- y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);\r
- \r
- for( z = 0; z < 4; z++ )\r
- {\r
- z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);\r
- \r
- /* nudge origin */\r
- trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);\r
- trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);\r
- trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);\r
- \r
- /* try at nudged origin */\r
- trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );\r
- if( trace.cluster < 0 )\r
- continue;\r
- \r
- /* trace */\r
- LightingAtSample( &trace, ds->vertexStyles, colors );\r
- \r
- /* store */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );\r
- VectorCopy( colors[ lightmapNum ], radVertLuxel );\r
- }\r
- \r
- /* bright enough? */\r
- radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );\r
- if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||\r
- radVertLuxel[ 1 ] > ambientColor[ 1 ] ||\r
- radVertLuxel[ 2 ] > ambientColor[ 2 ] )\r
- x = y = z = 1000;\r
- }\r
- }\r
- }\r
- }\r
- }\r
- \r
- /* another happy customer */\r
- numVertsIlluminated++;\r
- \r
- /* store it */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- /* get luxels */\r
- vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );\r
- radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );\r
- \r
- /* store */\r
- if( bouncing || bounce == 0 || !bounceOnly )\r
- VectorAdd( vertLuxel, radVertLuxel, vertLuxel );\r
- ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );\r
- }\r
- }\r
- \r
- /* free light list */\r
- FreeTraceLights( &trace );\r
- \r
- /* return to sender */\r
- return;\r
- }\r
- \r
- /* -----------------------------------------------------------------\r
- reconstitute vertex lighting from the luxels\r
- ----------------------------------------------------------------- */\r
- \r
- /* set styles from lightmap */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];\r
- \r
- /* get max search radius */\r
- maxRadius = lm->sw;\r
- maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;\r
- \r
- /* walk the surface verts */\r
- verts = yDrawVerts + ds->firstVert;\r
- for( i = 0; i < ds->numVerts; i++ )\r
- {\r
- /* do each lightmap */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- /* early out */\r
- if( lm->superLuxels[ lightmapNum ] == NULL )\r
- continue;\r
- \r
- /* get luxel coords */\r
- x = verts[ i ].lightmap[ lightmapNum ][ 0 ];\r
- y = verts[ i ].lightmap[ lightmapNum ][ 1 ];\r
- if( x < 0 )\r
- x = 0;\r
- else if( x >= lm->sw )\r
- x = lm->sw - 1;\r
- if( y < 0 )\r
- y = 0;\r
- else if( y >= lm->sh )\r
- y = lm->sh - 1;\r
- \r
- /* get vertex luxels */\r
- vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );\r
- radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );\r
- \r
- /* color the luxel with the normal? */\r
- if( normalmap )\r
- {\r
- radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;\r
- radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;\r
- radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;\r
- }\r
- \r
- /* color the luxel with surface num? */\r
- else if( debugSurfaces )\r
- VectorCopy( debugColors[ num % 12 ], radVertLuxel );\r
- \r
- /* divine color from the superluxels */\r
- else\r
- {\r
- /* increasing radius */\r
- VectorClear( radVertLuxel );\r
- samples = 0.0f;\r
- for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )\r
- {\r
- /* sample within radius */\r
- for( sy = (y - radius); sy <= (y + radius); sy++ )\r
- {\r
- if( sy < 0 || sy >= lm->sh )\r
- continue;\r
- \r
- for( sx = (x - radius); sx <= (x + radius); sx++ )\r
- {\r
- if( sx < 0 || sx >= lm->sw )\r
- continue;\r
- \r
- /* get luxel particulars */\r
- luxel = SUPER_LUXEL( lightmapNum, sx, sy );\r
- cluster = SUPER_CLUSTER( sx, sy );\r
- if( *cluster < 0 )\r
- continue;\r
- \r
- /* testing: must be brigher than ambient color */\r
- //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )\r
- //% continue;\r
- \r
- /* add its distinctiveness to our own */\r
- VectorAdd( radVertLuxel, luxel, radVertLuxel );\r
- samples += luxel[ 3 ];\r
- }\r
- }\r
- }\r
- \r
- /* any color? */\r
- if( samples > 0.0f )\r
- VectorDivide( radVertLuxel, samples, radVertLuxel );\r
- else\r
- VectorCopy( ambientColor, radVertLuxel );\r
- }\r
- \r
- /* store into floating point storage */\r
- VectorAdd( vertLuxel, radVertLuxel, vertLuxel );\r
- numVertsIlluminated++;\r
- \r
- /* store into bytes (for vertex approximation) */\r
- ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );\r
- }\r
- }\r
-}\r
-\r
-\r
-\r
-/* -------------------------------------------------------------------------------\r
-\r
-light optimization (-fast)\r
-\r
-creates a list of lights that will affect a surface and stores it in tw\r
-this is to optimize surface lighting by culling out as many of the\r
-lights in the world as possible from further calculation\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-/*\r
-SetupBrushes()\r
-determines opaque brushes in the world and find sky shaders for sunlight calculations\r
-*/\r
-\r
-void SetupBrushes( void )\r
-{\r
- int i, j, b, compileFlags;\r
- qboolean inside;\r
- bspBrush_t *brush;\r
- bspBrushSide_t *side;\r
- bspShader_t *shader;\r
- shaderInfo_t *si;\r
- \r
- \r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );\r
- \r
- /* allocate */\r
- if( opaqueBrushes == NULL )\r
- opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );\r
- \r
- /* clear */\r
- memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );\r
- numOpaqueBrushes = 0;\r
- \r
- /* walk the list of worldspawn brushes */\r
- for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )\r
- {\r
- /* get brush */\r
- b = bspModels[ 0 ].firstBSPBrush + i;\r
- brush = &bspBrushes[ b ];\r
- \r
- /* check all sides */\r
- inside = qtrue;\r
- compileFlags = 0;\r
- for( j = 0; j < brush->numSides && inside; j++ )\r
- {\r
- /* do bsp shader calculations */\r
- side = &bspBrushSides[ brush->firstSide + j ];\r
- shader = &bspShaders[ side->shaderNum ];\r
- \r
- /* get shader info */\r
- si = ShaderInfoForShader( shader->shader );\r
- if( si == NULL )\r
- continue;\r
- \r
- /* or together compile flags */\r
- compileFlags |= si->compileFlags;\r
- }\r
- \r
- /* determine if this brush is opaque to light */\r
- if( !(compileFlags & C_TRANSLUCENT) )\r
- {\r
- opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));\r
- numOpaqueBrushes++;\r
- maxOpaqueBrush = i;\r
- }\r
- }\r
- \r
- /* emit some statistics */\r
- Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );\r
-}\r
-\r
-\r
-\r
-/*\r
-ClusterVisible()\r
-determines if two clusters are visible to each other using the PVS\r
-*/\r
-\r
-qboolean ClusterVisible( int a, int b )\r
-{\r
- int portalClusters, leafBytes;\r
- byte *pvs;\r
- \r
- \r
- /* dummy check */\r
- if( a < 0 || b < 0 )\r
- return qfalse;\r
- \r
- /* early out */\r
- if( a == b )\r
- return qtrue;\r
- \r
- /* not vised? */\r
- if( numBSPVisBytes <=8 )\r
- return qtrue;\r
- \r
- /* get pvs data */\r
- portalClusters = ((int *) bspVisBytes)[ 0 ];\r
- leafBytes = ((int*) bspVisBytes)[ 1 ];\r
- pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);\r
- \r
- /* check */\r
- if( (pvs[ b >> 3 ] & (1 << (b & 7))) )\r
- return qtrue;\r
- return qfalse;\r
-}\r
-\r
-\r
-\r
-/*\r
-PointInLeafNum_r()\r
-borrowed from vlight.c\r
-*/\r
-\r
-int PointInLeafNum_r( vec3_t point, int nodenum )\r
-{\r
- int leafnum;\r
- vec_t dist;\r
- bspNode_t *node;\r
- bspPlane_t *plane;\r
- \r
- \r
- while( nodenum >= 0 )\r
- {\r
- node = &bspNodes[ nodenum ];\r
- plane = &bspPlanes[ node->planeNum ];\r
- dist = DotProduct( point, plane->normal ) - plane->dist;\r
- if( dist > 0.1 )\r
- nodenum = node->children[ 0 ];\r
- else if( dist < -0.1 )\r
- nodenum = node->children[ 1 ];\r
- else\r
- {\r
- leafnum = PointInLeafNum_r( point, node->children[ 0 ] );\r
- if( bspLeafs[ leafnum ].cluster != -1 )\r
- return leafnum;\r
- nodenum = node->children[ 1 ];\r
- }\r
- }\r
- \r
- leafnum = -nodenum - 1;\r
- return leafnum;\r
-}\r
-\r
-\r
-\r
-/*\r
-PointInLeafnum()\r
-borrowed from vlight.c\r
-*/\r
-\r
-int PointInLeafNum( vec3_t point )\r
-{\r
- return PointInLeafNum_r( point, 0 );\r
-}\r
-\r
-\r
-\r
-/*\r
-ClusterVisibleToPoint() - ydnar\r
-returns qtrue if point can "see" cluster\r
-*/\r
-\r
-qboolean ClusterVisibleToPoint( vec3_t point, int cluster )\r
-{\r
- int pointCluster;\r
- \r
-\r
- /* get leafNum for point */\r
- pointCluster = ClusterForPoint( point );\r
- if( pointCluster < 0 )\r
- return qfalse;\r
- \r
- /* check pvs */\r
- return ClusterVisible( pointCluster, cluster );\r
-}\r
-\r
-\r
-\r
-/*\r
-ClusterForPoint() - ydnar\r
-returns the pvs cluster for point\r
-*/\r
-\r
-int ClusterForPoint( vec3_t point )\r
-{\r
- int leafNum;\r
- \r
-\r
- /* get leafNum for point */\r
- leafNum = PointInLeafNum( point );\r
- if( leafNum < 0 )\r
- return -1;\r
- \r
- /* return the cluster */\r
- return bspLeafs[ leafNum ].cluster;\r
-}\r
-\r
-\r
-\r
-/*\r
-ClusterForPointExt() - ydnar\r
-also takes brushes into account for occlusion testing\r
-*/\r
-\r
-int ClusterForPointExt( vec3_t point, float epsilon )\r
-{\r
- int i, j, b, leafNum, cluster;\r
- float dot;\r
- qboolean inside;\r
- int *brushes, numBSPBrushes;\r
- bspLeaf_t *leaf;\r
- bspBrush_t *brush;\r
- bspPlane_t *plane;\r
- \r
- \r
- /* get leaf for point */\r
- leafNum = PointInLeafNum( point );\r
- if( leafNum < 0 )\r
- return -1;\r
- leaf = &bspLeafs[ leafNum ];\r
- \r
- /* get the cluster */\r
- cluster = leaf->cluster;\r
- if( cluster < 0 )\r
- return -1;\r
- \r
- /* transparent leaf, so check point against all brushes in the leaf */\r
- brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];\r
- numBSPBrushes = leaf->numBSPLeafBrushes;\r
- for( i = 0; i < numBSPBrushes; i++ )\r
- {\r
- /* get parts */\r
- b = brushes[ i ];\r
- if( b > maxOpaqueBrush )\r
- continue;\r
- brush = &bspBrushes[ b ];\r
- if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )\r
- continue;\r
- \r
- /* check point against all planes */\r
- inside = qtrue;\r
- for( j = 0; j < brush->numSides && inside; j++ )\r
- {\r
- plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];\r
- dot = DotProduct( point, plane->normal );\r
- dot -= plane->dist;\r
- if( dot > epsilon )\r
- inside = qfalse;\r
- }\r
- \r
- /* if inside, return bogus cluster */\r
- if( inside )\r
- return -1 - b;\r
- }\r
- \r
- /* if the point made it this far, it's not inside any opaque brushes */\r
- return cluster;\r
-}\r
-\r
-\r
-\r
-/*\r
-ClusterForPointExtFilter() - ydnar\r
-adds cluster checking against a list of known valid clusters\r
-*/\r
-\r
-int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )\r
-{\r
- int i, cluster;\r
- \r
- \r
- /* get cluster for point */\r
- cluster = ClusterForPointExt( point, epsilon );\r
- \r
- /* check if filtering is necessary */\r
- if( cluster < 0 || numClusters <= 0 || clusters == NULL )\r
- return cluster;\r
- \r
- /* filter */\r
- for( i = 0; i < numClusters; i++ )\r
- {\r
- if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )\r
- return cluster;\r
- }\r
- \r
- /* failed */\r
- return -1;\r
-}\r
-\r
-\r
-\r
-/*\r
-ShaderForPointInLeaf() - ydnar\r
-checks a point against all brushes in a leaf, returning the shader of the brush\r
-also sets the cumulative surface and content flags for the brush hit\r
-*/\r
-\r
-int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )\r
-{\r
- int i, j;\r
- float dot;\r
- qboolean inside;\r
- int *brushes, numBSPBrushes;\r
- bspLeaf_t *leaf;\r
- bspBrush_t *brush;\r
- bspBrushSide_t *side;\r
- bspPlane_t *plane;\r
- bspShader_t *shader;\r
- int allSurfaceFlags, allContentFlags;\r
-\r
- \r
- /* clear things out first */\r
- *surfaceFlags = 0;\r
- *contentFlags = 0;\r
- \r
- /* get leaf */\r
- if( leafNum < 0 )\r
- return -1;\r
- leaf = &bspLeafs[ leafNum ];\r
- \r
- /* transparent leaf, so check point against all brushes in the leaf */\r
- brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];\r
- numBSPBrushes = leaf->numBSPLeafBrushes;\r
- for( i = 0; i < numBSPBrushes; i++ )\r
- {\r
- /* get parts */\r
- brush = &bspBrushes[ brushes[ i ] ];\r
- \r
- /* check point against all planes */\r
- inside = qtrue;\r
- allSurfaceFlags = 0;\r
- allContentFlags = 0;\r
- for( j = 0; j < brush->numSides && inside; j++ )\r
- {\r
- side = &bspBrushSides[ brush->firstSide + j ];\r
- plane = &bspPlanes[ side->planeNum ];\r
- dot = DotProduct( point, plane->normal );\r
- dot -= plane->dist;\r
- if( dot > epsilon )\r
- inside = qfalse;\r
- else\r
- {\r
- shader = &bspShaders[ side->shaderNum ];\r
- allSurfaceFlags |= shader->surfaceFlags;\r
- allContentFlags |= shader->contentFlags;\r
- }\r
- }\r
- \r
- /* handle if inside */\r
- if( inside )\r
- {\r
- /* if there are desired flags, check for same and continue if they aren't matched */\r
- if( wantContentFlags && !(wantContentFlags & allContentFlags) )\r
- continue;\r
- if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )\r
- continue;\r
- \r
- /* store the cumulative flags and return the brush shader (which is mostly useless) */\r
- *surfaceFlags = allSurfaceFlags;\r
- *contentFlags = allContentFlags;\r
- return brush->shaderNum;\r
- }\r
- }\r
- \r
- /* if the point made it this far, it's not inside any brushes */\r
- return -1;\r
-}\r
-\r
-\r
-\r
-/*\r
-ChopBounds()\r
-chops a bounding box by the plane defined by origin and normal\r
-returns qfalse if the bounds is entirely clipped away\r
-\r
-this is not exactly the fastest way to do this...\r
-*/\r
-\r
-qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )\r
-{\r
- /* FIXME: rewrite this so it doesn't use bloody brushes */\r
- return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-SetupEnvelopes()\r
-calculates each light's effective envelope,\r
-taking into account brightness, type, and pvs.\r
-*/\r
-\r
-#define LIGHT_EPSILON 0.125f\r
-#define LIGHT_NUDGE 2.0f\r
-\r
-void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )\r
-{\r
- int i, x, y, z, x1, y1, z1;\r
- light_t *light, *light2, **owner;\r
- bspLeaf_t *leaf;\r
- vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };\r
- float radius, intensity;\r
- light_t *buckets[ 256 ];\r
- \r
- \r
- /* early out for weird cases where there are no lights */\r
- if( lights == NULL )\r
- return;\r
- \r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );\r
- \r
- /* count lights */\r
- numLights = 0;\r
- numCulledLights = 0;\r
- owner = &lights;\r
- while( *owner != NULL )\r
- {\r
- /* get light */\r
- light = *owner;\r
- \r
- /* handle negative lights */\r
- if( light->photons < 0.0f || light->add < 0.0f )\r
- {\r
- light->photons *= -1.0f;\r
- light->add *= -1.0f;\r
- light->flags |= LIGHT_NEGATIVE;\r
- }\r
- \r
- /* sunlight? */\r
- if( light->type == EMIT_SUN )\r
- {\r
- /* special cased */\r
- light->cluster = 0;\r
- light->envelope = MAX_WORLD_COORD * 8.0f;\r
- VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );\r
- VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );\r
- }\r
- \r
- /* everything else */\r
- else\r
- {\r
- /* get pvs cluster for light */\r
- light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );\r
- \r
- /* invalid cluster? */\r
- if( light->cluster < 0 )\r
- {\r
- /* nudge the sample point around a bit */\r
- for( x = 0; x < 4; x++ )\r
- {\r
- /* two's complement 0, 1, -1, 2, -2, etc */\r
- x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);\r
- \r
- for( y = 0; y < 4; y++ )\r
- {\r
- y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);\r
- \r
- for( z = 0; z < 4; z++ )\r
- {\r
- z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);\r
- \r
- /* nudge origin */\r
- origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);\r
- origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);\r
- origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);\r
- \r
- /* try at nudged origin */\r
- light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );\r
- if( light->cluster < 0 )\r
- continue;\r
- \r
- /* set origin */\r
- VectorCopy( origin, light->origin );\r
- }\r
- }\r
- }\r
- }\r
- \r
- /* only calculate for lights in pvs and outside of opaque brushes */\r
- if( light->cluster >= 0 )\r
- {\r
- /* set light fast flag */\r
- if( fastFlag )\r
- light->flags |= LIGHT_FAST_TEMP;\r
- else\r
- light->flags &= ~LIGHT_FAST_TEMP;\r
- if( light->si && light->si->noFast )\r
- light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);\r
- \r
- /* clear light envelope */\r
- light->envelope = 0;\r
- \r
- /* handle area lights */\r
- if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )\r
- {\r
- /* ugly hack to calculate extent for area lights, but only done once */\r
- VectorScale( light->normal, -1.0f, dir );\r
- for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )\r
- {\r
- float factor;\r
- \r
- VectorMA( light->origin, radius, light->normal, origin );\r
- factor = PointToPolygonFormFactor( origin, dir, light->w );\r
- if( factor < 0.0f )\r
- factor *= -1.0f;\r
- if( (factor * light->add) <= light->falloffTolerance )\r
- light->envelope = radius;\r
- }\r
- \r
- /* check for fast mode */\r
- if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )\r
- light->envelope = MAX_WORLD_COORD * 8.0f;\r
- }\r
- else\r
- {\r
- radius = 0.0f;\r
- intensity = light->photons;\r
- }\r
- \r
- /* other calcs */\r
- if( light->envelope <= 0.0f )\r
- {\r
- /* solve distance for non-distance lights */\r
- if( !(light->flags & LIGHT_ATTEN_DISTANCE) )\r
- light->envelope = MAX_WORLD_COORD * 8.0f;\r
- \r
- /* solve distance for linear lights */\r
- else if( (light->flags & LIGHT_ATTEN_LINEAR ) )\r
- //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;\r
- light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;\r
-\r
- /*\r
- add = angle * light->photons * linearScale - (dist * light->fade);\r
- T = (light->photons * linearScale) - (dist * light->fade);\r
- T + (dist * light->fade) = (light->photons * linearScale);\r
- dist * light->fade = (light->photons * linearScale) - T;\r
- dist = ((light->photons * linearScale) - T) / light->fade;\r
- */\r
- \r
- /* solve for inverse square falloff */\r
- else\r
- light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;\r
- \r
- /*\r
- add = light->photons / (dist * dist);\r
- T = light->photons / (dist * dist);\r
- T * (dist * dist) = light->photons;\r
- dist = sqrt( light->photons / T );\r
- */\r
- }\r
- \r
- /* chop radius against pvs */\r
- {\r
- /* clear bounds */\r
- ClearBounds( mins, maxs );\r
- \r
- /* check all leaves */\r
- for( i = 0; i < numBSPLeafs; i++ )\r
- {\r
- /* get test leaf */\r
- leaf = &bspLeafs[ i ];\r
- \r
- /* in pvs? */\r
- if( leaf->cluster < 0 )\r
- continue;\r
- if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */\r
- continue;\r
- \r
- /* add this leafs bbox to the bounds */\r
- VectorCopy( leaf->mins, origin );\r
- AddPointToBounds( origin, mins, maxs );\r
- VectorCopy( leaf->maxs, origin );\r
- AddPointToBounds( origin, mins, maxs );\r
- }\r
- \r
- /* test to see if bounds encompass light */\r
- for( i = 0; i < 3; i++ )\r
- {\r
- if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )\r
- {\r
- //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",\r
- //% mins[ 0 ], mins[ 1 ], mins[ 2 ],\r
- //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],\r
- //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );\r
- AddPointToBounds( light->origin, mins, maxs );\r
- }\r
- }\r
- \r
- /* chop the bounds by a plane for area lights and spotlights */\r
- if( light->type == EMIT_AREA || light->type == EMIT_SPOT )\r
- ChopBounds( mins, maxs, light->origin, light->normal );\r
- \r
- /* copy bounds */\r
- VectorCopy( mins, light->mins );\r
- VectorCopy( maxs, light->maxs );\r
- \r
- /* reflect bounds around light origin */\r
- //% VectorMA( light->origin, -1.0f, origin, origin );\r
- VectorScale( light->origin, 2, origin );\r
- VectorSubtract( origin, maxs, origin );\r
- AddPointToBounds( origin, mins, maxs );\r
- //% VectorMA( light->origin, -1.0f, mins, origin );\r
- VectorScale( light->origin, 2, origin );\r
- VectorSubtract( origin, mins, origin );\r
- AddPointToBounds( origin, mins, maxs );\r
- \r
- /* calculate spherical bounds */\r
- VectorSubtract( maxs, light->origin, dir );\r
- radius = (float) VectorLength( dir );\r
- \r
- /* if this radius is smaller than the envelope, then set the envelope to it */\r
- if( radius < light->envelope )\r
- {\r
- light->envelope = radius;\r
- //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );\r
- }\r
- //% else\r
- //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );\r
- }\r
- \r
- /* add grid/surface only check */\r
- if( forGrid )\r
- {\r
- if( !(light->flags & LIGHT_GRID) )\r
- light->envelope = 0.0f;\r
- }\r
- else\r
- {\r
- if( !(light->flags & LIGHT_SURFACES) )\r
- light->envelope = 0.0f;\r
- }\r
- }\r
- \r
- /* culled? */\r
- if( light->cluster < 0 || light->envelope <= 0.0f )\r
- {\r
- /* debug code */\r
- //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );\r
- \r
- /* delete the light */\r
- numCulledLights++;\r
- *owner = light->next;\r
- if( light->w != NULL )\r
- free( light->w );\r
- free( light );\r
- continue;\r
- }\r
- }\r
- \r
- /* square envelope */\r
- light->envelope2 = (light->envelope * light->envelope);\r
- \r
- /* increment light count */\r
- numLights++;\r
- \r
- /* set next light */\r
- owner = &((**owner).next);\r
- }\r
- \r
- /* bucket sort lights by style */\r
- memset( buckets, 0, sizeof( buckets ) );\r
- light2 = NULL;\r
- for( light = lights; light != NULL; light = light2 )\r
- {\r
- /* get next light */\r
- light2 = light->next;\r
- \r
- /* filter into correct bucket */\r
- light->next = buckets[ light->style ];\r
- buckets[ light->style ] = light;\r
- }\r
- \r
- /* filter back into light list */\r
- lights = NULL;\r
- for( i = 255; i >= 0; i-- )\r
- {\r
- light2 = NULL;\r
- for( light = buckets[ i ]; light != NULL; light = light2 )\r
- {\r
- light2 = light->next;\r
- light->next = lights;\r
- lights = light;\r
- }\r
- }\r
- \r
- /* emit some statistics */\r
- Sys_Printf( "%9d total lights\n", numLights );\r
- Sys_Printf( "%9d culled lights\n", numCulledLights );\r
-}\r
-\r
-\r
-\r
-/*\r
-CreateTraceLightsForBounds()\r
-creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)\r
-*/\r
-\r
-void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )\r
-{\r
- int i;\r
- light_t *light;\r
- vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };\r
- float radius, dist, length;\r
- \r
- \r
- /* potential pre-setup */\r
- if( numLights == 0 )\r
- SetupEnvelopes( qfalse, fast );\r
- \r
- /* debug code */\r
- //% 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 ] );\r
- \r
- /* allocate the light list */\r
- trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );\r
- trace->numLights = 0;\r
- \r
- /* calculate spherical bounds */\r
- VectorAdd( mins, maxs, origin );\r
- VectorScale( origin, 0.5f, origin );\r
- VectorSubtract( maxs, origin, dir );\r
- radius = (float) VectorLength( dir );\r
- \r
- /* get length of normal vector */\r
- if( normal != NULL )\r
- length = VectorLength( normal );\r
- else\r
- {\r
- normal = nullVector;\r
- length = 0;\r
- }\r
- \r
- /* test each light and see if it reaches the sphere */\r
- /* note: the attenuation code MUST match LightingAtSample() */\r
- for( light = lights; light; light = light->next )\r
- {\r
- /* check zero sized envelope */\r
- if( light->envelope <= 0 )\r
- {\r
- lightsEnvelopeCulled++;\r
- continue;\r
- }\r
- \r
- /* check flags */\r
- if( !(light->flags & flags) )\r
- continue;\r
- \r
- /* sunlight skips all this nonsense */\r
- if( light->type != EMIT_SUN )\r
- {\r
- /* sun only? */\r
- if( sunOnly )\r
- continue;\r
- \r
- /* check against pvs cluster */\r
- if( numClusters > 0 && clusters != NULL )\r
- {\r
- for( i = 0; i < numClusters; i++ )\r
- {\r
- if( ClusterVisible( light->cluster, clusters[ i ] ) )\r
- break;\r
- }\r
- \r
- /* fixme! */\r
- if( i == numClusters )\r
- {\r
- lightsClusterCulled++;\r
- continue;\r
- }\r
- }\r
- \r
- /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */\r
- VectorSubtract( light->origin, origin, dir );\r
- dist = VectorLength( dir );\r
- dist -= light->envelope;\r
- dist -= radius;\r
- if( dist > 0 )\r
- {\r
- lightsEnvelopeCulled++;\r
- continue;\r
- }\r
- \r
- /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */\r
- #if 0\r
- skip = qfalse;\r
- for( i = 0; i < 3; i++ )\r
- {\r
- if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )\r
- skip = qtrue;\r
- }\r
- if( skip )\r
- {\r
- lightsBoundsCulled++;\r
- continue;\r
- }\r
- #endif\r
- }\r
- \r
- /* planar surfaces (except twosided surfaces) have a couple more checks */\r
- if( length > 0.0f && trace->twoSided == qfalse )\r
- {\r
- /* lights coplanar with a surface won't light it */\r
- if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )\r
- {\r
- lightsPlaneCulled++;\r
- continue;\r
- }\r
- \r
- /* check to see if light is behind the plane */\r
- if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )\r
- {\r
- lightsPlaneCulled++;\r
- continue;\r
- }\r
- }\r
- \r
- /* add this light */\r
- trace->lights[ trace->numLights++ ] = light;\r
- }\r
- \r
- /* make last night null */\r
- trace->lights[ trace->numLights ] = NULL;\r
-}\r
-\r
-\r
-\r
-void FreeTraceLights( trace_t *trace )\r
-{\r
- if( trace->lights != NULL )\r
- free( trace->lights );\r
-}\r
-\r
-\r
-\r
-/*\r
-CreateTraceLightsForSurface()\r
-creates a list of lights that can potentially affect a drawsurface\r
-*/\r
-\r
-void CreateTraceLightsForSurface( int num, trace_t *trace )\r
-{\r
- int i;\r
- vec3_t mins, maxs, normal;\r
- bspDrawVert_t *dv;\r
- bspDrawSurface_t *ds;\r
- surfaceInfo_t *info;\r
- \r
- \r
- /* dummy check */\r
- if( num < 0 )\r
- return;\r
- \r
- /* get drawsurface and info */\r
- ds = &bspDrawSurfaces[ num ];\r
- info = &surfaceInfos[ num ];\r
- \r
- /* get the mins/maxs for the dsurf */\r
- ClearBounds( mins, maxs );\r
- VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );\r
- for( i = 0; i < ds->numVerts; i++ )\r
- {\r
- dv = &yDrawVerts[ ds->firstVert + i ];\r
- AddPointToBounds( dv->xyz, mins, maxs );\r
- if( !VectorCompare( dv->normal, normal ) )\r
- VectorClear( normal );\r
- }\r
- \r
- /* create the lights for the bounding box */\r
- CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );\r
-}\r
-\r
-\r
-\r
-\r
-\r
+/*
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
+For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+----------------------------------------------------------------------------------
+
+This code has been altered significantly from its original form, to support
+several games based on the Quake III Arena engine, in the form of "Q3Map2."
+
+------------------------------------------------------------------------------- */
+
+
+
+/* marker */
+#define LIGHT_YDNAR_C
+
+
+
+/* dependencies */
+#include "q3map2.h"
+
+
+
+
+/*
+ColorToBytes()
+ydnar: moved to here 2001-02-04
+*/
+
+void ColorToBytes( const float *color, byte *colorBytes, float scale )
+{
+ float max;
+ vec3_t sample;
+
+
+ /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
+ if( scale <= 0.0f )
+ scale = 1.0f;
+
+ /* make a local copy */
+ VectorScale( color, scale, sample );
+
+ /* handle negative light */
+ if( sample[ 0 ] < 0.0f )
+ sample[ 0 ] = 0.0f;
+ if( sample[ 1 ] < 0.0f )
+ sample[ 1 ] = 0.0f;
+ if( sample[ 2 ] < 0.0f )
+ sample[ 2 ] = 0.0f;
+
+ /* clamp with color normalization */
+ max = sample[ 0 ];
+ if( sample[ 1 ] > max )
+ max = sample[ 1 ];
+ if( sample[ 2 ] > max )
+ max = sample[ 2 ];
+ if( max > 255.0f )
+ VectorScale( sample, (255.0f / max), sample );
+
+ /* store it off */
+ colorBytes[ 0 ] = sample[ 0 ];
+ colorBytes[ 1 ] = sample[ 1 ];
+ colorBytes[ 2 ] = sample[ 2 ];
+}
+
+
+
+/* -------------------------------------------------------------------------------
+
+this section deals with phong shading (normal interpolation across brush faces)
+
+------------------------------------------------------------------------------- */
+
+/*
+SmoothNormals()
+smooths together coincident vertex normals across the bsp
+*/
+
+#define MAX_SAMPLES 256
+#define THETA_EPSILON 0.000001
+#define EQUAL_NORMAL_EPSILON 0.01
+
+void SmoothNormals( void )
+{
+ int i, j, k, f, cs, numVerts, numVotes, fOld, start;
+ float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
+ bspDrawSurface_t *ds;
+ shaderInfo_t *si;
+ float *shadeAngles;
+ byte *smoothed;
+ vec3_t average, diff;
+ int indexes[ MAX_SAMPLES ];
+ vec3_t votes[ MAX_SAMPLES ];
+
+
+ /* allocate shade angle table */
+ shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
+ memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
+
+ /* allocate smoothed table */
+ cs = (numBSPDrawVerts / 8) + 1;
+ smoothed = safe_malloc( cs );
+ memset( smoothed, 0, cs );
+
+ /* set default shade angle */
+ defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
+ maxShadeAngle = 0;
+
+ /* run through every surface and flag verts belonging to non-lightmapped surfaces
+ and set per-vertex smoothing angle */
+ for( i = 0; i < numBSPDrawSurfaces; i++ )
+ {
+ /* get drawsurf */
+ ds = &bspDrawSurfaces[ i ];
+
+ /* get shader for shade angle */
+ si = surfaceInfos[ i ].si;
+ if( si->shadeAngleDegrees )
+ shadeAngle = DEG2RAD( si->shadeAngleDegrees );
+ else
+ shadeAngle = defaultShadeAngle;
+ if( shadeAngle > maxShadeAngle )
+ maxShadeAngle = shadeAngle;
+
+ /* flag its verts */
+ for( j = 0; j < ds->numVerts; j++ )
+ {
+ f = ds->firstVert + j;
+ shadeAngles[ f ] = shadeAngle;
+ if( ds->surfaceType == MST_TRIANGLE_SOUP )
+ smoothed[ f >> 3 ] |= (1 << (f & 7));
+ }
+
+ /* ydnar: optional force-to-trisoup */
+ if( trisoup && ds->surfaceType == MST_PLANAR )
+ {
+ ds->surfaceType = MST_TRIANGLE_SOUP;
+ ds->lightmapNum[ 0 ] = -3;
+ }
+ }
+
+ /* bail if no surfaces have a shade angle */
+ if( maxShadeAngle == 0 )
+ {
+ free( shadeAngles );
+ free( smoothed );
+ return;
+ }
+
+ /* init pacifier */
+ fOld = -1;
+ start = I_FloatTime();
+
+ /* go through the list of vertexes */
+ for( i = 0; i < numBSPDrawVerts; i++ )
+ {
+ /* print pacifier */
+ f = 10 * i / numBSPDrawVerts;
+ if( f != fOld )
+ {
+ fOld = f;
+ Sys_Printf( "%i...", f );
+ }
+
+ /* already smoothed? */
+ if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
+ continue;
+
+ /* clear */
+ VectorClear( average );
+ numVerts = 0;
+ numVotes = 0;
+
+ /* build a table of coincident vertexes */
+ for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
+ {
+ /* already smoothed? */
+ if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
+ continue;
+
+ /* test vertexes */
+ if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
+ continue;
+
+ /* use smallest shade angle */
+ shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
+
+ /* check shade angle */
+ dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
+ if( dot > 1.0 )
+ dot = 1.0;
+ else if( dot < -1.0 )
+ dot = -1.0;
+ testAngle = acos( dot ) + THETA_EPSILON;
+ if( testAngle >= shadeAngle )
+ {
+ //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
+ continue;
+ }
+ //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
+
+ /* add to the list */
+ indexes[ numVerts++ ] = j;
+
+ /* flag vertex */
+ smoothed[ j >> 3 ] |= (1 << (j & 7));
+
+ /* see if this normal has already been voted */
+ for( k = 0; k < numVotes; k++ )
+ {
+ VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
+ if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
+ fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
+ fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
+ break;
+ }
+
+ /* add a new vote? */
+ if( k == numVotes && numVotes < MAX_SAMPLES )
+ {
+ VectorAdd( average, bspDrawVerts[ j ].normal, average );
+ VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
+ numVotes++;
+ }
+ }
+
+ /* don't average for less than 2 verts */
+ if( numVerts < 2 )
+ continue;
+
+ /* average normal */
+ if( VectorNormalize( average, average ) > 0 )
+ {
+ /* smooth */
+ for( j = 0; j < numVerts; j++ )
+ VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
+ }
+ }
+
+ /* free the tables */
+ free( shadeAngles );
+ free( smoothed );
+
+ /* print time */
+ Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
+}
+
+
+
+/* -------------------------------------------------------------------------------
+
+this section deals with phong shaded lightmap tracing
+
+------------------------------------------------------------------------------- */
+
+/* 9th rewrite (recursive subdivision of a lightmap triangle) */
+
+/*
+CalcTangentVectors()
+calculates the st tangent vectors for normalmapping
+*/
+
+static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
+{
+ int i;
+ float bb, s, t;
+ vec3_t bary;
+
+
+ /* calculate barycentric basis for the triangle */
+ 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 ]);
+ if( fabs( bb ) < 0.00000001f )
+ return qfalse;
+
+ /* do each vertex */
+ for( i = 0; i < numVerts; i++ )
+ {
+ /* calculate s tangent vector */
+ s = dv[ i ]->st[ 0 ] + 10.0f;
+ t = dv[ i ]->st[ 1 ];
+ bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
+ bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
+ bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
+
+ stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
+ stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
+ stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
+
+ VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
+ VectorNormalize( stv[ i ], stv[ i ] );
+
+ /* calculate t tangent vector */
+ s = dv[ i ]->st[ 0 ];
+ t = dv[ i ]->st[ 1 ] + 10.0f;
+ bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
+ bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
+ bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
+
+ ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
+ ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
+ ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
+
+ VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
+ VectorNormalize( ttv[ i ], ttv[ i ] );
+
+ /* debug code */
+ //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
+ //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
+ }
+
+ /* return to caller */
+ return qtrue;
+}
+
+
+
+
+/*
+PerturbNormal()
+perterbs the normal by the shader's normalmap in tangent space
+*/
+
+static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
+{
+ int i;
+ vec4_t bump;
+
+
+ /* passthrough */
+ VectorCopy( dv->normal, pNormal );
+
+ /* sample normalmap */
+ if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
+ return;
+
+ /* remap sampled normal from [0,255] to [-1,-1] */
+ for( i = 0; i < 3; i++ )
+ bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
+
+ /* scale tangent vectors and add to original normal */
+ VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
+ VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
+ VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
+
+ /* renormalize and return */
+ VectorNormalize( pNormal, pNormal );
+}
+
+
+
+/*
+MapSingleLuxel()
+maps a luxel for triangle bv at
+*/
+
+#define NUDGE 0.5f
+#define BOGUS_NUDGE -99999.0f
+
+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 ] )
+{
+ int i, x, y, numClusters, *clusters, pointCluster, *cluster;
+ float *luxel, *origin, *normal, d, lightmapSampleOffset;
+ shaderInfo_t *si;
+ vec3_t pNormal;
+ vec3_t vecs[ 3 ];
+ vec3_t nudged;
+ float *nudge;
+ static float nudges[][ 2 ] =
+ {
+ //%{ 0, 0 }, /* try center first */
+ { -NUDGE, 0 }, /* left */
+ { NUDGE, 0 }, /* right */
+ { 0, NUDGE }, /* up */
+ { 0, -NUDGE }, /* down */
+ { -NUDGE, NUDGE }, /* left/up */
+ { NUDGE, -NUDGE }, /* right/down */
+ { NUDGE, NUDGE }, /* right/up */
+ { -NUDGE, -NUDGE }, /* left/down */
+ { BOGUS_NUDGE, BOGUS_NUDGE }
+ };
+
+
+ /* find luxel xy coords (fixme: subtract 0.5?) */
+ x = dv->lightmap[ 0 ][ 0 ];
+ y = dv->lightmap[ 0 ][ 1 ];
+ if( x < 0 )
+ x = 0;
+ else if( x >= lm->sw )
+ x = lm->sw - 1;
+ if( y < 0 )
+ y = 0;
+ else if( y >= lm->sh )
+ y = lm->sh - 1;
+
+ /* set shader and cluster list */
+ if( info != NULL )
+ {
+ si = info->si;
+ numClusters = info->numSurfaceClusters;
+ clusters = &surfaceClusters[ info->firstSurfaceCluster ];
+ }
+ else
+ {
+ si = NULL;
+ numClusters = 0;
+ clusters = NULL;
+ }
+
+ /* get luxel, origin, cluster, and normal */
+ luxel = SUPER_LUXEL( 0, x, y );
+ origin = SUPER_ORIGIN( x, y );
+ normal = SUPER_NORMAL( x, y );
+ cluster = SUPER_CLUSTER( x, y );
+
+ /* don't attempt to remap occluded luxels for planar surfaces */
+ if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
+ return (*cluster);
+
+ /* only average the normal for premapped luxels */
+ else if( (*cluster) >= 0 )
+ {
+ /* do bumpmap calculations */
+ if( stv != NULL )
+ PerturbNormal( dv, si, pNormal, stv, ttv );
+ else
+ VectorCopy( dv->normal, pNormal );
+
+ /* add the additional normal data */
+ VectorAdd( normal, pNormal, normal );
+ luxel[ 3 ] += 1.0f;
+ return (*cluster);
+ }
+
+ /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
+
+ /* get origin */
+
+ /* axial lightmap projection */
+ if( lm->vecs != NULL )
+ {
+ /* calculate an origin for the sample from the lightmap vectors */
+ VectorCopy( lm->origin, origin );
+ for( i = 0; i < 3; i++ )
+ {
+ /* add unless it's the axis, which is taken care of later */
+ if( i == lm->axisNum )
+ continue;
+ origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
+ }
+
+ /* project the origin onto the plane */
+ d = DotProduct( origin, plane ) - plane[ 3 ];
+ d /= plane[ lm->axisNum ];
+ origin[ lm->axisNum ] -= d;
+ }
+
+ /* non axial lightmap projection (explicit xyz) */
+ else
+ VectorCopy( dv->xyz, origin );
+
+ /* planar surfaces have precalculated lightmap vectors for nudging */
+ if( lm->plane != NULL )
+ {
+ VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
+ VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
+ VectorCopy( lm->plane, vecs[ 2 ] );
+ }
+
+ /* non-planar surfaces must calculate them */
+ else
+ {
+ if( plane != NULL )
+ VectorCopy( plane, vecs[ 2 ] );
+ else
+ VectorCopy( dv->normal, vecs[ 2 ] );
+ MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
+ }
+
+ /* push the origin off the surface a bit */
+ if( si != NULL )
+ lightmapSampleOffset = si->lightmapSampleOffset;
+ else
+ lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
+ if( lm->axisNum < 0 )
+ VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
+ else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
+ origin[ lm->axisNum ] -= lightmapSampleOffset;
+ else
+ origin[ lm->axisNum ] += lightmapSampleOffset;
+
+ /* get cluster */
+ pointCluster = ClusterForPointExtFilter( origin, LUXEL_EPSILON, numClusters, clusters );
+
+ /* another retarded hack, storing nudge count in luxel[ 1 ] */
+ luxel[ 1 ] = 0.0f;
+
+ /* point in solid? */
+ if( pointCluster < 0 )
+ {
+ /* nudge the the location around */
+ nudge = nudges[ 0 ];
+ while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
+ {
+ /* nudge the vector around a bit */
+ for( i = 0; i < 3; i++ )
+ {
+ /* set nudged point*/
+ nudged[ i ] = origin[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
+ }
+ nudge += 2;
+
+ /* get pvs cluster */
+ pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
+ if( pointCluster >= 0 )
+ VectorCopy( nudged, origin );
+ luxel[ 1 ] += 1.0f;
+ }
+ }
+
+ /* as a last resort, if still in solid, try drawvert origin offset by normal */
+ if( pointCluster < 0 && si != NULL )
+ {
+ VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
+ pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
+ if( pointCluster >= 0 )
+ VectorCopy( nudged, origin );
+ luxel[ 1 ] += 1.0f;
+ }
+
+ /* valid? */
+ if( pointCluster < 0 )
+ {
+ (*cluster) = CLUSTER_OCCLUDED;
+ VectorClear( origin );
+ VectorClear( normal );
+ numLuxelsOccluded++;
+ return (*cluster);
+ }
+
+ /* debug code */
+ //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
+
+ /* do bumpmap calculations */
+ if( stv )
+ PerturbNormal( dv, si, pNormal, stv, ttv );
+ else
+ VectorCopy( dv->normal, pNormal );
+
+ /* store the cluster and normal */
+ (*cluster) = pointCluster;
+ VectorCopy( pNormal, normal );
+
+ /* store explicit mapping pass and implicit mapping pass */
+ luxel[ 0 ] = pass;
+ luxel[ 3 ] = 1.0f;
+
+ /* add to count */
+ numLuxelsMapped++;
+
+ /* return ok */
+ return (*cluster);
+}
+
+
+
+/*
+MapTriangle_r()
+recursively subdivides a triangle until its edges are shorter
+than the distance between two luxels (thanks jc :)
+*/
+
+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 ] )
+{
+ bspDrawVert_t mid, *dv2[ 3 ];
+ int max;
+
+
+ /* map the vertexes */
+ #if 0
+ MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
+ MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
+ MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
+ #endif
+
+ /* subdivide calc */
+ {
+ int i;
+ float *a, *b, dx, dy, dist, maxDist;
+
+
+ /* find the longest edge and split it */
+ max = -1;
+ maxDist = 0;
+ for( i = 0; i < 3; i++ )
+ {
+ /* get verts */
+ a = dv[ i ]->lightmap[ 0 ];
+ b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
+
+ /* get dists */
+ dx = a[ 0 ] - b[ 0 ];
+ dy = a[ 1 ] - b[ 1 ];
+ dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
+
+ /* longer? */
+ if( dist > maxDist )
+ {
+ maxDist = dist;
+ max = i;
+ }
+ }
+
+ /* try to early out */
+ if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
+ return;
+ }
+
+ /* split the longest edge and map it */
+ LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
+ MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv );
+
+ /* push the point up a little bit to account for fp creep (fixme: revisit this) */
+ //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
+
+ /* recurse to first triangle */
+ VectorCopy( dv, dv2 );
+ dv2[ max ] = ∣
+ MapTriangle_r( lm, info, dv2, plane, stv, ttv );
+
+ /* recurse to second triangle */
+ VectorCopy( dv, dv2 );
+ dv2[ (max + 1) % 3 ] = ∣
+ MapTriangle_r( lm, info, dv2, plane, stv, ttv );
+}
+
+
+
+/*
+MapTriangle()
+seed function for MapTriangle_r()
+requires a cw ordered triangle
+*/
+
+static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
+{
+ int i;
+ vec4_t plane;
+ vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
+
+
+ /* get plane if possible */
+ if( lm->plane != NULL )
+ {
+ VectorCopy( lm->plane, plane );
+ plane[ 3 ] = lm->plane[ 3 ];
+ }
+
+ /* otherwise make one from the points */
+ else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
+ return qfalse;
+
+ /* check to see if we need to calculate texture->world tangent vectors */
+ if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
+ {
+ stv = stvStatic;
+ ttv = ttvStatic;
+ }
+ else
+ {
+ stv = NULL;
+ ttv = NULL;
+ }
+
+ /* map the vertexes */
+ MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
+ MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
+ MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
+
+ /* 2002-11-20: prefer axial triangle edges */
+ if( mapNonAxial )
+ {
+ /* subdivide the triangle */
+ MapTriangle_r( lm, info, dv, plane, stv, ttv );
+ return qtrue;
+ }
+
+ for( i = 0; i < 3; i++ )
+ {
+ float *a, *b;
+ bspDrawVert_t *dv2[ 3 ];
+
+
+ /* get verts */
+ a = dv[ i ]->lightmap[ 0 ];
+ b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
+
+ /* make degenerate triangles for mapping edges */
+ if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
+ {
+ dv2[ 0 ] = dv[ i ];
+ dv2[ 1 ] = dv[ (i + 1) % 3 ];
+ dv2[ 2 ] = dv[ (i + 1) % 3 ];
+
+ /* map the degenerate triangle */
+ MapTriangle_r( lm, info, dv2, plane, stv, ttv );
+ }
+ }
+
+ return qtrue;
+}
+
+
+
+/*
+MapQuad_r()
+recursively subdivides a quad until its edges are shorter
+than the distance between two luxels
+*/
+
+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 ] )
+{
+ bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
+ int max;
+
+
+ /* subdivide calc */
+ {
+ int i;
+ float *a, *b, dx, dy, dist, maxDist;
+
+
+ /* find the longest edge and split it */
+ max = -1;
+ maxDist = 0;
+ for( i = 0; i < 4; i++ )
+ {
+ /* get verts */
+ a = dv[ i ]->lightmap[ 0 ];
+ b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
+
+ /* get dists */
+ dx = a[ 0 ] - b[ 0 ];
+ dy = a[ 1 ] - b[ 1 ];
+ dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
+
+ /* longer? */
+ if( dist > maxDist )
+ {
+ maxDist = dist;
+ max = i;
+ }
+ }
+
+ /* try to early out */
+ if( max < 0 || maxDist <= subdivideThreshold )
+ return;
+ }
+
+ /* we only care about even/odd edges */
+ max &= 1;
+
+ /* split the longest edges */
+ LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
+ LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
+
+ /* map the vertexes */
+ MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv );
+ MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv );
+
+ /* 0 and 2 */
+ if( max == 0 )
+ {
+ /* recurse to first quad */
+ dv2[ 0 ] = dv[ 0 ];
+ dv2[ 1 ] = &mid[ 0 ];
+ dv2[ 2 ] = &mid[ 1 ];
+ dv2[ 3 ] = dv[ 3 ];
+ MapQuad_r( lm, info, dv2, plane, stv, ttv );
+
+ /* recurse to second quad */
+ dv2[ 0 ] = &mid[ 0 ];
+ dv2[ 1 ] = dv[ 1 ];
+ dv2[ 2 ] = dv[ 2 ];
+ dv2[ 3 ] = &mid[ 1 ];
+ MapQuad_r( lm, info, dv2, plane, stv, ttv );
+ }
+
+ /* 1 and 3 */
+ else
+ {
+ /* recurse to first quad */
+ dv2[ 0 ] = dv[ 0 ];
+ dv2[ 1 ] = dv[ 1 ];
+ dv2[ 2 ] = &mid[ 0 ];
+ dv2[ 3 ] = &mid[ 1 ];
+ MapQuad_r( lm, info, dv2, plane, stv, ttv );
+
+ /* recurse to second quad */
+ dv2[ 0 ] = &mid[ 1 ];
+ dv2[ 1 ] = &mid[ 0 ];
+ dv2[ 2 ] = dv[ 2 ];
+ dv2[ 3 ] = dv[ 3 ];
+ MapQuad_r( lm, info, dv2, plane, stv, ttv );
+ }
+}
+
+
+
+/*
+MapQuad()
+seed function for MapQuad_r()
+requires a cw ordered triangle quad
+*/
+
+#define QUAD_PLANAR_EPSILON 0.5f
+
+static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
+{
+ float dist;
+ vec4_t plane;
+ vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
+
+
+ /* get plane if possible */
+ if( lm->plane != NULL )
+ {
+ VectorCopy( lm->plane, plane );
+ plane[ 3 ] = lm->plane[ 3 ];
+ }
+
+ /* otherwise make one from the points */
+ else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
+ return qfalse;
+
+ /* 4th point must fall on the plane */
+ dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
+ if( fabs( dist ) > QUAD_PLANAR_EPSILON )
+ return qfalse;
+
+ /* check to see if we need to calculate texture->world tangent vectors */
+ if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
+ {
+ stv = stvStatic;
+ ttv = ttvStatic;
+ }
+ else
+ {
+ stv = NULL;
+ ttv = NULL;
+ }
+
+ /* map the vertexes */
+ MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
+ MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
+ MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
+ MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv );
+
+ /* subdivide the quad */
+ MapQuad_r( lm, info, dv, plane, stv, ttv );
+ return qtrue;
+}
+
+
+
+/*
+MapRawLightmap()
+maps the locations, normals, and pvs clusters for a raw lightmap
+*/
+
+#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)
+
+void MapRawLightmap( int rawLightmapNum )
+{
+ int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
+ float *luxel, *origin, *normal, samples, radius, pass;
+ rawLightmap_t *lm;
+ bspDrawSurface_t *ds;
+ surfaceInfo_t *info;
+ mesh_t src, *subdivided, *mesh;
+ bspDrawVert_t *verts, *dv[ 4 ], fake;
+
+
+ /* bail if this number exceeds the number of raw lightmaps */
+ if( rawLightmapNum >= numRawLightmaps )
+ return;
+
+ /* get lightmap */
+ lm = &rawLightmaps[ rawLightmapNum ];
+
+ /* -----------------------------------------------------------------
+ map referenced surfaces onto the raw lightmap
+ ----------------------------------------------------------------- */
+
+ /* walk the list of surfaces on this raw lightmap */
+ for( n = 0; n < lm->numLightSurfaces; n++ )
+ {
+ /* with > 1 surface per raw lightmap, clear occluded */
+ if( n > 0 )
+ {
+ for( y = 0; y < lm->sh; y++ )
+ {
+ for( x = 0; x < lm->sw; x++ )
+ {
+ /* get cluster */
+ cluster = SUPER_CLUSTER( x, y );
+ if( *cluster < 0 )
+ *cluster = CLUSTER_UNMAPPED;
+ }
+ }
+ }
+
+ /* get surface */
+ num = lightSurfaces[ lm->firstLightSurface + n ];
+ ds = &bspDrawSurfaces[ num ];
+ info = &surfaceInfos[ num ];
+
+ /* bail if no lightmap to calculate */
+ if( info->lm != lm )
+ {
+ Sys_Printf( "!" );
+ continue;
+ }
+
+ /* map the surface onto the lightmap origin/cluster/normal buffers */
+ switch( ds->surfaceType )
+ {
+ case MST_PLANAR:
+ /* get verts */
+ verts = yDrawVerts + ds->firstVert;
+
+ /* map the triangles */
+ for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
+ {
+ for( i = 0; i < ds->numIndexes; i += 3 )
+ {
+ dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
+ dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
+ dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
+ MapTriangle( lm, info, dv, mapNonAxial );
+ }
+ }
+ break;
+
+ case MST_PATCH:
+ /* make a mesh from the drawsurf */
+ src.width = ds->patchWidth;
+ src.height = ds->patchHeight;
+ src.verts = &yDrawVerts[ ds->firstVert ];
+ //% subdivided = SubdivideMesh( src, 8, 512 );
+ subdivided = SubdivideMesh2( src, info->patchIterations );
+
+ /* fit it to the curve and remove colinear verts on rows/columns */
+ PutMeshOnCurve( *subdivided );
+ mesh = RemoveLinearMeshColumnsRows( subdivided );
+ FreeMesh( subdivided );
+
+ /* get verts */
+ verts = mesh->verts;
+
+ /* debug code */
+ #if 0
+ if( lm->plane )
+ {
+ Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
+ lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
+ lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
+ lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
+ }
+ #endif
+
+ /* map the mesh quads */
+ #if 0
+
+ for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
+ {
+ for( y = 0; y < (mesh->height - 1); y++ )
+ {
+ for( x = 0; x < (mesh->width - 1); x++ )
+ {
+ /* set indexes */
+ pw[ 0 ] = x + (y * mesh->width);
+ pw[ 1 ] = x + ((y + 1) * mesh->width);
+ pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
+ pw[ 3 ] = x + 1 + (y * mesh->width);
+ pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
+
+ /* set radix */
+ r = (x + y) & 1;
+
+ /* get drawverts and map first triangle */
+ dv[ 0 ] = &verts[ pw[ r + 0 ] ];
+ dv[ 1 ] = &verts[ pw[ r + 1 ] ];
+ dv[ 2 ] = &verts[ pw[ r + 2 ] ];
+ MapTriangle( lm, info, dv, mapNonAxial );
+
+ /* get drawverts and map second triangle */
+ dv[ 0 ] = &verts[ pw[ r + 0 ] ];
+ dv[ 1 ] = &verts[ pw[ r + 2 ] ];
+ dv[ 2 ] = &verts[ pw[ r + 3 ] ];
+ MapTriangle( lm, info, dv, mapNonAxial );
+ }
+ }
+ }
+
+ #else
+
+ for( y = 0; y < (mesh->height - 1); y++ )
+ {
+ for( x = 0; x < (mesh->width - 1); x++ )
+ {
+ /* set indexes */
+ pw[ 0 ] = x + (y * mesh->width);
+ pw[ 1 ] = x + ((y + 1) * mesh->width);
+ pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
+ pw[ 3 ] = x + 1 + (y * mesh->width);
+ pw[ 4 ] = pw[ 0 ];
+
+ /* set radix */
+ r = (x + y) & 1;
+
+ /* attempt to map quad first */
+ dv[ 0 ] = &verts[ pw[ r + 0 ] ];
+ dv[ 1 ] = &verts[ pw[ r + 1 ] ];
+ dv[ 2 ] = &verts[ pw[ r + 2 ] ];
+ dv[ 3 ] = &verts[ pw[ r + 3 ] ];
+ if( MapQuad( lm, info, dv ) )
+ continue;
+
+ /* get drawverts and map first triangle */
+ MapTriangle( lm, info, dv, mapNonAxial );
+
+ /* get drawverts and map second triangle */
+ dv[ 1 ] = &verts[ pw[ r + 2 ] ];
+ dv[ 2 ] = &verts[ pw[ r + 3 ] ];
+ MapTriangle( lm, info, dv, mapNonAxial );
+ }
+ }
+
+ #endif
+
+ /* free the mesh */
+ FreeMesh( mesh );
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* -----------------------------------------------------------------
+ average and clean up luxel normals
+ ----------------------------------------------------------------- */
+
+ /* walk the luxels */
+ for( y = 0; y < lm->sh; y++ )
+ {
+ for( x = 0; x < lm->sw; x++ )
+ {
+ /* get luxel */
+ luxel = SUPER_LUXEL( 0, x, y );
+ normal = SUPER_NORMAL( x, y );
+ cluster = SUPER_CLUSTER( x, y );
+
+ /* only look at mapped luxels */
+ if( *cluster < 0 )
+ continue;
+
+ /* the normal data could be the sum of multiple samples */
+ if( luxel[ 3 ] > 1.0f )
+ VectorNormalize( normal, normal );
+
+ /* mark this luxel as having only one normal */
+ luxel[ 3 ] = 1.0f;
+ }
+ }
+
+ /* non-planar surfaces stop here */
+ if( lm->plane == NULL )
+ return;
+
+ /* -----------------------------------------------------------------
+ map occluded or unuxed luxels
+ ----------------------------------------------------------------- */
+
+ /* walk the luxels */
+ radius = floor( superSample / 2 );
+ radius = radius > 0 ? radius : 1.0f;
+ radius += 1.0f;
+ for( pass = 2.0f; pass <= radius; pass += 1.0f )
+ {
+ for( y = 0; y < lm->sh; y++ )
+ {
+ for( x = 0; x < lm->sw; x++ )
+ {
+ /* get luxel */
+ luxel = SUPER_LUXEL( 0, x, y );
+ normal = SUPER_NORMAL( x, y );
+ cluster = SUPER_CLUSTER( x, y );
+
+ /* only look at unmapped luxels */
+ if( *cluster != CLUSTER_UNMAPPED )
+ continue;
+
+ /* divine a normal and origin from neighboring luxels */
+ VectorClear( fake.xyz );
+ VectorClear( fake.normal );
+ fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
+ fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
+ samples = 0.0f;
+ for( sy = (y - 1); sy <= (y + 1); sy++ )
+ {
+ if( sy < 0 || sy >= lm->sh )
+ continue;
+
+ for( sx = (x - 1); sx <= (x + 1); sx++ )
+ {
+ if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
+ continue;
+
+ /* get neighboring luxel */
+ luxel = SUPER_LUXEL( 0, sx, sy );
+ origin = SUPER_ORIGIN( sx, sy );
+ normal = SUPER_NORMAL( sx, sy );
+ cluster = SUPER_CLUSTER( sx, sy );
+
+ /* only consider luxels mapped in previous passes */
+ if( *cluster < 0 || luxel[ 0 ] >= pass )
+ continue;
+
+ /* add its distinctiveness to our own */
+ VectorAdd( fake.xyz, origin, fake.xyz );
+ VectorAdd( fake.normal, normal, fake.normal );
+ samples += luxel[ 3 ];
+ }
+ }
+
+ /* any samples? */
+ if( samples == 0.0f )
+ continue;
+
+ /* average */
+ VectorDivide( fake.xyz, samples, fake.xyz );
+ //% VectorDivide( fake.normal, samples, fake.normal );
+ if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
+ continue;
+
+ /* map the fake vert */
+ MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL );
+ }
+ }
+ }
+
+ /* -----------------------------------------------------------------
+ average and clean up luxel normals
+ ----------------------------------------------------------------- */
+
+ /* walk the luxels */
+ for( y = 0; y < lm->sh; y++ )
+ {
+ for( x = 0; x < lm->sw; x++ )
+ {
+ /* get luxel */
+ luxel = SUPER_LUXEL( 0, x, y );
+ normal = SUPER_NORMAL( x, y );
+ cluster = SUPER_CLUSTER( x, y );
+
+ /* only look at mapped luxels */
+ if( *cluster < 0 )
+ continue;
+
+ /* the normal data could be the sum of multiple samples */
+ if( luxel[ 3 ] > 1.0f )
+ VectorNormalize( normal, normal );
+
+ /* mark this luxel as having only one normal */
+ luxel[ 3 ] = 1.0f;
+ }
+ }
+
+ /* debug code */
+ #if 0
+ Sys_Printf( "\n" );
+ for( y = 0; y < lm->sh; y++ )
+ {
+ for( x = 0; x < lm->sw; x++ )
+ {
+ vec3_t mins, maxs;
+
+
+ cluster = SUPER_CLUSTER( x, y );
+ origin = SUPER_ORIGIN( x, y );
+ normal = SUPER_NORMAL( x, y );
+ luxel = SUPER_LUXEL( x, y );
+
+ if( *cluster < 0 )
+ continue;
+
+ /* check if within the bounding boxes of all surfaces referenced */
+ ClearBounds( mins, maxs );
+ for( n = 0; n < lm->numLightSurfaces; n++ )
+ {
+ int TOL;
+ info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
+ TOL = info->sampleSize + 2;
+ AddPointToBounds( info->mins, mins, maxs );
+ AddPointToBounds( info->maxs, mins, maxs );
+ if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
+ origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
+ origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
+ break;
+ }
+
+ /* inside? */
+ if( n < lm->numLightSurfaces )
+ continue;
+
+ /* report bogus origin */
+ 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",
+ rawLightmapNum, x, y, *cluster,
+ origin[ 0 ], origin[ 1 ], origin[ 2 ],
+ mins[ 0 ], mins[ 1 ], mins[ 2 ],
+ maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
+ luxel[ 3 ] );
+ }
+ }
+ #endif
+}
+
+
+
+/*
+SubmapRawLuxel()
+calculates the pvs cluster, origin, normal of a sub-luxel
+*/
+
+static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
+{
+ int i, *cluster, *cluster2;
+ float *origin, *origin2, *normal; //% , *normal2;
+ vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
+
+
+ /* calulate x vector */
+ if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
+ {
+ cluster = SUPER_CLUSTER( x, y );
+ origin = SUPER_ORIGIN( x, y );
+ //% normal = SUPER_NORMAL( x, y );
+ cluster2 = SUPER_CLUSTER( x + 1, y );
+ origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
+ //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
+ }
+ else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
+ {
+ cluster = SUPER_CLUSTER( x - 1, y );
+ origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
+ //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
+ cluster2 = SUPER_CLUSTER( x, y );
+ origin2 = SUPER_ORIGIN( x, y );
+ //% normal2 = SUPER_NORMAL( x, y );
+ }
+ else
+ Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
+
+ VectorSubtract( origin2, origin, originVecs[ 0 ] );
+ //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
+
+ /* calulate y vector */
+ if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
+ {
+ cluster = SUPER_CLUSTER( x, y );
+ origin = SUPER_ORIGIN( x, y );
+ //% normal = SUPER_NORMAL( x, y );
+ cluster2 = SUPER_CLUSTER( x, y + 1 );
+ origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
+ //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
+ }
+ else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
+ {
+ cluster = SUPER_CLUSTER( x, y - 1 );
+ origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
+ //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
+ cluster2 = SUPER_CLUSTER( x, y );
+ origin2 = SUPER_ORIGIN( x, y );
+ //% normal2 = SUPER_NORMAL( x, y );
+ }
+ else
+ Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
+
+ VectorSubtract( origin2, origin, originVecs[ 1 ] );
+ //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
+
+ /* calculate new origin */
+ //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
+ //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
+ for( i = 0; i < 3; i++ )
+ sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
+
+ /* get cluster */
+ *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
+ if( *sampleCluster < 0 )
+ return qfalse;
+
+ /* calculate new normal */
+ //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
+ //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
+ //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
+ //% return qfalse;
+ normal = SUPER_NORMAL( x, y );
+ VectorCopy( normal, sampleNormal );
+
+ /* return ok */
+ return qtrue;
+}
+
+
+/*
+SubsampleRawLuxel_r()
+recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
+*/
+
+void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )
+{
+ int b, samples, mapped, lighted;
+ int cluster[ 4 ];
+ vec4_t luxel[ 4 ];
+ vec3_t origin[ 4 ], normal[ 4 ];
+ float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
+ vec3_t color, total;
+
+
+ /* limit check */
+ if( lightLuxel[ 3 ] >= lightSamples )
+ return;
+
+ /* setup */
+ VectorClear( total );
+ mapped = 0;
+ lighted = 0;
+
+ /* make 2x2 subsample stamp */
+ for( b = 0; b < 4; b++ )
+ {
+ /* set origin */
+ VectorCopy( sampleOrigin, origin[ b ] );
+
+ /* calculate position */
+ if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
+ {
+ cluster[ b ] = -1;
+ continue;
+ }
+ mapped++;
+
+ /* increment sample count */
+ luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
+
+ /* setup trace */
+ trace->cluster = *cluster;
+ VectorCopy( origin[ b ], trace->origin );
+ VectorCopy( normal[ b ], trace->normal );
+
+ /* sample light */
+ //% LightContributionToSample( light, cluster[ b ], origin[ b ], normal[ b ], luxel[ b ], qtrue, qfalse, lm->numLightSurfaces, &lightSurfaces[ lm->firstLightSurface ] );
+ LightContributionToSample( trace );
+
+ /* add to totals (fixme: make contrast function) */
+ VectorCopy( trace->color, luxel[ b ] );
+ VectorAdd( total, trace->color, total );
+ if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
+ lighted++;
+ }
+
+ /* subsample further? */
+ if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
+ (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
+ lighted != 0 && lighted != mapped )
+ {
+ for( b = 0; b < 4; b++ )
+ {
+ if( cluster[ b ] < 0 )
+ continue;
+ SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.25f), luxel[ b ] );
+ }
+ }
+
+ /* average */
+ //% VectorClear( color );
+ //% samples = 0;
+ VectorCopy( lightLuxel, color );
+ samples = 1;
+ for( b = 0; b < 4; b++ )
+ {
+ if( cluster[ b ] < 0 )
+ continue;
+ VectorAdd( color, luxel[ b ], color );
+ samples++;
+ }
+
+ /* add to luxel */
+ if( samples > 0 )
+ {
+ /* average */
+ color[ 0 ] /= samples;
+ color[ 1 ] /= samples;
+ color[ 2 ] /= samples;
+
+ /* add to color */
+ VectorCopy( color, lightLuxel );
+ lightLuxel[ 3 ] += 1.0f;
+ }
+}
+
+
+
+/*
+IlluminateRawLightmap()
+illuminates the luxels
+*/
+
+#define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
+
+void IlluminateRawLightmap( int rawLightmapNum )
+{
+ int i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
+ int *cluster, *cluster2, mapped, lighted, totalLighted;
+ rawLightmap_t *lm;
+ surfaceInfo_t *info;
+ qboolean filterColor, filterDir;
+ float brightness;
+ float *origin, *normal, *luxel, *luxel2, *deluxel, *deluxel2;
+ float *lightLuxels, *lightLuxel, samples, filterRadius, weight;
+ vec3_t color, averageColor, averageDir, total, temp, temp2;
+ float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
+ trace_t trace;
+
+
+ /* bail if this number exceeds the number of raw lightmaps */
+ if( rawLightmapNum >= numRawLightmaps )
+ return;
+
+ /* get lightmap */
+ lm = &rawLightmaps[ rawLightmapNum ];
+
+ /* setup trace */
+ trace.testOcclusion = !noTrace;
+ trace.forceSunlight = qfalse;
+ trace.recvShadows = lm->recvShadows;
+ trace.numSurfaces = lm->numLightSurfaces;
+ trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
+ trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
+
+ /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
+ trace.twoSided = qfalse;
+ for( i = 0; i < trace.numSurfaces; i++ )
+ {
+ /* get surface */
+ info = &surfaceInfos[ trace.surfaces[ i ] ];
+
+ /* check twosidedness */
+ if( info->si->twoSided )
+ {
+ trace.twoSided = qtrue;
+ break;
+ }
+ }
+
+ /* create a culled light list for this raw lightmap */
+ CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
+
+ /* -----------------------------------------------------------------
+ fill pass
+ ----------------------------------------------------------------- */
+
+ /* test debugging state */
+ if( debugSurfaces || debugAxis || debugCluster || debugOrigin || normalmap )
+ {
+ /* debug fill the luxels */
+ for( y = 0; y < lm->sh; y++ )
+ {
+ for( x = 0; x < lm->sw; x++ )
+ {
+ /* get cluster */
+ cluster = SUPER_CLUSTER( x, y );
+
+ /* only fill mapped luxels */
+ if( *cluster < 0 )
+ continue;
+
+ /* get particulars */
+ luxel = SUPER_LUXEL( 0, x, y );
+ origin = SUPER_ORIGIN( x, y );
+ normal = SUPER_NORMAL( x, y );
+
+ /* color the luxel with raw lightmap num? */
+ if( debugSurfaces )
+ VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
+
+ /* color the luxel with lightmap axis? */
+ else if( debugAxis )
+ {
+ luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
+ luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
+ luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
+ }
+
+ /* color the luxel with luxel cluster? */
+ else if( debugCluster )
+ VectorCopy( debugColors[ *cluster % 12 ], luxel );
+
+ /* color the luxel with luxel origin? */
+ else if( debugOrigin )
+ {
+ VectorSubtract( lm->maxs, lm->mins, temp );
+ VectorScale( temp, (1.0f / 255.0f), temp );
+ VectorSubtract( origin, lm->mins, temp2 );
+ luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
+ luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
+ luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
+ }
+
+ /* color the luxel with the normal */
+ else if( normalmap )
+ {
+ luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
+ luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
+ luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
+ }
+
+ /* add to counts */
+ numLuxelsIlluminated++;
+ luxel[ 3 ] = 1.0f;
+ }
+ }
+ }
+ else
+ {
+ /* allocate temporary per-light luxel storage */
+ llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
+ lightLuxels = safe_malloc( llSize );
+
+ /* clear luxels */
+ //% memset( lm->superLuxels[ 0 ], 0, llSize );
+
+ /* set ambient color */
+ for( y = 0; y < lm->sh; y++ )
+ {
+ for( x = 0; x < lm->sw; x++ )
+ {
+ /* get cluster */
+ cluster = SUPER_CLUSTER( x, y );
+ luxel = SUPER_LUXEL( 0, x, y );
+ normal = SUPER_NORMAL( x, y );
+ deluxel = SUPER_DELUXEL( x, y );
+
+ /* blacken unmapped clusters */
+ if( *cluster < 0 )
+ VectorClear( luxel );
+
+ /* set ambient */
+ else
+ {
+ VectorCopy( ambientColor, luxel );
+ if( deluxemap )
+ VectorScale( normal, 0.00390625f, deluxel );
+ luxel[ 3 ] = 1.0f;
+ }
+ }
+ }
+
+ /* clear styled lightmaps */
+ size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
+ for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ if( lm->superLuxels[ lightmapNum ] != NULL )
+ memset( lm->superLuxels[ lightmapNum ], 0, size );
+ }
+
+ /* debugging code */
+ //% if( trace.numLights <= 0 )
+ //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
+
+ /* walk light list */
+ for( i = 0; i < trace.numLights; i++ )
+ {
+ /* setup trace */
+ trace.light = trace.lights[ i ];
+
+ /* style check */
+ for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ if( lm->styles[ lightmapNum ] == trace.light->style ||
+ lm->styles[ lightmapNum ] == LS_NONE )
+ break;
+ }
+
+ /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
+ if( lightmapNum >= MAX_LIGHTMAPS )
+ {
+ Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
+ continue;
+ }
+
+ /* setup */
+ memset( lightLuxels, 0, llSize );
+ totalLighted = 0;
+
+ /* initial pass, one sample per luxel */
+ for( y = 0; y < lm->sh; y++ )
+ {
+ for( x = 0; x < lm->sw; x++ )
+ {
+ /* get cluster */
+ cluster = SUPER_CLUSTER( x, y );
+ if( *cluster < 0 )
+ continue;
+
+ /* get particulars */
+ lightLuxel = LIGHT_LUXEL( x, y );
+ deluxel = SUPER_DELUXEL( x, y );
+ origin = SUPER_ORIGIN( x, y );
+ normal = SUPER_NORMAL( x, y );
+
+ /* set contribution count */
+ lightLuxel[ 3 ] = 1.0f;
+
+ /* setup trace */
+ trace.cluster = *cluster;
+ VectorCopy( origin, trace.origin );
+ VectorCopy( normal, trace.normal );
+
+ /* get light for this sample */
+ LightContributionToSample( &trace );
+ VectorCopy( trace.color, lightLuxel );
+
+ /* add to count */
+ if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
+ totalLighted++;
+
+ /* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */
+ if( deluxemap )
+ {
+ /* color to grayscale (photoshop rgb weighting) */
+ brightness = trace.color[ 0 ] * 0.3f + trace.color[ 1 ] * 0.59f + trace.color[ 2 ] * 0.11f;
+ brightness *= (1.0 / 255.0);
+ VectorScale( trace.direction, brightness, trace.direction );
+ VectorAdd( deluxel, trace.direction, deluxel );
+ }
+ }
+ }
+
+ /* don't even bother with everything else if nothing was lit */
+ if( totalLighted == 0 )
+ continue;
+
+ /* determine filter radius */
+ filterRadius = lm->filterRadius > trace.light->filterRadius
+ ? lm->filterRadius
+ : trace.light->filterRadius;
+ if( filterRadius < 0.0f )
+ filterRadius = 0.0f;
+
+ /* set luxel filter radius */
+ luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
+ if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
+ luxelFilterRadius = 1;
+
+ /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
+ /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
+ if( lightSamples > 1 && luxelFilterRadius == 0 )
+ {
+ /* walk luxels */
+ for( y = 0; y < (lm->sh - 1); y++ )
+ {
+ for( x = 0; x < (lm->sw - 1); x++ )
+ {
+ /* setup */
+ mapped = 0;
+ lighted = 0;
+ VectorClear( total );
+
+ /* test 2x2 stamp */
+ for( t = 0; t < 4; t++ )
+ {
+ /* set sample coords */
+ sx = x + tests[ t ][ 0 ];
+ sy = y + tests[ t ][ 1 ];
+
+ /* get cluster */
+ cluster = SUPER_CLUSTER( sx, sy );
+ if( *cluster < 0 )
+ continue;
+ mapped++;
+
+ /* get luxel */
+ lightLuxel = LIGHT_LUXEL( sx, sy );
+ VectorAdd( total, lightLuxel, total );
+ if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
+ lighted++;
+ }
+
+ /* if total color is under a certain amount, then don't bother subsampling */
+ if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
+ continue;
+
+ /* if all 4 pixels are either in shadow or light, then don't subsample */
+ if( lighted != 0 && lighted != mapped )
+ {
+ for( t = 0; t < 4; t++ )
+ {
+ /* set sample coords */
+ sx = x + tests[ t ][ 0 ];
+ sy = y + tests[ t ][ 1 ];
+
+ /* get luxel */
+ cluster = SUPER_CLUSTER( sx, sy );
+ if( *cluster < 0 )
+ continue;
+ lightLuxel = LIGHT_LUXEL( sx, sy );
+ origin = SUPER_ORIGIN( sx, sy );
+
+ /* only subsample shadowed luxels */
+ //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
+ //% continue;
+
+ /* subsample it */
+ SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f, lightLuxel );
+
+ /* debug code to colorize subsampled areas to yellow */
+ //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
+ //% VectorSet( luxel, 255, 204, 0 );
+ }
+ }
+ }
+ }
+ }
+
+ /* allocate sampling lightmap storage */
+ if( lm->superLuxels[ lightmapNum ] == NULL )
+ {
+ /* allocate sampling lightmap storage */
+ size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
+ lm->superLuxels[ lightmapNum ] = safe_malloc( size );
+ memset( lm->superLuxels[ lightmapNum ], 0, size );
+ }
+
+ /* set style */
+ if( lightmapNum > 0 )
+ {
+ lm->styles[ lightmapNum ] = trace.light->style;
+ //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
+ }
+
+ /* copy to permanent luxels */
+ for( y = 0; y < lm->sh; y++ )
+ {
+ for( x = 0; x < lm->sw; x++ )
+ {
+ /* get cluster and origin */
+ cluster = SUPER_CLUSTER( x, y );
+ if( *cluster < 0 )
+ continue;
+ origin = SUPER_ORIGIN( x, y );
+
+ /* filter? */
+ if( luxelFilterRadius )
+ {
+ /* setup */
+ VectorClear( averageColor );
+ samples = 0.0f;
+
+ /* cheaper distance-based filtering */
+ for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
+ {
+ if( sy < 0 || sy >= lm->sh )
+ continue;
+
+ for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
+ {
+ if( sx < 0 || sx >= lm->sw )
+ continue;
+
+ /* get particulars */
+ cluster = SUPER_CLUSTER( sx, sy );
+ if( *cluster < 0 )
+ continue;
+ lightLuxel = LIGHT_LUXEL( sx, sy );
+
+ /* create weight */
+ weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
+ weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
+
+ /* scale luxel by filter weight */
+ VectorScale( lightLuxel, weight, color );
+ VectorAdd( averageColor, color, averageColor );
+ samples += weight;
+ }
+ }
+
+ /* any samples? */
+ if( samples <= 0.0f )
+ continue;
+
+ /* scale into luxel */
+ luxel = SUPER_LUXEL( lightmapNum, x, y );
+ luxel[ 3 ] = 1.0f;
+
+ /* handle negative light */
+ if( trace.light->flags & LIGHT_NEGATIVE )
+ {
+ luxel[ 0 ] -= averageColor[ 0 ] / samples;
+ luxel[ 1 ] -= averageColor[ 1 ] / samples;
+ luxel[ 2 ] -= averageColor[ 2 ] / samples;
+ }
+
+ /* handle normal light */
+ else
+ {
+ luxel[ 0 ] += averageColor[ 0 ] / samples;
+ luxel[ 1 ] += averageColor[ 1 ] / samples;
+ luxel[ 2 ] += averageColor[ 2 ] / samples;
+ }
+ }
+
+ /* single sample */
+ else
+ {
+ /* get particulars */
+ lightLuxel = LIGHT_LUXEL( x, y );
+ luxel = SUPER_LUXEL( lightmapNum, x, y );
+
+ /* handle negative light */
+ if( trace.light->flags & LIGHT_NEGATIVE )
+ VectorScale( averageColor, -1.0f, averageColor );
+
+ /* add color */
+ luxel[ 3 ] = 1.0f;
+
+ /* handle negative light */
+ if( trace.light->flags & LIGHT_NEGATIVE )
+ VectorSubtract( luxel, lightLuxel, luxel );
+
+ /* handle normal light */
+ else
+ VectorAdd( luxel, lightLuxel, luxel );
+ }
+ }
+ }
+ }
+
+ /* free temporary luxels */
+ free( lightLuxels );
+ }
+
+ /* free light list */
+ FreeTraceLights( &trace );
+
+ /* -----------------------------------------------------------------
+ filter pass
+ ----------------------------------------------------------------- */
+
+ /* walk lightmaps */
+ for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* early out */
+ if( lm->superLuxels[ lightmapNum ] == NULL )
+ continue;
+
+ /* average occluded luxels from neighbors */
+ for( y = 0; y < lm->sh; y++ )
+ {
+ for( x = 0; x < lm->sw; x++ )
+ {
+ /* get particulars */
+ cluster = SUPER_CLUSTER( x, y );
+ luxel = SUPER_LUXEL( lightmapNum, x, y );
+ deluxel = SUPER_DELUXEL( x, y );
+ normal = SUPER_NORMAL( x, y );
+
+ /* determine if filtering is necessary */
+ filterColor = qfalse;
+ filterDir = qfalse;
+ if( *cluster < 0 ||
+ (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
+ filterColor = qtrue;
+ if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
+ filterDir = qtrue;
+
+ if( !filterColor && !filterDir )
+ continue;
+
+ /* choose seed amount */
+ VectorClear( averageColor );
+ VectorClear( averageDir );
+ samples = 0;
+
+ /* walk 3x3 matrix */
+ for( sy = (y - 1); sy <= (y + 1); sy++ )
+ {
+ if( sy < 0 || sy >= lm->sh )
+ continue;
+
+ for( sx = (x - 1); sx <= (x + 1); sx++ )
+ {
+ if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
+ continue;
+
+ /* get neighbor's particulars */
+ cluster2 = SUPER_CLUSTER( sx, sy );
+ luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
+ deluxel2 = SUPER_DELUXEL( sx, sy );
+
+ /* ignore unmapped/unlit luxels */
+ if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
+ (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
+ continue;
+
+ /* add its distinctiveness to our own */
+ VectorAdd( averageColor, luxel2, averageColor );
+ samples += luxel2[ 3 ];
+ if( filterDir )
+ VectorAdd( averageDir, deluxel2, averageDir );
+ }
+ }
+
+ /* fall through */
+ if( samples == 0.0f )
+ continue;
+
+ /* average it */
+ if( filterColor )
+ {
+ VectorDivide( averageColor, samples, luxel );
+ luxel[ 3 ] = 1.0f;
+ }
+ if( filterDir )
+ VectorDivide( averageDir, samples, deluxel );
+
+ /* set cluster to -3 */
+ if( *cluster < 0 )
+ *cluster = CLUSTER_FLOODED;
+ }
+ }
+ }
+}
+
+
+
+/*
+IlluminateVertexes()
+light the surface vertexes
+*/
+
+#define VERTEX_NUDGE 2.0f
+
+void IlluminateVertexes( int num )
+{
+ int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
+ int lightmapNum;
+ float samples, *vertLuxel, *radVertLuxel, *luxel;
+ vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ];
+ bspDrawSurface_t *ds;
+ surfaceInfo_t *info;
+ rawLightmap_t *lm;
+ bspDrawVert_t *verts;
+ trace_t trace;
+
+
+ /* der... */
+ if( noVertexLighting )
+ return;
+
+ /* get surface, info, and raw lightmap */
+ ds = &bspDrawSurfaces[ num ];
+ info = &surfaceInfos[ num ];
+ lm = info->lm;
+
+ /* -----------------------------------------------------------------
+ illuminate the vertexes
+ ----------------------------------------------------------------- */
+
+ /* calculate vertex lighting for surfaces without lightmaps */
+ if( lm == NULL )
+ {
+ /* setup trace */
+ trace.testOcclusion = !noTrace;
+ trace.forceSunlight = info->si->forceSunlight;
+ trace.recvShadows = info->recvShadows;
+ trace.numSurfaces = 1;
+ trace.surfaces = #
+ trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
+
+ /* twosided lighting */
+ trace.twoSided = info->si->twoSided;
+
+ /* make light list for this surface */
+ CreateTraceLightsForSurface( num, &trace );
+
+ /* walk the surface verts */
+ verts = yDrawVerts + ds->firstVert;
+ for( i = 0; i < ds->numVerts; i++ )
+ {
+ /* get vertex luxel */
+ radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
+
+ /* color the luxel with raw lightmap num? */
+ if( debugSurfaces )
+ VectorCopy( debugColors[ num % 12 ], radVertLuxel );
+
+ /* color the luxel with luxel origin? */
+ else if( debugOrigin )
+ {
+ VectorSubtract( info->maxs, info->mins, temp );
+ VectorScale( temp, (1.0f / 255.0f), temp );
+ VectorSubtract( origin, lm->mins, temp2 );
+ radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
+ radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
+ radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
+ }
+
+ /* color the luxel with the normal */
+ else if( normalmap )
+ {
+ radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
+ radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
+ radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
+ }
+
+ /* illuminate the vertex */
+ else
+ {
+ /* clear vertex luxel */
+ VectorCopy( ambientColor, radVertLuxel );
+
+ /* try at initial origin */
+ trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
+ if( trace.cluster >= 0 )
+ {
+ /* setup trace */
+ VectorCopy( verts[ i ].xyz, trace.origin );
+ VectorCopy( verts[ i ].normal, trace.normal );
+
+ /* trace */
+ LightingAtSample( &trace, ds->vertexStyles, colors );
+
+ /* store */
+ for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
+ VectorCopy( colors[ lightmapNum ], radVertLuxel );
+ }
+ }
+
+ /* is this sample bright enough? */
+ if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
+ radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
+ radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
+ {
+ /* nudge the sample point around a bit */
+ for( x = 0; x < 4; x++ )
+ {
+ /* two's complement 0, 1, -1, 2, -2, etc */
+ x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
+
+ for( y = 0; y < 4; y++ )
+ {
+ y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
+
+ for( z = 0; z < 4; z++ )
+ {
+ z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
+
+ /* nudge origin */
+ trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
+ trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
+ trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
+
+ /* try at nudged origin */
+ trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
+ if( trace.cluster < 0 )
+ continue;
+
+ /* trace */
+ LightingAtSample( &trace, ds->vertexStyles, colors );
+
+ /* store */
+ for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
+ VectorCopy( colors[ lightmapNum ], radVertLuxel );
+ }
+
+ /* bright enough? */
+ radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
+ if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
+ radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
+ radVertLuxel[ 2 ] > ambientColor[ 2 ] )
+ x = y = z = 1000;
+ }
+ }
+ }
+ }
+ }
+
+ /* another happy customer */
+ numVertsIlluminated++;
+
+ /* store it */
+ for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* get luxels */
+ vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
+ radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
+
+ /* store */
+ if( bouncing || bounce == 0 || !bounceOnly )
+ VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
+ ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
+ }
+ }
+
+ /* free light list */
+ FreeTraceLights( &trace );
+
+ /* return to sender */
+ return;
+ }
+
+ /* -----------------------------------------------------------------
+ reconstitute vertex lighting from the luxels
+ ----------------------------------------------------------------- */
+
+ /* set styles from lightmap */
+ for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
+
+ /* get max search radius */
+ maxRadius = lm->sw;
+ maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
+
+ /* walk the surface verts */
+ verts = yDrawVerts + ds->firstVert;
+ for( i = 0; i < ds->numVerts; i++ )
+ {
+ /* do each lightmap */
+ for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* early out */
+ if( lm->superLuxels[ lightmapNum ] == NULL )
+ continue;
+
+ /* get luxel coords */
+ x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
+ y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
+ if( x < 0 )
+ x = 0;
+ else if( x >= lm->sw )
+ x = lm->sw - 1;
+ if( y < 0 )
+ y = 0;
+ else if( y >= lm->sh )
+ y = lm->sh - 1;
+
+ /* get vertex luxels */
+ vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
+ radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
+
+ /* color the luxel with the normal? */
+ if( normalmap )
+ {
+ radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
+ radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
+ radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
+ }
+
+ /* color the luxel with surface num? */
+ else if( debugSurfaces )
+ VectorCopy( debugColors[ num % 12 ], radVertLuxel );
+
+ /* divine color from the superluxels */
+ else
+ {
+ /* increasing radius */
+ VectorClear( radVertLuxel );
+ samples = 0.0f;
+ for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
+ {
+ /* sample within radius */
+ for( sy = (y - radius); sy <= (y + radius); sy++ )
+ {
+ if( sy < 0 || sy >= lm->sh )
+ continue;
+
+ for( sx = (x - radius); sx <= (x + radius); sx++ )
+ {
+ if( sx < 0 || sx >= lm->sw )
+ continue;
+
+ /* get luxel particulars */
+ luxel = SUPER_LUXEL( lightmapNum, sx, sy );
+ cluster = SUPER_CLUSTER( sx, sy );
+ if( *cluster < 0 )
+ continue;
+
+ /* testing: must be brigher than ambient color */
+ //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
+ //% continue;
+
+ /* add its distinctiveness to our own */
+ VectorAdd( radVertLuxel, luxel, radVertLuxel );
+ samples += luxel[ 3 ];
+ }
+ }
+ }
+
+ /* any color? */
+ if( samples > 0.0f )
+ VectorDivide( radVertLuxel, samples, radVertLuxel );
+ else
+ VectorCopy( ambientColor, radVertLuxel );
+ }
+
+ /* store into floating point storage */
+ VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
+ numVertsIlluminated++;
+
+ /* store into bytes (for vertex approximation) */
+ ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
+ }
+ }
+}
+
+
+
+/* -------------------------------------------------------------------------------
+
+light optimization (-fast)
+
+creates a list of lights that will affect a surface and stores it in tw
+this is to optimize surface lighting by culling out as many of the
+lights in the world as possible from further calculation
+
+------------------------------------------------------------------------------- */
+
+/*
+SetupBrushes()
+determines opaque brushes in the world and find sky shaders for sunlight calculations
+*/
+
+void SetupBrushes( void )
+{
+ int i, j, b, compileFlags;
+ qboolean inside;
+ bspBrush_t *brush;
+ bspBrushSide_t *side;
+ bspShader_t *shader;
+ shaderInfo_t *si;
+
+
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
+
+ /* allocate */
+ if( opaqueBrushes == NULL )
+ opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
+
+ /* clear */
+ memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
+ numOpaqueBrushes = 0;
+
+ /* walk the list of worldspawn brushes */
+ for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
+ {
+ /* get brush */
+ b = bspModels[ 0 ].firstBSPBrush + i;
+ brush = &bspBrushes[ b ];
+
+ /* check all sides */
+ inside = qtrue;
+ compileFlags = 0;
+ for( j = 0; j < brush->numSides && inside; j++ )
+ {
+ /* do bsp shader calculations */
+ side = &bspBrushSides[ brush->firstSide + j ];
+ shader = &bspShaders[ side->shaderNum ];
+
+ /* get shader info */
+ si = ShaderInfoForShader( shader->shader );
+ if( si == NULL )
+ continue;
+
+ /* or together compile flags */
+ compileFlags |= si->compileFlags;
+ }
+
+ /* determine if this brush is opaque to light */
+ if( !(compileFlags & C_TRANSLUCENT) )
+ {
+ opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
+ numOpaqueBrushes++;
+ maxOpaqueBrush = i;
+ }
+ }
+
+ /* emit some statistics */
+ Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
+}
+
+
+
+/*
+ClusterVisible()
+determines if two clusters are visible to each other using the PVS
+*/
+
+qboolean ClusterVisible( int a, int b )
+{
+ int portalClusters, leafBytes;
+ byte *pvs;
+
+
+ /* dummy check */
+ if( a < 0 || b < 0 )
+ return qfalse;
+
+ /* early out */
+ if( a == b )
+ return qtrue;
+
+ /* not vised? */
+ if( numBSPVisBytes <=8 )
+ return qtrue;
+
+ /* get pvs data */
+ portalClusters = ((int *) bspVisBytes)[ 0 ];
+ leafBytes = ((int*) bspVisBytes)[ 1 ];
+ pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
+
+ /* check */
+ if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
+ return qtrue;
+ return qfalse;
+}
+
+
+
+/*
+PointInLeafNum_r()
+borrowed from vlight.c
+*/
+
+int PointInLeafNum_r( vec3_t point, int nodenum )
+{
+ int leafnum;
+ vec_t dist;
+ bspNode_t *node;
+ bspPlane_t *plane;
+
+
+ while( nodenum >= 0 )
+ {
+ node = &bspNodes[ nodenum ];
+ plane = &bspPlanes[ node->planeNum ];
+ dist = DotProduct( point, plane->normal ) - plane->dist;
+ if( dist > 0.1 )
+ nodenum = node->children[ 0 ];
+ else if( dist < -0.1 )
+ nodenum = node->children[ 1 ];
+ else
+ {
+ leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
+ if( bspLeafs[ leafnum ].cluster != -1 )
+ return leafnum;
+ nodenum = node->children[ 1 ];
+ }
+ }
+
+ leafnum = -nodenum - 1;
+ return leafnum;
+}
+
+
+
+/*
+PointInLeafnum()
+borrowed from vlight.c
+*/
+
+int PointInLeafNum( vec3_t point )
+{
+ return PointInLeafNum_r( point, 0 );
+}
+
+
+
+/*
+ClusterVisibleToPoint() - ydnar
+returns qtrue if point can "see" cluster
+*/
+
+qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
+{
+ int pointCluster;
+
+
+ /* get leafNum for point */
+ pointCluster = ClusterForPoint( point );
+ if( pointCluster < 0 )
+ return qfalse;
+
+ /* check pvs */
+ return ClusterVisible( pointCluster, cluster );
+}
+
+
+
+/*
+ClusterForPoint() - ydnar
+returns the pvs cluster for point
+*/
+
+int ClusterForPoint( vec3_t point )
+{
+ int leafNum;
+
+
+ /* get leafNum for point */
+ leafNum = PointInLeafNum( point );
+ if( leafNum < 0 )
+ return -1;
+
+ /* return the cluster */
+ return bspLeafs[ leafNum ].cluster;
+}
+
+
+
+/*
+ClusterForPointExt() - ydnar
+also takes brushes into account for occlusion testing
+*/
+
+int ClusterForPointExt( vec3_t point, float epsilon )
+{
+ int i, j, b, leafNum, cluster;
+ float dot;
+ qboolean inside;
+ int *brushes, numBSPBrushes;
+ bspLeaf_t *leaf;
+ bspBrush_t *brush;
+ bspPlane_t *plane;
+
+
+ /* get leaf for point */
+ leafNum = PointInLeafNum( point );
+ if( leafNum < 0 )
+ return -1;
+ leaf = &bspLeafs[ leafNum ];
+
+ /* get the cluster */
+ cluster = leaf->cluster;
+ if( cluster < 0 )
+ return -1;
+
+ /* transparent leaf, so check point against all brushes in the leaf */
+ brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
+ numBSPBrushes = leaf->numBSPLeafBrushes;
+ for( i = 0; i < numBSPBrushes; i++ )
+ {
+ /* get parts */
+ b = brushes[ i ];
+ if( b > maxOpaqueBrush )
+ continue;
+ brush = &bspBrushes[ b ];
+ if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
+ continue;
+
+ /* check point against all planes */
+ inside = qtrue;
+ for( j = 0; j < brush->numSides && inside; j++ )
+ {
+ plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
+ dot = DotProduct( point, plane->normal );
+ dot -= plane->dist;
+ if( dot > epsilon )
+ inside = qfalse;
+ }
+
+ /* if inside, return bogus cluster */
+ if( inside )
+ return -1 - b;
+ }
+
+ /* if the point made it this far, it's not inside any opaque brushes */
+ return cluster;
+}
+
+
+
+/*
+ClusterForPointExtFilter() - ydnar
+adds cluster checking against a list of known valid clusters
+*/
+
+int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
+{
+ int i, cluster;
+
+
+ /* get cluster for point */
+ cluster = ClusterForPointExt( point, epsilon );
+
+ /* check if filtering is necessary */
+ if( cluster < 0 || numClusters <= 0 || clusters == NULL )
+ return cluster;
+
+ /* filter */
+ for( i = 0; i < numClusters; i++ )
+ {
+ if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
+ return cluster;
+ }
+
+ /* failed */
+ return -1;
+}
+
+
+
+/*
+ShaderForPointInLeaf() - ydnar
+checks a point against all brushes in a leaf, returning the shader of the brush
+also sets the cumulative surface and content flags for the brush hit
+*/
+
+int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
+{
+ int i, j;
+ float dot;
+ qboolean inside;
+ int *brushes, numBSPBrushes;
+ bspLeaf_t *leaf;
+ bspBrush_t *brush;
+ bspBrushSide_t *side;
+ bspPlane_t *plane;
+ bspShader_t *shader;
+ int allSurfaceFlags, allContentFlags;
+
+
+ /* clear things out first */
+ *surfaceFlags = 0;
+ *contentFlags = 0;
+
+ /* get leaf */
+ if( leafNum < 0 )
+ return -1;
+ leaf = &bspLeafs[ leafNum ];
+
+ /* transparent leaf, so check point against all brushes in the leaf */
+ brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
+ numBSPBrushes = leaf->numBSPLeafBrushes;
+ for( i = 0; i < numBSPBrushes; i++ )
+ {
+ /* get parts */
+ brush = &bspBrushes[ brushes[ i ] ];
+
+ /* check point against all planes */
+ inside = qtrue;
+ allSurfaceFlags = 0;
+ allContentFlags = 0;
+ for( j = 0; j < brush->numSides && inside; j++ )
+ {
+ side = &bspBrushSides[ brush->firstSide + j ];
+ plane = &bspPlanes[ side->planeNum ];
+ dot = DotProduct( point, plane->normal );
+ dot -= plane->dist;
+ if( dot > epsilon )
+ inside = qfalse;
+ else
+ {
+ shader = &bspShaders[ side->shaderNum ];
+ allSurfaceFlags |= shader->surfaceFlags;
+ allContentFlags |= shader->contentFlags;
+ }
+ }
+
+ /* handle if inside */
+ if( inside )
+ {
+ /* if there are desired flags, check for same and continue if they aren't matched */
+ if( wantContentFlags && !(wantContentFlags & allContentFlags) )
+ continue;
+ if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
+ continue;
+
+ /* store the cumulative flags and return the brush shader (which is mostly useless) */
+ *surfaceFlags = allSurfaceFlags;
+ *contentFlags = allContentFlags;
+ return brush->shaderNum;
+ }
+ }
+
+ /* if the point made it this far, it's not inside any brushes */
+ return -1;
+}
+
+
+
+/*
+ChopBounds()
+chops a bounding box by the plane defined by origin and normal
+returns qfalse if the bounds is entirely clipped away
+
+this is not exactly the fastest way to do this...
+*/
+
+qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
+{
+ /* FIXME: rewrite this so it doesn't use bloody brushes */
+ return qtrue;
+}
+
+
+
+/*
+SetupEnvelopes()
+calculates each light's effective envelope,
+taking into account brightness, type, and pvs.
+*/
+
+#define LIGHT_EPSILON 0.125f
+#define LIGHT_NUDGE 2.0f
+
+void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
+{
+ int i, x, y, z, x1, y1, z1;
+ light_t *light, *light2, **owner;
+ bspLeaf_t *leaf;
+ vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
+ float radius, intensity;
+ light_t *buckets[ 256 ];
+
+
+ /* early out for weird cases where there are no lights */
+ if( lights == NULL )
+ return;
+
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
+
+ /* count lights */
+ numLights = 0;
+ numCulledLights = 0;
+ owner = &lights;
+ while( *owner != NULL )
+ {
+ /* get light */
+ light = *owner;
+
+ /* handle negative lights */
+ if( light->photons < 0.0f || light->add < 0.0f )
+ {
+ light->photons *= -1.0f;
+ light->add *= -1.0f;
+ light->flags |= LIGHT_NEGATIVE;
+ }
+
+ /* sunlight? */
+ if( light->type == EMIT_SUN )
+ {
+ /* special cased */
+ light->cluster = 0;
+ light->envelope = MAX_WORLD_COORD * 8.0f;
+ VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
+ VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
+ }
+
+ /* everything else */
+ else
+ {
+ /* get pvs cluster for light */
+ light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
+
+ /* invalid cluster? */
+ if( light->cluster < 0 )
+ {
+ /* nudge the sample point around a bit */
+ for( x = 0; x < 4; x++ )
+ {
+ /* two's complement 0, 1, -1, 2, -2, etc */
+ x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
+
+ for( y = 0; y < 4; y++ )
+ {
+ y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
+
+ for( z = 0; z < 4; z++ )
+ {
+ z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
+
+ /* nudge origin */
+ origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
+ origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
+ origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
+
+ /* try at nudged origin */
+ light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
+ if( light->cluster < 0 )
+ continue;
+
+ /* set origin */
+ VectorCopy( origin, light->origin );
+ }
+ }
+ }
+ }
+
+ /* only calculate for lights in pvs and outside of opaque brushes */
+ if( light->cluster >= 0 )
+ {
+ /* set light fast flag */
+ if( fastFlag )
+ light->flags |= LIGHT_FAST_TEMP;
+ else
+ light->flags &= ~LIGHT_FAST_TEMP;
+ if( light->si && light->si->noFast )
+ light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
+
+ /* clear light envelope */
+ light->envelope = 0;
+
+ /* handle area lights */
+ if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
+ {
+ /* ugly hack to calculate extent for area lights, but only done once */
+ VectorScale( light->normal, -1.0f, dir );
+ for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
+ {
+ float factor;
+
+ VectorMA( light->origin, radius, light->normal, origin );
+ factor = PointToPolygonFormFactor( origin, dir, light->w );
+ if( factor < 0.0f )
+ factor *= -1.0f;
+ if( (factor * light->add) <= light->falloffTolerance )
+ light->envelope = radius;
+ }
+
+ /* check for fast mode */
+ if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
+ light->envelope = MAX_WORLD_COORD * 8.0f;
+ }
+ else
+ {
+ radius = 0.0f;
+ intensity = light->photons;
+ }
+
+ /* other calcs */
+ if( light->envelope <= 0.0f )
+ {
+ /* solve distance for non-distance lights */
+ if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
+ light->envelope = MAX_WORLD_COORD * 8.0f;
+
+ /* solve distance for linear lights */
+ else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
+ //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
+ light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
+
+ /*
+ add = angle * light->photons * linearScale - (dist * light->fade);
+ T = (light->photons * linearScale) - (dist * light->fade);
+ T + (dist * light->fade) = (light->photons * linearScale);
+ dist * light->fade = (light->photons * linearScale) - T;
+ dist = ((light->photons * linearScale) - T) / light->fade;
+ */
+
+ /* solve for inverse square falloff */
+ else
+ light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
+
+ /*
+ add = light->photons / (dist * dist);
+ T = light->photons / (dist * dist);
+ T * (dist * dist) = light->photons;
+ dist = sqrt( light->photons / T );
+ */
+ }
+
+ /* chop radius against pvs */
+ {
+ /* clear bounds */
+ ClearBounds( mins, maxs );
+
+ /* check all leaves */
+ for( i = 0; i < numBSPLeafs; i++ )
+ {
+ /* get test leaf */
+ leaf = &bspLeafs[ i ];
+
+ /* in pvs? */
+ if( leaf->cluster < 0 )
+ continue;
+ if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
+ continue;
+
+ /* add this leafs bbox to the bounds */
+ VectorCopy( leaf->mins, origin );
+ AddPointToBounds( origin, mins, maxs );
+ VectorCopy( leaf->maxs, origin );
+ AddPointToBounds( origin, mins, maxs );
+ }
+
+ /* test to see if bounds encompass light */
+ for( i = 0; i < 3; i++ )
+ {
+ if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
+ {
+ //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
+ //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
+ //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
+ //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
+ AddPointToBounds( light->origin, mins, maxs );
+ }
+ }
+
+ /* chop the bounds by a plane for area lights and spotlights */
+ if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
+ ChopBounds( mins, maxs, light->origin, light->normal );
+
+ /* copy bounds */
+ VectorCopy( mins, light->mins );
+ VectorCopy( maxs, light->maxs );
+
+ /* reflect bounds around light origin */
+ //% VectorMA( light->origin, -1.0f, origin, origin );
+ VectorScale( light->origin, 2, origin );
+ VectorSubtract( origin, maxs, origin );
+ AddPointToBounds( origin, mins, maxs );
+ //% VectorMA( light->origin, -1.0f, mins, origin );
+ VectorScale( light->origin, 2, origin );
+ VectorSubtract( origin, mins, origin );
+ AddPointToBounds( origin, mins, maxs );
+
+ /* calculate spherical bounds */
+ VectorSubtract( maxs, light->origin, dir );
+ radius = (float) VectorLength( dir );
+
+ /* if this radius is smaller than the envelope, then set the envelope to it */
+ if( radius < light->envelope )
+ {
+ light->envelope = radius;
+ //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
+ }
+ //% else
+ //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
+ }
+
+ /* add grid/surface only check */
+ if( forGrid )
+ {
+ if( !(light->flags & LIGHT_GRID) )
+ light->envelope = 0.0f;
+ }
+ else
+ {
+ if( !(light->flags & LIGHT_SURFACES) )
+ light->envelope = 0.0f;
+ }
+ }
+
+ /* culled? */
+ if( light->cluster < 0 || light->envelope <= 0.0f )
+ {
+ /* debug code */
+ //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
+
+ /* delete the light */
+ numCulledLights++;
+ *owner = light->next;
+ if( light->w != NULL )
+ free( light->w );
+ free( light );
+ continue;
+ }
+ }
+
+ /* square envelope */
+ light->envelope2 = (light->envelope * light->envelope);
+
+ /* increment light count */
+ numLights++;
+
+ /* set next light */
+ owner = &((**owner).next);
+ }
+
+ /* bucket sort lights by style */
+ memset( buckets, 0, sizeof( buckets ) );
+ light2 = NULL;
+ for( light = lights; light != NULL; light = light2 )
+ {
+ /* get next light */
+ light2 = light->next;
+
+ /* filter into correct bucket */
+ light->next = buckets[ light->style ];
+ buckets[ light->style ] = light;
+ }
+
+ /* filter back into light list */
+ lights = NULL;
+ for( i = 255; i >= 0; i-- )
+ {
+ light2 = NULL;
+ for( light = buckets[ i ]; light != NULL; light = light2 )
+ {
+ light2 = light->next;
+ light->next = lights;
+ lights = light;
+ }
+ }
+
+ /* emit some statistics */
+ Sys_Printf( "%9d total lights\n", numLights );
+ Sys_Printf( "%9d culled lights\n", numCulledLights );
+}
+
+
+
+/*
+CreateTraceLightsForBounds()
+creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
+*/
+
+void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
+{
+ int i;
+ light_t *light;
+ vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
+ float radius, dist, length;
+
+
+ /* potential pre-setup */
+ if( numLights == 0 )
+ SetupEnvelopes( qfalse, fast );
+
+ /* debug code */
+ //% 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 ] );
+
+ /* allocate the light list */
+ trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
+ trace->numLights = 0;
+
+ /* calculate spherical bounds */
+ VectorAdd( mins, maxs, origin );
+ VectorScale( origin, 0.5f, origin );
+ VectorSubtract( maxs, origin, dir );
+ radius = (float) VectorLength( dir );
+
+ /* get length of normal vector */
+ if( normal != NULL )
+ length = VectorLength( normal );
+ else
+ {
+ normal = nullVector;
+ length = 0;
+ }
+
+ /* test each light and see if it reaches the sphere */
+ /* note: the attenuation code MUST match LightingAtSample() */
+ for( light = lights; light; light = light->next )
+ {
+ /* check zero sized envelope */
+ if( light->envelope <= 0 )
+ {
+ lightsEnvelopeCulled++;
+ continue;
+ }
+
+ /* check flags */
+ if( !(light->flags & flags) )
+ continue;
+
+ /* sunlight skips all this nonsense */
+ if( light->type != EMIT_SUN )
+ {
+ /* sun only? */
+ if( sunOnly )
+ continue;
+
+ /* check against pvs cluster */
+ if( numClusters > 0 && clusters != NULL )
+ {
+ for( i = 0; i < numClusters; i++ )
+ {
+ if( ClusterVisible( light->cluster, clusters[ i ] ) )
+ break;
+ }
+
+ /* fixme! */
+ if( i == numClusters )
+ {
+ lightsClusterCulled++;
+ continue;
+ }
+ }
+
+ /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
+ VectorSubtract( light->origin, origin, dir );
+ dist = VectorLength( dir );
+ dist -= light->envelope;
+ dist -= radius;
+ if( dist > 0 )
+ {
+ lightsEnvelopeCulled++;
+ continue;
+ }
+
+ /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
+ #if 0
+ skip = qfalse;
+ for( i = 0; i < 3; i++ )
+ {
+ if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
+ skip = qtrue;
+ }
+ if( skip )
+ {
+ lightsBoundsCulled++;
+ continue;
+ }
+ #endif
+ }
+
+ /* planar surfaces (except twosided surfaces) have a couple more checks */
+ if( length > 0.0f && trace->twoSided == qfalse )
+ {
+ /* lights coplanar with a surface won't light it */
+ if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
+ {
+ lightsPlaneCulled++;
+ continue;
+ }
+
+ /* check to see if light is behind the plane */
+ if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
+ {
+ lightsPlaneCulled++;
+ continue;
+ }
+ }
+
+ /* add this light */
+ trace->lights[ trace->numLights++ ] = light;
+ }
+
+ /* make last night null */
+ trace->lights[ trace->numLights ] = NULL;
+}
+
+
+
+void FreeTraceLights( trace_t *trace )
+{
+ if( trace->lights != NULL )
+ free( trace->lights );
+}
+
+
+
+/*
+CreateTraceLightsForSurface()
+creates a list of lights that can potentially affect a drawsurface
+*/
+
+void CreateTraceLightsForSurface( int num, trace_t *trace )
+{
+ int i;
+ vec3_t mins, maxs, normal;
+ bspDrawVert_t *dv;
+ bspDrawSurface_t *ds;
+ surfaceInfo_t *info;
+
+
+ /* dummy check */
+ if( num < 0 )
+ return;
+
+ /* get drawsurface and info */
+ ds = &bspDrawSurfaces[ num ];
+ info = &surfaceInfos[ num ];
+
+ /* get the mins/maxs for the dsurf */
+ ClearBounds( mins, maxs );
+ VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
+ for( i = 0; i < ds->numVerts; i++ )
+ {
+ dv = &yDrawVerts[ ds->firstVert + i ];
+ AddPointToBounds( dv->xyz, mins, maxs );
+ if( !VectorCompare( dv->normal, normal ) )
+ VectorClear( normal );
+ }
+
+ /* create the lights for the bounding box */
+ CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
+}
+
+
+
+
+