-/*\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 LIGHTMAPS_YDNAR_C\r
-\r
-\r
-\r
-/* dependencies */\r
-#include "q3map2.h"\r
-\r
-\r
-\r
-\r
-/* -------------------------------------------------------------------------------\r
-\r
-this file contains code that doe lightmap allocation and projection that\r
-runs in the -light phase.\r
-\r
-this is handled here rather than in the bsp phase for a few reasons--\r
-surfaces are no longer necessarily convex polygons, patches may or may not be\r
-planar or have lightmaps projected directly onto control points.\r
-\r
-also, this allows lightmaps to be calculated before being allocated and stored\r
-in the bsp. lightmaps that have little high-frequency information are candidates\r
-for having their resolutions scaled down.\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-/*\r
-WriteTGA24()\r
-based on WriteTGA() from imagelib.c\r
-*/\r
-\r
-void WriteTGA24( char *filename, byte *data, int width, int height, qboolean flip )\r
-{\r
- int i, c;\r
- byte *buffer, *in;\r
- FILE *file;\r
- \r
- \r
- /* allocate a buffer and set it up */\r
- buffer = safe_malloc( width * height * 3 + 18 );\r
- memset( buffer, 0, 18 );\r
- buffer[ 2 ] = 2;\r
- buffer[ 12 ] = width & 255;\r
- buffer[ 13 ] = width >> 8;\r
- buffer[ 14 ] = height & 255;\r
- buffer[ 15 ] = height >> 8;\r
- buffer[ 16 ] = 24;\r
-\r
- /* swap rgb to bgr */\r
- c = (width * height * 3) + 18;\r
- for( i = 18; i < c; i += 3 )\r
- {\r
- buffer[ i ] = data[ i - 18 + 2 ]; /* blue */\r
- buffer[ i + 1 ] = data[ i - 18 + 1 ]; /* green */\r
- buffer[ i + 2 ] = data[ i - 18 + 0 ]; /* red */\r
- }\r
- \r
- /* write it and free the buffer */\r
- file = fopen( filename, "wb" );\r
- if( file == NULL )\r
- Error( "Unable to open %s for writing", filename );\r
- \r
- /* flip vertically? */\r
- if( flip )\r
- {\r
- fwrite( buffer, 1, 18, file );\r
- for( in = buffer + ((height - 1) * width * 3) + 18; in >= buffer; in -= (width * 3) )\r
- fwrite( in, 1, (width * 3), file );\r
- }\r
- else\r
- fwrite( buffer, 1, c, file );\r
- \r
- /* close the file */\r
- fclose( file );\r
- free( buffer );\r
-}\r
-\r
-\r
-\r
-/*\r
-ExportLightmaps()\r
-exports the lightmaps as a list of numbered tga images\r
-*/\r
-\r
-void ExportLightmaps( void )\r
-{\r
- int i;\r
- char dirname[ 1024 ], filename[ 1024 ];\r
- byte *lightmap;\r
- \r
- \r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "--- ExportLightmaps ---\n");\r
- \r
- /* do some path mangling */\r
- strcpy( dirname, source );\r
- StripExtension( dirname );\r
- \r
- /* sanity check */\r
- if( bspLightBytes == NULL )\r
- {\r
- Sys_Printf( "WARNING: No BSP lightmap data\n" );\r
- return;\r
- }\r
- \r
- /* make a directory for the lightmaps */\r
- Q_mkdir( dirname );\r
- \r
- /* iterate through the lightmaps */\r
- for( i = 0, lightmap = bspLightBytes; lightmap < (bspLightBytes + numBSPLightBytes); i++, lightmap += (LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3) )\r
- {\r
- /* write a tga image out */\r
- sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );\r
- Sys_Printf( "Writing %s\n", filename );\r
- WriteTGA24( filename, lightmap, LIGHTMAP_WIDTH, LIGHTMAP_HEIGHT, qfalse );\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-ExportLightmapsMain()\r
-exports the lightmaps as a list of numbered tga images\r
-*/\r
-\r
-int ExportLightmapsMain( int argc, char **argv )\r
-{\r
- /* arg checking */\r
- if( argc < 1 )\r
- {\r
- Sys_Printf( "Usage: q3map -export [-v] <mapname>\n" );\r
- return 0;\r
- }\r
- \r
- /* do some path mangling */\r
- strcpy( source, ExpandArg( argv[ argc - 1 ] ) );\r
- StripExtension( source );\r
- DefaultExtension( source, ".bsp" );\r
- \r
- /* load the bsp */\r
- Sys_Printf( "Loading %s\n", source );\r
- LoadBSPFile( source );\r
- \r
- /* export the lightmaps */\r
- ExportLightmaps();\r
- \r
- /* return to sender */\r
- return 0;\r
-}\r
-\r
-\r
-\r
-/*\r
-ImportLightmapsMain()\r
-imports the lightmaps from a list of numbered tga images\r
-*/\r
-\r
-int ImportLightmapsMain( int argc, char **argv )\r
-{\r
- int i, x, y, len, width, height;\r
- char dirname[ 1024 ], filename[ 1024 ];\r
- byte *lightmap, *buffer, *pixels, *in, *out;\r
- \r
- \r
- /* arg checking */\r
- if( argc < 1 )\r
- {\r
- Sys_Printf( "Usage: q3map -import [-v] <mapname>\n" );\r
- return 0;\r
- }\r
- \r
- /* do some path mangling */\r
- strcpy( source, ExpandArg( argv[ argc - 1 ] ) );\r
- StripExtension( source );\r
- DefaultExtension( source, ".bsp" );\r
- \r
- /* load the bsp */\r
- Sys_Printf( "Loading %s\n", source );\r
- LoadBSPFile( source );\r
- \r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "--- ImportLightmaps ---\n");\r
- \r
- /* do some path mangling */\r
- strcpy( dirname, source );\r
- StripExtension( dirname );\r
- \r
- /* sanity check */\r
- if( bspLightBytes == NULL )\r
- Error( "No lightmap data" );\r
- \r
- /* make a directory for the lightmaps */\r
- Q_mkdir( dirname );\r
- \r
- /* iterate through the lightmaps */\r
- for( i = 0, lightmap = bspLightBytes; lightmap < (bspLightBytes + numBSPLightBytes); i++, lightmap += (LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3) )\r
- {\r
- /* read a tga image */\r
- sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );\r
- Sys_Printf( "Loading %s\n", filename );\r
- buffer = NULL;\r
- len = vfsLoadFile( filename, (void*) &buffer, -1 );\r
- if( len < 0 )\r
- {\r
- Sys_Printf( "WARNING: Unable to load image %s\n", filename );\r
- continue;\r
- }\r
- \r
- /* parse file into an image */\r
- pixels = NULL;\r
- LoadTGABuffer( buffer, &pixels, &width, &height );\r
- free( buffer );\r
- \r
- /* sanity check it */\r
- if( pixels == NULL )\r
- {\r
- Sys_Printf( "WARNING: Unable to load image %s\n", filename );\r
- continue;\r
- }\r
- if( width != LIGHTMAP_WIDTH || height != LIGHTMAP_HEIGHT )\r
- Sys_Printf( "WARNING: Image %s is not the right size (%d, %d) != (%d, %d)\n",\r
- filename, width, height, LIGHTMAP_WIDTH, LIGHTMAP_HEIGHT );\r
- \r
- /* copy the pixels */\r
- in = pixels;\r
- for( y = 1; y <= LIGHTMAP_HEIGHT; y++ )\r
- {\r
- out = lightmap + ((LIGHTMAP_HEIGHT - y) * LIGHTMAP_WIDTH * 3);\r
- for( x = 0; x < LIGHTMAP_WIDTH; x++, in += 4, out += 3 )\r
- VectorCopy( in, out );\r
- }\r
- \r
- /* free the image */\r
- free( pixels );\r
- }\r
- \r
- /* write the bsp */\r
- Sys_Printf( "writing %s\n", source );\r
- WriteBSPFile( source );\r
- \r
- /* return to sender */\r
- return 0;\r
-}\r
-\r
-\r
-\r
-/* -------------------------------------------------------------------------------\r
-\r
-this section deals with projecting a lightmap onto a raw drawsurface\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-/*\r
-CompareLightSurface()\r
-compare function for qsort()\r
-*/\r
-\r
-static int CompareLightSurface( const void *a, const void *b )\r
-{\r
- shaderInfo_t *asi, *bsi;\r
- \r
- \r
- /* get shaders */\r
- asi = surfaceInfos[ *((int*) a) ].si;\r
- bsi = surfaceInfos[ *((int*) b) ].si;\r
- \r
- /* dummy check */\r
- if( asi == NULL )\r
- return -1;\r
- if( bsi == NULL )\r
- return 1;\r
- \r
- /* compare shader names */\r
- return strcmp( asi->shader, bsi->shader );\r
-}\r
-\r
-\r
-\r
-/*\r
-FinishRawLightmap()\r
-allocates a raw lightmap's necessary buffers\r
-*/\r
-\r
-void FinishRawLightmap( rawLightmap_t *lm )\r
-{\r
- int i, j, c, size, *sc;\r
- float is;\r
- surfaceInfo_t *info;\r
- \r
- \r
- /* sort light surfaces by shader name */\r
- qsort( &lightSurfaces[ lm->firstLightSurface ], lm->numLightSurfaces, sizeof( int ), CompareLightSurface );\r
- \r
- /* count clusters */\r
- lm->numLightClusters = 0;\r
- for( i = 0; i < lm->numLightSurfaces; i++ )\r
- {\r
- /* get surface info */\r
- info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];\r
- \r
- /* add surface clusters */\r
- lm->numLightClusters += info->numSurfaceClusters;\r
- }\r
- \r
- /* allocate buffer for clusters and copy */\r
- lm->lightClusters = safe_malloc( lm->numLightClusters * sizeof( *lm->lightClusters ) );\r
- c = 0;\r
- for( i = 0; i < lm->numLightSurfaces; i++ )\r
- {\r
- /* get surface info */\r
- info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];\r
- \r
- /* add surface clusters */\r
- for( j = 0; j < info->numSurfaceClusters; j++ )\r
- lm->lightClusters[ c++ ] = surfaceClusters[ info->firstSurfaceCluster + j ];\r
- }\r
- \r
- /* set styles */\r
- lm->styles[ 0 ] = LS_NORMAL;\r
- for( i = 1; i < MAX_LIGHTMAPS; i++ )\r
- lm->styles[ i ] = LS_NONE;\r
- \r
- /* set supersampling size */\r
- lm->sw = lm->w * superSample;\r
- lm->sh = lm->h * superSample;\r
- \r
- /* add to super luxel count */\r
- numRawSuperLuxels += (lm->sw * lm->sh);\r
- \r
- /* manipulate origin/vecs for supersampling */\r
- if( superSample > 1 && lm->vecs != NULL )\r
- {\r
- /* calc inverse supersample */\r
- is = 1.0f / superSample;\r
- \r
- /* scale the vectors and shift the origin */\r
- #if 1\r
- /* new code that works for arbitrary supersampling values */\r
- VectorMA( lm->origin, -0.5, lm->vecs[ 0 ], lm->origin );\r
- VectorMA( lm->origin, -0.5, lm->vecs[ 1 ], lm->origin );\r
- VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );\r
- VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );\r
- VectorMA( lm->origin, is, lm->vecs[ 0 ], lm->origin );\r
- VectorMA( lm->origin, is, lm->vecs[ 1 ], lm->origin );\r
- #else\r
- /* old code that only worked with a value of 2 */\r
- VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );\r
- VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );\r
- VectorMA( lm->origin, -is, lm->vecs[ 0 ], lm->origin );\r
- VectorMA( lm->origin, -is, lm->vecs[ 1 ], lm->origin );\r
- #endif\r
- }\r
- \r
- /* allocate bsp lightmap storage */\r
- size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );\r
- if( lm->bspLuxels[ 0 ] == NULL )\r
- lm->bspLuxels[ 0 ] = safe_malloc( size );\r
- memset( lm->bspLuxels[ 0 ], 0, size );\r
- \r
- /* allocate radiosity lightmap storage */\r
- if( bounce )\r
- {\r
- size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );\r
- if( lm->radLuxels[ 0 ] == NULL )\r
- lm->radLuxels[ 0 ] = safe_malloc( size );\r
- memset( lm->radLuxels[ 0 ], 0, size );\r
- }\r
- \r
- /* allocate sampling lightmap storage */\r
- size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );\r
- if( lm->superLuxels[ 0 ] == NULL )\r
- lm->superLuxels[ 0 ] = safe_malloc( size );\r
- memset( lm->superLuxels[ 0 ], 0, size );\r
- \r
- /* allocate origin map storage */\r
- size = lm->sw * lm->sh * SUPER_ORIGIN_SIZE * sizeof( float );\r
- if( lm->superOrigins == NULL )\r
- lm->superOrigins = safe_malloc( size );\r
- memset( lm->superOrigins, 0, size );\r
- \r
- /* allocate normal map storage */\r
- size = lm->sw * lm->sh * SUPER_NORMAL_SIZE * sizeof( float );\r
- if( lm->superNormals == NULL )\r
- lm->superNormals = safe_malloc( size );\r
- memset( lm->superNormals, 0, size );\r
- \r
- /* allocate cluster map storage */\r
- size = lm->sw * lm->sh * sizeof( int );\r
- if( lm->superClusters == NULL )\r
- lm->superClusters = safe_malloc( size );\r
- size = lm->sw * lm->sh;\r
- sc = lm->superClusters;\r
- for( i = 0; i < size; i++ )\r
- (*sc++) = CLUSTER_UNMAPPED;\r
- \r
- /* deluxemap allocation */\r
- if( deluxemap )\r
- {\r
- /* allocate sampling deluxel storage */\r
- size = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );\r
- if( lm->superDeluxels == NULL )\r
- lm->superDeluxels = safe_malloc( size );\r
- memset( lm->superDeluxels, 0, size );\r
- \r
- /* allocate bsp deluxel storage */\r
- size = lm->w * lm->h * BSP_DELUXEL_SIZE * sizeof( float );\r
- if( lm->bspDeluxels == NULL )\r
- lm->bspDeluxels = safe_malloc( size );\r
- memset( lm->bspDeluxels, 0, size );\r
- }\r
- \r
- /* add to count */\r
- numLuxels += (lm->sw * lm->sh);\r
-}\r
-\r
-\r
-\r
-/*\r
-AddPatchToRawLightmap()\r
-projects a lightmap for a patch surface\r
-since lightmap calculation for surfaces is now handled in a general way (light_ydnar.c),\r
-it is no longer necessary for patch verts to fall exactly on a lightmap sample\r
-based on AllocateLightmapForPatch()\r
-*/\r
-\r
-qboolean AddPatchToRawLightmap( int num, rawLightmap_t *lm )\r
-{\r
- bspDrawSurface_t *ds;\r
- surfaceInfo_t *info;\r
- int x, y;\r
- bspDrawVert_t *verts, *a, *b;\r
- vec3_t delta;\r
- mesh_t src, *subdivided, *mesh;\r
- float sBasis, tBasis, s, t;\r
- float length, widthTable[ MAX_EXPANDED_AXIS ], heightTable[ MAX_EXPANDED_AXIS ];\r
- \r
- \r
- /* patches finish a raw lightmap */\r
- lm->finished = qtrue;\r
- \r
- /* get surface and info */\r
- ds = &bspDrawSurfaces[ num ];\r
- info = &surfaceInfos[ num ];\r
- \r
- /* make a temporary 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
- /* find the longest distance on each row/column */\r
- verts = mesh->verts;\r
- memset( widthTable, 0, sizeof( widthTable ) );\r
- memset( heightTable, 0, sizeof( heightTable ) );\r
- for( y = 0; y < mesh->height; y++ )\r
- {\r
- for( x = 0; x < mesh->width; x++ )\r
- {\r
- /* get width */\r
- if( x + 1 < mesh->width )\r
- {\r
- a = &verts[ (y * mesh->width) + x ];\r
- b = &verts[ (y * mesh->width) + x + 1 ];\r
- VectorSubtract( a->xyz, b->xyz, delta );\r
- length = VectorLength( delta );\r
- if( length > widthTable[ x ] )\r
- widthTable[ x ] = length;\r
- }\r
- \r
- /* get height */\r
- if( y + 1 < mesh->height )\r
- {\r
- a = &verts[ (y * mesh->width) + x ];\r
- b = &verts[ ((y + 1) * mesh->width) + x ];\r
- VectorSubtract( a->xyz, b->xyz, delta );\r
- length = VectorLength( delta );\r
- if( length > heightTable[ y ] )\r
- heightTable[ y ] = length;\r
- }\r
- }\r
- }\r
- \r
- /* determine lightmap width */\r
- length = 0;\r
- for( x = 0; x < (mesh->width - 1); x++ )\r
- length += widthTable[ x ];\r
- lm->w = ceil( length / lm->sampleSize ) + 1;\r
- if( lm->w < ds->patchWidth )\r
- lm->w = ds->patchWidth;\r
- if( lm->w > lm->customWidth )\r
- lm->w = lm->customWidth;\r
- sBasis = (float) (lm->w - 1) / (float) (ds->patchWidth - 1);\r
- \r
- /* determine lightmap height */\r
- length = 0;\r
- for( y = 0; y < (mesh->height - 1); y++ )\r
- length += heightTable[ y ];\r
- lm->h = ceil( length / lm->sampleSize ) + 1;\r
- if( lm->h < ds->patchHeight )\r
- lm->h = ds->patchHeight;\r
- if( lm->h > lm->customHeight )\r
- lm->h = lm->customHeight;\r
- tBasis = (float) (lm->h - 1) / (float) (ds->patchHeight - 1);\r
- \r
- /* free the temporary mesh */\r
- FreeMesh( mesh );\r
- \r
- /* set the lightmap texture coordinates in yDrawVerts */\r
- lm->wrap[ 0 ] = qtrue;\r
- lm->wrap[ 1 ] = qtrue;\r
- verts = &yDrawVerts[ ds->firstVert ];\r
- for( y = 0; y < ds->patchHeight; y++ )\r
- {\r
- t = (tBasis * y) + 0.5f;\r
- for( x = 0; x < ds->patchWidth; x++ )\r
- {\r
- s = (sBasis * x) + 0.5f;\r
- verts[ (y * ds->patchWidth) + x ].lightmap[ 0 ][ 0 ] = s * superSample;\r
- verts[ (y * ds->patchWidth) + x ].lightmap[ 0 ][ 1 ] = t * superSample;\r
- \r
- if( y == 0 && !VectorCompare( verts[ x ].xyz, verts[ ((ds->patchHeight - 1) * ds->patchWidth) + x ].xyz ) )\r
- lm->wrap[ 1 ] = qfalse;\r
- }\r
- \r
- if( !VectorCompare( verts[ (y * ds->patchWidth) ].xyz, verts[ (y * ds->patchWidth) + (ds->patchWidth - 1) ].xyz ) )\r
- lm->wrap[ 0 ] = qfalse;\r
- }\r
- \r
- /* debug code: */\r
- //% Sys_Printf( "wrap S: %d wrap T: %d\n", lm->wrap[ 0 ], lm->wrap[ 1 ] );\r
- //% if( lm->w > (ds->lightmapWidth & 0xFF) || lm->h > (ds->lightmapHeight & 0xFF) )\r
- //% Sys_Printf( "Patch lightmap: (%3d %3d) > (%3d, %3d)\n", lm->w, lm->h, ds->lightmapWidth & 0xFF, ds->lightmapHeight & 0xFF );\r
- //% ds->lightmapWidth = lm->w | (ds->lightmapWidth & 0xFFFF0000);\r
- //% ds->lightmapHeight = lm->h | (ds->lightmapHeight & 0xFFFF0000);\r
- \r
- /* add to counts */\r
- numPatchesLightmapped++;\r
- \r
- /* return */\r
- return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-AddSurfaceToRawLightmap()\r
-projects a lightmap for a surface\r
-based on AllocateLightmapForSurface()\r
-*/\r
-\r
-qboolean AddSurfaceToRawLightmap( int num, rawLightmap_t *lm )\r
-{\r
- bspDrawSurface_t *ds, *ds2;\r
- surfaceInfo_t *info, *info2;\r
- int num2, n, i, axisNum;\r
- float s, t, d, len, sampleSize;\r
- vec3_t mins, maxs, origin, faxis, size, exactSize, delta, normalized, vecs[ 2 ];\r
- vec4_t plane;\r
- bspDrawVert_t *verts;\r
- \r
- \r
- /* get surface and info */\r
- ds = &bspDrawSurfaces[ num ];\r
- info = &surfaceInfos[ num ];\r
- \r
- /* add the surface to the raw lightmap */\r
- lightSurfaces[ numLightSurfaces++ ] = num;\r
- lm->numLightSurfaces++;\r
- \r
- /* does this raw lightmap already have any surfaces? */\r
- if( lm->numLightSurfaces > 1 )\r
- {\r
- /* surface and raw lightmap must have the same lightmap projection axis */\r
- if( VectorCompare( info->axis, lm->axis ) == qfalse )\r
- return qfalse;\r
- \r
- /* match identical attributes */\r
- if( info->sampleSize != lm->sampleSize ||\r
- info->entityNum != lm->entityNum ||\r
- info->recvShadows != lm->recvShadows ||\r
- info->si->lmCustomWidth != lm->customWidth ||\r
- info->si->lmCustomHeight != lm->customHeight ||\r
- info->si->lmGamma != lm->gamma ||\r
- info->si->lmFilterRadius != lm->filterRadius ||\r
- info->si->splotchFix != lm->splotchFix )\r
- return qfalse;\r
- \r
- /* surface bounds must intersect with raw lightmap bounds */\r
- for( i = 0; i < 3; i++ )\r
- {\r
- if( info->mins[ i ] > lm->maxs[ i ] )\r
- return qfalse;\r
- if( info->maxs[ i ] < lm->mins[ i ] )\r
- return qfalse;\r
- }\r
- \r
- /* plane check (fixme: allow merging of nonplanars) */\r
- if( info->si->lmMergable == qfalse )\r
- {\r
- if( info->plane == NULL || lm->plane == NULL )\r
- return qfalse;\r
- \r
- /* compare planes */\r
- for( i = 0; i < 4; i++ )\r
- if( fabs( info->plane[ i ] - lm->plane[ i ] ) > EQUAL_EPSILON )\r
- return qfalse;\r
- }\r
- \r
- /* debug code hacking */\r
- //% if( lm->numLightSurfaces > 1 )\r
- //% return qfalse;\r
- }\r
- \r
- /* set plane */\r
- if( info->plane == NULL )\r
- lm->plane = NULL;\r
- \r
- /* add surface to lightmap bounds */\r
- AddPointToBounds( info->mins, lm->mins, lm->maxs );\r
- AddPointToBounds( info->maxs, lm->mins, lm->maxs );\r
- \r
- /* check to see if this is a non-planar patch */\r
- if( ds->surfaceType == MST_PATCH &&\r
- lm->axis[ 0 ] == 0.0f && lm->axis[ 1 ] == 0.0f && lm->axis[ 2 ] == 0.0f )\r
- return AddPatchToRawLightmap( num, lm );\r
- \r
- /* start with initially requested sample size */\r
- sampleSize = lm->sampleSize;\r
- \r
- /* round to the lightmap resolution */\r
- for( i = 0; i < 3; i++ )\r
- {\r
- exactSize[ i ] = lm->maxs[ i ] - lm->mins[ i ];\r
- mins[ i ] = sampleSize * floor( lm->mins[ i ] / sampleSize );\r
- maxs[ i ] = sampleSize * ceil( lm->maxs[ i ] / sampleSize );\r
- size[ i ] = (maxs[ i ] - mins[ i ]) / sampleSize + 1.0f;\r
- \r
- /* hack (god this sucks) */\r
- if( size[ i ] > lm->customWidth || size[ i ] > lm->customHeight )\r
- {\r
- i = -1;\r
- sampleSize += 1.0f;\r
- }\r
- }\r
- \r
- /* set actual sample size */\r
- lm->actualSampleSize = sampleSize;\r
- \r
- /* fixme: copy rounded mins/maxes to lightmap record? */\r
- if( lm->plane == NULL )\r
- {\r
- VectorCopy( mins, lm->mins );\r
- VectorCopy( maxs, lm->maxs );\r
- VectorCopy( mins, origin );\r
- }\r
- \r
- /* set lightmap origin */\r
- VectorCopy( lm->mins, origin );\r
- \r
- /* make absolute axis */\r
- faxis[ 0 ] = fabs( lm->axis[ 0 ] );\r
- faxis[ 1 ] = fabs( lm->axis[ 1 ] );\r
- faxis[ 2 ] = fabs( lm->axis[ 2 ] );\r
- \r
- /* clear out lightmap vectors */\r
- memset( vecs, 0, sizeof( vecs ) );\r
- \r
- /* classify the plane (x y or z major) (ydnar: biased to z axis projection) */\r
- if( faxis[ 2 ] >= faxis[ 0 ] && faxis[ 2 ] >= faxis[ 1 ] )\r
- {\r
- axisNum = 2;\r
- lm->w = size[ 0 ];\r
- lm->h = size[ 1 ];\r
- vecs[ 0 ][ 0 ] = 1.0f / sampleSize;\r
- vecs[ 1 ][ 1 ] = 1.0f / sampleSize;\r
- }\r
- else if( faxis[ 0 ] >= faxis[ 1 ] && faxis[ 0 ] >= faxis[ 2 ] )\r
- {\r
- axisNum = 0;\r
- lm->w = size[ 1 ];\r
- lm->h = size[ 2 ];\r
- vecs[ 0 ][ 1 ] = 1.0f / sampleSize;\r
- vecs[ 1 ][ 2 ] = 1.0f / sampleSize;\r
- }\r
- else\r
- {\r
- axisNum = 1;\r
- lm->w = size[ 0 ];\r
- lm->h = size[ 2 ];\r
- vecs[ 0 ][ 0 ] = 1.0f / sampleSize;\r
- vecs[ 1 ][ 2 ] = 1.0f / sampleSize;\r
- }\r
- \r
- /* check for bogus axis */\r
- if( faxis[ axisNum ] == 0.0f )\r
- {\r
- Sys_Printf( "WARNING: ProjectSurfaceLightmap: Chose a 0 valued axis\n" );\r
- lm->w = lm->h = 0;\r
- return qfalse;\r
- }\r
- \r
- /* store the axis number in the lightmap */\r
- lm->axisNum = axisNum;\r
- \r
- /* walk the list of surfaces on this raw lightmap */\r
- for( n = 0; n < lm->numLightSurfaces; n++ )\r
- {\r
- /* get surface */\r
- num2 = lightSurfaces[ lm->firstLightSurface + n ];\r
- ds2 = &bspDrawSurfaces[ num2 ];\r
- info2 = &surfaceInfos[ num2 ];\r
- verts = &yDrawVerts[ ds2->firstVert ];\r
- \r
- /* set the lightmap texture coordinates in yDrawVerts in [0, superSample * lm->customWidth] space */\r
- for( i = 0; i < ds2->numVerts; i++ )\r
- {\r
- VectorSubtract( verts[ i ].xyz, origin, delta );\r
- s = DotProduct( delta, vecs[ 0 ] ) + 0.5f;\r
- t = DotProduct( delta, vecs[ 1 ] ) + 0.5f;\r
- verts[ i ].lightmap[ 0 ][ 0 ] = s * superSample;\r
- verts[ i ].lightmap[ 0 ][ 1 ] = t * superSample;\r
- \r
- if( s > (float) lm->w || t > (float) lm->h )\r
- {\r
- Sys_FPrintf( SYS_VRB, "WARNING: Lightmap texture coords out of range: S %1.4f > %3d || T %1.4f > %3d\n",\r
- s, lm->w, t, lm->h );\r
- }\r
- }\r
- }\r
- \r
- /* get first drawsurface */\r
- num2 = lightSurfaces[ lm->firstLightSurface ];\r
- ds2 = &bspDrawSurfaces[ num2 ];\r
- info2 = &surfaceInfos[ num2 ];\r
- verts = &yDrawVerts[ ds2->firstVert ];\r
- \r
- /* calculate lightmap origin */\r
- if( VectorLength( ds2->lightmapVecs[ 2 ] ) )\r
- VectorCopy( ds2->lightmapVecs[ 2 ], plane );\r
- else\r
- VectorCopy( lm->axis, plane );\r
- plane[ 3 ] = DotProduct( verts[ 0 ].xyz, plane );\r
- \r
- VectorCopy( origin, lm->origin );\r
- d = DotProduct( lm->origin, plane ) - plane[ 3 ];\r
- d /= plane[ axisNum ];\r
- lm->origin[ axisNum ] -= d;\r
- \r
- /* legacy support */\r
- VectorCopy( lm->origin, ds->lightmapOrigin );\r
- \r
- /* for planar surfaces, create lightmap vectors for st->xyz conversion */\r
- if( VectorLength( ds->lightmapVecs[ 2 ] ) || 1 ) /* ydnar: can't remember what exactly i was thinking here... */\r
- {\r
- /* allocate space for the vectors */\r
- lm->vecs = safe_malloc( 3 * sizeof( vec3_t ) );\r
- memset( lm->vecs, 0, 3 * sizeof( vec3_t ) );\r
- VectorCopy( ds->lightmapVecs[ 2 ], lm->vecs[ 2 ] );\r
- \r
- /* project stepped lightmap blocks and subtract to get planevecs */\r
- for( i = 0; i < 2; i++ )\r
- {\r
- len = VectorNormalize( vecs[ i ], normalized );\r
- VectorScale( normalized, (1.0 / len), lm->vecs[ i ] );\r
- d = DotProduct( lm->vecs[ i ], plane );\r
- d /= plane[ axisNum ];\r
- lm->vecs[ i ][ axisNum ] -= d;\r
- }\r
- }\r
- else\r
- {\r
- /* lightmap vectors are useless on a non-planar surface */\r
- lm->vecs = NULL;\r
- }\r
- \r
- /* add to counts */\r
- if( ds->surfaceType == MST_PATCH )\r
- {\r
- numPatchesLightmapped++;\r
- if( lm->plane != NULL )\r
- numPlanarPatchesLightmapped++;\r
- }\r
- else\r
- {\r
- if( lm->plane != NULL )\r
- numPlanarsLightmapped++;\r
- else\r
- numNonPlanarsLightmapped++;\r
- }\r
- \r
- /* return */\r
- return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-CompareSurfaceInfo()\r
-compare function for qsort()\r
-*/\r
-\r
-static int CompareSurfaceInfo( const void *a, const void *b )\r
-{\r
- surfaceInfo_t *aInfo, *bInfo;\r
- int i;\r
- \r
-\r
- /* get surface info */\r
- aInfo = &surfaceInfos[ *((int*) a) ];\r
- bInfo = &surfaceInfos[ *((int*) b) ];\r
- \r
- /* model first */\r
- if( aInfo->model < bInfo->model )\r
- return 1;\r
- else if( aInfo->model > bInfo->model )\r
- return -1;\r
- \r
- /* then lightmap status */\r
- if( aInfo->hasLightmap < bInfo->hasLightmap )\r
- return 1;\r
- else if( aInfo->hasLightmap > bInfo->hasLightmap )\r
- return -1;\r
- \r
- /* then lightmap sample size */\r
- if( aInfo->sampleSize < bInfo->sampleSize )\r
- return 1;\r
- else if( aInfo->sampleSize > bInfo->sampleSize )\r
- return -1;\r
- \r
- /* then lightmap axis */\r
- for( i = 0; i < 3; i++ )\r
- {\r
- if( aInfo->axis[ i ] < bInfo->axis[ i ] )\r
- return 1;\r
- else if( aInfo->axis[ i ] > bInfo->axis[ i ] )\r
- return -1;\r
- }\r
- \r
- /* then plane */\r
- if( aInfo->plane == NULL && bInfo->plane != NULL )\r
- return 1;\r
- else if( aInfo->plane != NULL && bInfo->plane == NULL )\r
- return -1;\r
- else if( aInfo->plane != NULL && bInfo->plane != NULL )\r
- {\r
- for( i = 0; i < 4; i++ )\r
- {\r
- if( aInfo->plane[ i ] < bInfo->plane[ i ] )\r
- return 1;\r
- else if( aInfo->plane[ i ] > bInfo->plane[ i ] )\r
- return -1;\r
- }\r
- }\r
- \r
- /* then position in world */\r
- for( i = 0; i < 3; i++ )\r
- {\r
- if( aInfo->mins[ i ] < bInfo->mins[ i ] )\r
- return 1;\r
- else if( aInfo->mins[ i ] > bInfo->mins[ i ] )\r
- return -1;\r
- }\r
- \r
- /* these are functionally identical (this should almost never happen) */\r
- return 0;\r
-}\r
-\r
-\r
-\r
-/*\r
-SetupSurfaceLightmaps()\r
-allocates lightmaps for every surface in the bsp that needs one\r
-this depends on yDrawVerts being allocated\r
-*/\r
-\r
-void SetupSurfaceLightmaps( void )\r
-{\r
- int i, j, k, s,num, num2;\r
- bspModel_t *model;\r
- bspLeaf_t *leaf;\r
- bspDrawSurface_t *ds, *ds2;\r
- surfaceInfo_t *info, *info2;\r
- rawLightmap_t *lm;\r
- qboolean added;\r
- vec3_t mapSize, entityOrigin;\r
- \r
- \r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n");\r
- \r
- /* determine supersample amount */\r
- if( superSample < 1 )\r
- superSample = 1;\r
- else if( superSample > 8 )\r
- {\r
- Sys_Printf( "WARNING: Insane supersampling amount (%d) detected.\n", superSample );\r
- superSample = 8;\r
- }\r
- \r
- /* clear map bounds */\r
- ClearBounds( mapMins, mapMaxs );\r
- \r
- /* allocate a list of surface clusters */\r
- numSurfaceClusters = 0;\r
- maxSurfaceClusters = numBSPLeafSurfaces;\r
- surfaceClusters = safe_malloc( maxSurfaceClusters * sizeof( *surfaceClusters ) );\r
- memset( surfaceClusters, 0, maxSurfaceClusters * sizeof( *surfaceClusters ) );\r
- \r
- /* allocate a list for per-surface info */\r
- surfaceInfos = safe_malloc( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );\r
- memset( surfaceInfos, 0, numBSPDrawSurfaces * sizeof( *surfaceInfos ) );\r
- for( i = 0; i < numBSPDrawSurfaces; i++ )\r
- surfaceInfos[ i ].childSurfaceNum = -1;\r
- \r
- /* allocate a list of surface indexes to be sorted */\r
- sortSurfaces = safe_malloc( numBSPDrawSurfaces * sizeof( int ) );\r
- memset( sortSurfaces, 0, numBSPDrawSurfaces * sizeof( int ) );\r
- \r
- /* walk each model in the bsp */\r
- for( i = 0; i < numBSPModels; i++ )\r
- {\r
- /* get model */\r
- model = &bspModels[ i ];\r
- \r
- /* walk the list of surfaces in this model and fill out the info structs */\r
- for( j = 0; j < model->numBSPSurfaces; j++ )\r
- {\r
- /* make surface index */\r
- num = model->firstBSPSurface + j;\r
- \r
- /* copy index to sort list */\r
- sortSurfaces[ num ] = num;\r
- \r
- /* get surface and info */\r
- ds = &bspDrawSurfaces[ num ];\r
- info = &surfaceInfos[ num ];\r
- \r
- /* set entity origin */\r
- if( ds->numVerts > 0 )\r
- VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );\r
- else\r
- VectorClear( entityOrigin );\r
- \r
- /* basic setup */\r
- info->model = model;\r
- info->lm = NULL;\r
- info->plane = NULL;\r
- info->firstSurfaceCluster = numSurfaceClusters;\r
- \r
- /* get extra data */\r
- info->si = GetSurfaceExtraShaderInfo( num );\r
- if( info->si == NULL )\r
- info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );\r
- info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );\r
- info->entityNum = GetSurfaceExtraEntityNum( num );\r
- info->castShadows = GetSurfaceExtraCastShadows( num );\r
- info->recvShadows = GetSurfaceExtraRecvShadows( num );\r
- info->sampleSize = GetSurfaceExtraSampleSize( num );\r
- info->longestCurve = GetSurfaceExtraLongestCurve( num );\r
- info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );\r
- GetSurfaceExtraLightmapAxis( num, info->axis );\r
- \r
- /* mark parent */\r
- if( info->parentSurfaceNum >= 0 )\r
- surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;\r
- \r
- /* determine surface bounds */\r
- ClearBounds( info->mins, info->maxs );\r
- for( k = 0; k < ds->numVerts; k++ )\r
- {\r
- AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );\r
- AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );\r
- }\r
- \r
- /* find all the bsp clusters the surface falls into */\r
- for( k = 0; k < numBSPLeafs; k++ )\r
- {\r
- /* get leaf */\r
- leaf = &bspLeafs[ k ];\r
- \r
- /* test bbox */\r
- if( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||\r
- leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||\r
- leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] )\r
- continue;\r
- \r
- /* test leaf surfaces */\r
- for( s = 0; s < leaf->numBSPLeafSurfaces; s++ )\r
- {\r
- if( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num )\r
- {\r
- if( numSurfaceClusters >= maxSurfaceClusters )\r
- Error( "maxSurfaceClusters exceeded" );\r
- surfaceClusters[ numSurfaceClusters ] = leaf->cluster;\r
- numSurfaceClusters++;\r
- info->numSurfaceClusters++;\r
- }\r
- }\r
- }\r
- \r
- /* determine if surface is planar */\r
- if( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f )\r
- {\r
- /* make a plane */\r
- info->plane = safe_malloc( 4 * sizeof( float ) );\r
- VectorCopy( ds->lightmapVecs[ 2 ], info->plane );\r
- info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );\r
- }\r
- \r
- /* determine if surface requires a lightmap */\r
- if( ds->surfaceType == MST_TRIANGLE_SOUP ||\r
- ds->surfaceType == MST_FOLIAGE ||\r
- (info->si->compileFlags & C_VERTEXLIT) )\r
- numSurfsVertexLit++;\r
- else\r
- {\r
- numSurfsLightmapped++;\r
- info->hasLightmap = qtrue;\r
- }\r
- }\r
- }\r
- \r
- /* find longest map distance */\r
- VectorSubtract( mapMaxs, mapMins, mapSize );\r
- maxMapDistance = VectorLength( mapSize );\r
- \r
- /* sort the surfaces info list */\r
- qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );\r
- \r
- /* allocate a list of surfaces that would go into raw lightmaps */\r
- numLightSurfaces = 0;\r
- lightSurfaces = safe_malloc( numSurfsLightmapped * sizeof( int ) );\r
- memset( lightSurfaces, 0, numSurfsLightmapped * sizeof( int ) );\r
- \r
- /* allocate a list of raw lightmaps */\r
- numRawSuperLuxels = 0;\r
- numRawLightmaps = 0;\r
- rawLightmaps = safe_malloc( numSurfsLightmapped * sizeof( *rawLightmaps ) );\r
- memset( rawLightmaps, 0, numSurfsLightmapped * sizeof( *rawLightmaps ) );\r
- \r
- /* walk the list of sorted surfaces */\r
- for( i = 0; i < numBSPDrawSurfaces; i++ )\r
- {\r
- /* get info and attempt early out */\r
- num = sortSurfaces[ i ];\r
- ds = &bspDrawSurfaces[ num ];\r
- info = &surfaceInfos[ num ];\r
- if( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 )\r
- continue;\r
- \r
- /* allocate a new raw lightmap */\r
- lm = &rawLightmaps[ numRawLightmaps ];\r
- numRawLightmaps++;\r
- \r
- /* set it up */\r
- lm->splotchFix = info->si->splotchFix;\r
- lm->firstLightSurface = numLightSurfaces;\r
- lm->numLightSurfaces = 0;\r
- lm->sampleSize = info->sampleSize;\r
- lm->actualSampleSize = info->sampleSize;\r
- lm->entityNum = info->entityNum;\r
- lm->recvShadows = info->recvShadows;\r
- lm->gamma = info->si->lmGamma;\r
- lm->filterRadius = info->si->lmFilterRadius;\r
- VectorCopy( info->axis, lm->axis );\r
- lm->plane = info->plane; \r
- VectorCopy( info->mins, lm->mins );\r
- VectorCopy( info->maxs, lm->maxs );\r
- \r
- lm->customWidth = info->si->lmCustomWidth;\r
- lm->customHeight = info->si->lmCustomHeight;\r
- \r
- /* add the surface to the raw lightmap */\r
- AddSurfaceToRawLightmap( num, lm );\r
- info->lm = lm;\r
- \r
- /* do an exhaustive merge */\r
- added = qtrue;\r
- while( added )\r
- {\r
- /* walk the list of surfaces again */\r
- added = qfalse;\r
- for( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )\r
- {\r
- /* get info and attempt early out */\r
- num2 = sortSurfaces[ j ];\r
- ds2 = &bspDrawSurfaces[ num2 ];\r
- info2 = &surfaceInfos[ num2 ];\r
- if( info2->hasLightmap == qfalse || info2->lm != NULL )\r
- continue;\r
- \r
- /* add the surface to the raw lightmap */\r
- if( AddSurfaceToRawLightmap( num2, lm ) )\r
- {\r
- info2->lm = lm;\r
- added = qtrue;\r
- }\r
- else\r
- {\r
- /* back up one */\r
- lm->numLightSurfaces--;\r
- numLightSurfaces--;\r
- }\r
- }\r
- }\r
- \r
- /* finish the lightmap and allocate the various buffers */\r
- FinishRawLightmap( lm );\r
- }\r
- \r
- /* allocate vertex luxel storage */\r
- for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
- {\r
- vertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) ); \r
- memset( vertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );\r
- radVertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );\r
- memset( radVertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );\r
- }\r
- \r
- /* emit some stats */\r
- Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );\r
- Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );\r
- Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );\r
- Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );\r
- Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );\r
- Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );\r
- Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );\r
- Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );\r
-}\r
-\r
-\r
-\r
-/*\r
-StitchSurfaceLightmaps()\r
-stitches lightmap edges\r
-2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams\r
-*/\r
-\r
-#define MAX_STITCH_CANDIDATES 32\r
-#define MAX_STITCH_LUXELS 64\r
-\r
-void StitchSurfaceLightmaps( void )\r
-{\r
- int i, j, x, y, x2, y2, *cluster, *cluster2,\r
- numStitched, numCandidates, numLuxels, f, fOld, start;\r
- rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];\r
- float *luxel, *luxel2, *origin, *origin2, *normal, *normal2, \r
- sampleSize, average[ 3 ], totalColor, ootc, *luxels[ MAX_STITCH_LUXELS ];\r
- \r
- \r
- /* disabled for now */\r
- return;\r
- \r
- /* note it */\r
- Sys_Printf( "--- StitchSurfaceLightmaps ---\n");\r
-\r
- /* init pacifier */\r
- fOld = -1;\r
- start = I_FloatTime();\r
- \r
- /* walk the list of raw lightmaps */\r
- numStitched = 0;\r
- for( i = 0; i < numRawLightmaps; i++ )\r
- {\r
- /* print pacifier */\r
- f = 10 * i / numRawLightmaps;\r
- if( f != fOld )\r
- {\r
- fOld = f;\r
- Sys_Printf( "%i...", f );\r
- }\r
- \r
- /* get lightmap a */\r
- a = &rawLightmaps[ i ];\r
- \r
- /* walk rest of lightmaps */\r
- numCandidates = 0;\r
- for( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )\r
- {\r
- /* get lightmap b */\r
- b = &rawLightmaps[ j ];\r
- \r
- /* test bounding box */\r
- if( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||\r
- a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||\r
- a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] )\r
- continue;\r
- \r
- /* add candidate */\r
- c[ numCandidates++ ] = b;\r
- }\r
- \r
- /* walk luxels */\r
- for( y = 0; y < a->sh; y++ )\r
- {\r
- for( x = 0; x < a->sw; x++ )\r
- {\r
- /* ignore unmapped/unlit luxels */\r
- lm = a;\r
- cluster = SUPER_CLUSTER( x, y );\r
- if( *cluster == CLUSTER_UNMAPPED )\r
- continue;\r
- luxel = SUPER_LUXEL( 0, x, y );\r
- if( luxel[ 3 ] <= 0.0f )\r
- continue;\r
- \r
- /* get particulars */\r
- origin = SUPER_ORIGIN( x, y );\r
- normal = SUPER_NORMAL( x, y );\r
- \r
- /* walk candidate list */\r
- for( j = 0; j < numCandidates; j++ )\r
- {\r
- /* get candidate */\r
- b = c[ j ];\r
- lm = b;\r
- \r
- /* set samplesize to the smaller of the pair */\r
- sampleSize = 0.5f * (a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize);\r
- \r
- /* test bounding box */\r
- if( origin[ 0 ] < (b->mins[ 0 ] - sampleSize) || (origin[ 0 ] > b->maxs[ 0 ] + sampleSize) ||\r
- origin[ 1 ] < (b->mins[ 1 ] - sampleSize) || (origin[ 1 ] > b->maxs[ 1 ] + sampleSize) ||\r
- origin[ 2 ] < (b->mins[ 2 ] - sampleSize) || (origin[ 2 ] > b->maxs[ 2 ] + sampleSize) )\r
- continue;\r
- \r
- /* walk candidate luxels */\r
- VectorClear( average );\r
- numLuxels = 0;\r
- totalColor = 0.0f;\r
- for( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )\r
- {\r
- for( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )\r
- {\r
- /* ignore same luxels */\r
- if( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 )\r
- continue;\r
- \r
- /* ignore unmapped/unlit luxels */\r
- cluster2 = SUPER_CLUSTER( x2, y2 );\r
- if( *cluster2 == CLUSTER_UNMAPPED )\r
- continue;\r
- luxel2 = SUPER_LUXEL( 0, x2, y2 );\r
- if( luxel2[ 3 ] <= 0.0f )\r
- continue;\r
- \r
- /* get particulars */\r
- origin2 = SUPER_ORIGIN( x2, y2 );\r
- normal2 = SUPER_NORMAL( x2, y2 );\r
- \r
- /* test normal */\r
- if( DotProduct( normal, normal2 ) < 0.5f )\r
- continue;\r
- \r
- /* test bounds */\r
- if( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||\r
- fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||\r
- fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize )\r
- continue;\r
- \r
- /* add luxel */\r
- //% VectorSet( luxel2, 255, 0, 255 );\r
- luxels[ numLuxels++ ] = luxel2;\r
- VectorAdd( average, luxel2, average );\r
- totalColor += luxel2[ 3 ];\r
- }\r
- }\r
- \r
- /* early out */\r
- if( numLuxels == 0 )\r
- continue;\r
- \r
- /* scale average */\r
- ootc = 1.0f / totalColor;\r
- VectorScale( average, ootc, luxel );\r
- luxel[ 3 ] = 1.0f;\r
- numStitched++;\r
- }\r
- }\r
- }\r
- }\r
- \r
- /* emit statistics */\r
- Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );\r
- Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );\r
-}\r
-\r
-\r
-\r
-/*\r
-CompareBSPLuxels()\r
-compares two surface lightmaps' bsp luxels, ignoring occluded luxels\r
-*/\r
-\r
-#define LUXEL_TOLERANCE 0.0025\r
-#define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */\r
-\r
-static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )\r
-{\r
- rawLightmap_t *lm;\r
- int x, y;\r
- double delta, total, rd, gd, bd;\r
- float *aLuxel, *bLuxel;\r
- \r
- \r
- /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */\r
- if( (minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ]) &&\r
- ((aNum == 0 && bNum != 0) || (aNum != 0 && bNum == 0)) )\r
- return qfalse;\r
- \r
- /* compare */\r
- if( a->w != b->w || a->h != b->h ||\r
- a->customWidth != b->customWidth || a->customHeight != b->customHeight ||\r
- a->gamma != b->gamma ||\r
- a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )\r
- return qfalse;\r
- \r
- /* compare luxels */\r
- delta = 0.0;\r
- total = 0.0;\r
- for( y = 0; y < a->h; y++ )\r
- {\r
- for( x = 0; x < a->w; x++ )\r
- {\r
- /* increment total */\r
- total += 1.0;\r
- \r
- /* get luxels */\r
- lm = a; aLuxel = BSP_LUXEL( aNum, x, y );\r
- lm = b; bLuxel = BSP_LUXEL( bNum, x, y );\r
- \r
- /* ignore unused luxels */\r
- if( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 )\r
- continue;\r
- \r
- /* get deltas */\r
- rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );\r
- gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );\r
- bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );\r
- \r
- /* 2003-09-27: compare individual luxels */\r
- if( rd > 3.0 || gd > 3.0 || bd > 3.0 )\r
- return qfalse;\r
- \r
- /* compare (fixme: take into account perceptual differences) */\r
- delta += rd * LUXEL_COLOR_FRAC;\r
- delta += gd * LUXEL_COLOR_FRAC;\r
- delta += bd * LUXEL_COLOR_FRAC;\r
- \r
- /* is the change too high? */\r
- if( total > 0.0 && ((delta / total) > LUXEL_TOLERANCE) )\r
- return qfalse;\r
- }\r
- }\r
- \r
- /* made it this far, they must be identical (or close enough) */\r
- return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-MergeBSPLuxels()\r
-merges two surface lightmaps' bsp luxels, overwriting occluded luxels\r
-*/\r
-\r
-static void MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )\r
-{\r
- rawLightmap_t *lm;\r
- int x, y;\r
- float luxel[ 3 ], *aLuxel, *bLuxel;\r
- \r
- \r
- /* compare */\r
- if( a->w != b->w || a->h != b->h ||\r
- a->customWidth != b->customWidth || a->customHeight != b->customHeight ||\r
- a->gamma != b->gamma ||\r
- a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )\r
- return;\r
- \r
- /* merge luxels */\r
- for( y = 0; y < a->h; y++ )\r
- {\r
- for( x = 0; x < a->w; x++ )\r
- {\r
- /* get luxels */\r
- lm = a; aLuxel = BSP_LUXEL( aNum, x, y );\r
- lm = b; bLuxel = BSP_LUXEL( bNum, x, y );\r
- \r
- /* handle occlusion mismatch */\r
- if( aLuxel[ 0 ] < 0.0f )\r
- VectorCopy( bLuxel, aLuxel );\r
- else if( bLuxel[ 0 ] < 0.0f )\r
- VectorCopy( aLuxel, bLuxel );\r
- else\r
- {\r
- /* average */\r
- VectorAdd( aLuxel, bLuxel, luxel );\r
- VectorScale( luxel, 0.5f, luxel );\r
- \r
- /* debugging code */\r
- //% luxel[ 2 ] += 64.0f;\r
- \r
- /* copy to both */\r
- VectorCopy( luxel, aLuxel );\r
- VectorCopy( luxel, bLuxel );\r
- }\r
- }\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-ApproximateLuxel()\r
-determines if a single luxel is can be approximated with the interpolated vertex rgba\r
-*/\r
-\r
-static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv )\r
-{\r
- int i, x, y, d, lightmapNum;\r
- float *luxel;\r
- vec3_t color, vertexColor;\r
- byte cb[ 4 ], vcb[ 4 ];\r
- \r
- \r
- /* find luxel xy coords */\r
- x = dv->lightmap[ 0 ][ 0 ] / superSample;\r
- y = dv->lightmap[ 0 ][ 1 ] / superSample;\r
- if( x < 0 )\r
- x = 0;\r
- else if( x >= lm->w )\r
- x = lm->w - 1;\r
- if( y < 0 )\r
- y = 0;\r
- else if( y >= lm->h )\r
- y = lm->h - 1;\r
- \r
- /* walk list */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- /* early out */\r
- if( lm->styles[ lightmapNum ] == LS_NONE )\r
- continue;\r
- \r
- /* get luxel */\r
- luxel = BSP_LUXEL( lightmapNum, x, y );\r
- \r
- /* ignore occluded luxels */\r
- if( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f )\r
- return qtrue;\r
- \r
- /* copy, set min color and compare */\r
- VectorCopy( luxel, color );\r
- VectorCopy( dv->color[ 0 ], vertexColor );\r
-\r
- /* styles are not affected by minlight */\r
- if( lightmapNum == 0 )\r
- {\r
- for( i = 0; i < 3; i++ )\r
- {\r
- /* set min color */\r
- if( color[ i ] < minLight[ i ] )\r
- color[ i ] = minLight[ i ];\r
- if( vertexColor[ i ] < minLight[ i ] ) /* note NOT minVertexLight */\r
- vertexColor[ i ] = minLight[ i ];\r
- }\r
- }\r
- \r
- /* set to bytes */\r
- ColorToBytes( color, cb, 1.0f );\r
- ColorToBytes( vertexColor, vcb, 1.0f );\r
- \r
- /* compare */\r
- for( i = 0; i < 3; i++ )\r
- {\r
- d = cb[ i ] - vcb[ i ];\r
- if( d < 0 )\r
- d *= -1;\r
- if( d > approximateTolerance )\r
- return qfalse;\r
- }\r
- }\r
- \r
- /* close enough for the girls i date */\r
- return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-ApproximateTriangle()\r
-determines if a single triangle can be approximated with vertex rgba\r
-*/\r
-\r
-static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] )\r
-{\r
- bspDrawVert_t mid, *dv2[ 3 ];\r
- int max;\r
- \r
- \r
- /* approximate the vertexes */\r
- if( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse )\r
- return qfalse;\r
- if( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse )\r
- return qfalse;\r
- if( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse )\r
- return qfalse;\r
- \r
- /* subdivide calc */\r
- {\r
- int i;\r
- float 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
- dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ (i + 1) % 3 ]->lightmap[ 0 ][ 0 ];\r
- dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ (i + 1) % 3 ]->lightmap[ 0 ][ 1 ];\r
- dist = sqrt( (dx * dx) + (dy * dy) );\r
- if( dist > maxDist )\r
- {\r
- maxDist = dist;\r
- max = i;\r
- }\r
- }\r
- \r
- /* try to early out */\r
- if( i < 0 || maxDist < subdivideThreshold )\r
- return qtrue;\r
- }\r
-\r
- /* split the longest edge and map it */\r
- LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );\r
- if( ApproximateLuxel( lm, &mid ) == qfalse )\r
- return qfalse;\r
- \r
- /* recurse to first triangle */\r
- VectorCopy( dv, dv2 );\r
- dv2[ max ] = ∣\r
- if( ApproximateTriangle_r( lm, dv2 ) == qfalse )\r
- return qfalse;\r
- \r
- /* recurse to second triangle */\r
- VectorCopy( dv, dv2 );\r
- dv2[ (max + 1) % 3 ] = ∣\r
- return ApproximateTriangle_r( lm, dv2 );\r
-}\r
-\r
-\r
-\r
-/*\r
-ApproximateLightmap()\r
-determines if a raw lightmap can be approximated sufficiently with vertex colors\r
-*/\r
-\r
-static qboolean ApproximateLightmap( rawLightmap_t *lm )\r
-{\r
- int n, num, i, x, y, pw[ 5 ], r;\r
- bspDrawSurface_t *ds;\r
- surfaceInfo_t *info;\r
- mesh_t src, *subdivided, *mesh;\r
- bspDrawVert_t *verts, *dv[ 3 ];\r
- qboolean approximated;\r
- \r
- \r
- /* approximating? */\r
- if( approximateTolerance <= 0 )\r
- return qfalse;\r
- \r
- /* test for jmonroe */\r
- #if 0\r
- /* don't approx lightmaps with styled twins */\r
- if( lm->numStyledTwins > 0 )\r
- return qfalse;\r
- \r
- /* don't approx lightmaps with styles */\r
- for( i = 1; i < MAX_LIGHTMAPS; i++ )\r
- {\r
- if( lm->styles[ i ] != LS_NONE )\r
- return qfalse;\r
- }\r
- #endif\r
- \r
- /* assume reduced until shadow detail is found */\r
- approximated = qtrue;\r
- \r
- /* walk the list of surfaces on this raw lightmap */\r
- for( n = 0; n < lm->numLightSurfaces; n++ )\r
- {\r
- /* get surface */\r
- num = lightSurfaces[ lm->firstLightSurface + n ];\r
- ds = &bspDrawSurfaces[ num ];\r
- info = &surfaceInfos[ num ];\r
- \r
- /* bail if lightmap doesn't match up */\r
- if( info->lm != lm )\r
- continue;\r
- \r
- /* assume reduced initially */\r
- info->approximated = qtrue;\r
- \r
- /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */\r
- if( (info->maxs[ 0 ] - info->mins[ 0 ]) <= (2.0f * info->sampleSize) &&\r
- (info->maxs[ 1 ] - info->mins[ 1 ]) <= (2.0f * info->sampleSize) &&\r
- (info->maxs[ 2 ] - info->mins[ 2 ]) <= (2.0f * info->sampleSize) )\r
- {\r
- numSurfsVertexForced++;\r
- continue;\r
- }\r
- \r
- /* handle the triangles */\r
- switch( ds->surfaceType )\r
- {\r
- case MST_PLANAR:\r
- /* get verts */\r
- verts = yDrawVerts + ds->firstVert;\r
- \r
- /* map the triangles */\r
- for( i = 0; i < ds->numIndexes && info->approximated; 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
- info->approximated = ApproximateTriangle_r( lm, dv );\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
- /* map the mesh quads */\r
- for( y = 0; y < (mesh->height - 1) && info->approximated; y++ )\r
- {\r
- for( x = 0; x < (mesh->width - 1) && info->approximated; 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
- info->approximated = ApproximateTriangle_r( lm, dv );\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
- if( info->approximated )\r
- info->approximated = ApproximateTriangle_r( lm, dv );\r
- }\r
- }\r
- \r
- /* free the mesh */\r
- FreeMesh( mesh );\r
- break;\r
- \r
- default:\r
- break;\r
- }\r
- \r
- /* reduced? */\r
- if( info->approximated == qfalse )\r
- approximated = qfalse;\r
- else\r
- numSurfsVertexApproximated++;\r
- }\r
- \r
- /* return */\r
- return approximated;\r
-}\r
-\r
-\r
-\r
-/*\r
-TestOutLightmapStamp()\r
-tests a stamp on a given lightmap for validity\r
-*/\r
-\r
-static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y )\r
-{\r
- int sx, sy, ox, oy, offset;\r
- float *luxel;\r
-\r
- \r
- /* bounds check */\r
- if( x < 0 || y < 0 || (x + lm->w) > olm->customWidth || (y + lm->h) > olm->customHeight )\r
- return qfalse;\r
- \r
- /* test the stamp */\r
- for( sy = 0; sy < lm->h; sy++ )\r
- {\r
- for( sx = 0; sx < lm->w; sx++ )\r
- {\r
- /* get luxel */\r
- luxel = BSP_LUXEL( lightmapNum, sx, sy );\r
- if( luxel[ 0 ] < 0.0f )\r
- continue;\r
- \r
- /* get bsp lightmap coords and test */\r
- ox = x + sx;\r
- oy = y + sy;\r
- offset = (oy * olm->customWidth) + ox;\r
- if( olm->lightBits[ offset >> 3 ] & (1 << (offset & 7)) )\r
- return qfalse;\r
- }\r
- }\r
- \r
- /* stamp is empty */\r
- return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-SetupOutLightmap()\r
-sets up an output lightmap\r
-*/\r
-\r
-static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm )\r
-{\r
- /* dummy check */\r
- if( lm == NULL || olm == NULL )\r
- return;\r
- \r
- /* is this a "normal" bsp-stored lightmap? */\r
- if( (lm->customWidth == LIGHTMAP_WIDTH && lm->customHeight == LIGHTMAP_HEIGHT) || externalLightmaps )\r
- {\r
- olm->lightmapNum = numBSPLightmaps;\r
- numBSPLightmaps++;\r
- \r
- /* lightmaps are interleaved with light direction maps */\r
- if( deluxemap )\r
- numBSPLightmaps++;\r
- }\r
- else\r
- olm->lightmapNum = -3;\r
- \r
- /* set external lightmap number */\r
- olm->extLightmapNum = -1;\r
- \r
- /* set it up */\r
- olm->numLightmaps = 0;\r
- olm->customWidth = lm->customWidth;\r
- olm->customHeight = lm->customHeight;\r
- olm->freeLuxels = olm->customWidth * olm->customHeight;\r
- olm->numShaders = 0;\r
- \r
- /* allocate buffers */\r
- olm->lightBits = safe_malloc( (olm->customWidth * olm->customHeight / 8) + 8 );\r
- memset( olm->lightBits, 0, (olm->customWidth * olm->customHeight / 8) + 8 );\r
- olm->bspLightBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );\r
- memset( olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3 );\r
- if( deluxemap )\r
- {\r
- olm->bspDirBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );\r
- memset( olm->bspDirBytes, 0, olm->customWidth * olm->customHeight * 3 );\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-FindOutLightmaps()\r
-for a given surface lightmap, find output lightmap pages and positions for it\r
-*/\r
-\r
-static void FindOutLightmaps( rawLightmap_t *lm )\r
-{\r
- int i, j, lightmapNum, xMax, yMax, x, y, sx, sy, ox, oy, offset, temp;\r
- outLightmap_t *olm;\r
- surfaceInfo_t *info;\r
- float *luxel, *deluxel;\r
- vec3_t color, direction;\r
- byte *pixel;\r
- qboolean ok;\r
- \r
- \r
- /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- lm->outLightmapNums[ lightmapNum ] = -3;\r
- \r
- /* can this lightmap be approximated with vertex color? */\r
- if( ApproximateLightmap( lm ) )\r
- return;\r
- \r
- /* walk list */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- /* early out */\r
- if( lm->styles[ lightmapNum ] == LS_NONE )\r
- continue;\r
- \r
- /* don't store twinned lightmaps */\r
- if( lm->twins[ lightmapNum ] != NULL )\r
- continue;\r
- \r
- /* if this is a styled lightmap, try some normalized locations first */\r
- ok = qfalse;\r
- if( lightmapNum > 0 && outLightmaps != NULL )\r
- {\r
- /* loop twice */\r
- for( j = 0; j < 2; j++ )\r
- {\r
- /* try identical position */\r
- for( i = 0; i < numOutLightmaps; i++ )\r
- {\r
- /* get the output lightmap */\r
- olm = &outLightmaps[ i ];\r
- \r
- /* simple early out test */\r
- if( olm->freeLuxels < lm->used )\r
- continue;\r
- \r
- /* don't store non-custom raw lightmaps on custom bsp lightmaps */\r
- if( olm->customWidth != lm->customWidth ||\r
- olm->customHeight != lm->customHeight )\r
- continue;\r
- \r
- /* try identical */\r
- if( j == 0 )\r
- {\r
- x = lm->lightmapX[ 0 ];\r
- y = lm->lightmapY[ 0 ];\r
- ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );\r
- }\r
- \r
- /* try shifting */\r
- else\r
- {\r
- for( sy = -1; sy <= 1; sy++ )\r
- {\r
- for( sx = -1; sx <= 1; sx++ )\r
- {\r
- x = lm->lightmapX[ 0 ] + sx * (olm->customWidth >> 1); //% lm->w;\r
- y = lm->lightmapY[ 0 ] + sy * (olm->customHeight >> 1); //% lm->h;\r
- ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );\r
-\r
- if( ok )\r
- break;\r
- }\r
- \r
- if( ok )\r
- break;\r
- }\r
- }\r
- \r
- if( ok )\r
- break;\r
- }\r
- \r
- if( ok )\r
- break;\r
- }\r
- }\r
- \r
- /* try normal placement algorithm */\r
- if( ok == qfalse )\r
- {\r
- /* reset origin */\r
- x = 0;\r
- y = 0;\r
- \r
- /* walk the list of lightmap pages */\r
- for( i = 0; i < numOutLightmaps; i++ )\r
- {\r
- /* get the output lightmap */\r
- olm = &outLightmaps[ i ];\r
- \r
- /* simple early out test */\r
- if( olm->freeLuxels < lm->used )\r
- continue;\r
- \r
- /* don't store non-custom raw lightmaps on custom bsp lightmaps */\r
- if( olm->customWidth != lm->customWidth ||\r
- olm->customHeight != lm->customHeight )\r
- continue;\r
- \r
- /* set maxs */\r
- xMax = (olm->customWidth - lm->w) + 1;\r
- yMax = (olm->customHeight - lm->h) + 1;\r
- \r
- /* walk the origin around the lightmap */\r
- for( y = 0; y < yMax; y++ )\r
- {\r
- for( x = 0; x < xMax; x++ )\r
- {\r
- /* find a fine tract of lauhnd */\r
- ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );\r
- \r
- if( ok )\r
- break;\r
- }\r
- \r
- if( ok )\r
- break;\r
- }\r
- \r
- if( ok )\r
- break;\r
- \r
- /* reset x and y */\r
- x = 0;\r
- y = 0;\r
- }\r
- }\r
- \r
- /* no match? */\r
- if( ok == qfalse )\r
- {\r
- /* allocate two new output lightmaps */\r
- numOutLightmaps += 2;\r
- olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );\r
- if( outLightmaps != NULL && numOutLightmaps > 2 )\r
- {\r
- memcpy( olm, outLightmaps, (numOutLightmaps - 2) * sizeof( outLightmap_t ) );\r
- free( outLightmaps );\r
- }\r
- outLightmaps = olm;\r
- \r
- /* initialize both out lightmaps */\r
- SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 2 ] );\r
- SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 1 ] );\r
- \r
- /* set out lightmap */\r
- i = numOutLightmaps - 2;\r
- olm = &outLightmaps[ i ];\r
- \r
- /* set stamp xy origin to the first surface lightmap */\r
- if( lightmapNum > 0 )\r
- {\r
- x = lm->lightmapX[ 0 ];\r
- y = lm->lightmapY[ 0 ];\r
- }\r
- }\r
- \r
- /* if this is a style-using lightmap, it must be exported */\r
- if( lightmapNum > 0 )\r
- olm->extLightmapNum = 0;\r
- \r
- /* add the surface lightmap to the bsp lightmap */\r
- lm->outLightmapNums[ lightmapNum ] = i;\r
- lm->lightmapX[ lightmapNum ] = x;\r
- lm->lightmapY[ lightmapNum ] = y;\r
- olm->numLightmaps++;\r
- \r
- /* add shaders */\r
- for( i = 0; i < lm->numLightSurfaces; i++ )\r
- {\r
- /* get surface info */\r
- info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];\r
- \r
- /* test for shader */\r
- for( j = 0; j < olm->numShaders; j++ )\r
- {\r
- if( olm->shaders[ j ] == info->si )\r
- break;\r
- }\r
- \r
- /* if it doesn't exist, add it */\r
- if( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS )\r
- {\r
- olm->shaders[ olm->numShaders ] = info->si;\r
- olm->numShaders++;\r
- numLightmapShaders++;\r
- }\r
- }\r
- \r
- /* mark the bits used */\r
- for( y = 0; y < lm->h; y++ )\r
- {\r
- for( x = 0; x < lm->w; x++ )\r
- {\r
- /* get luxel */\r
- luxel = BSP_LUXEL( lightmapNum, x, y );\r
- deluxel = BSP_DELUXEL( x, y );\r
- if( luxel[ 0 ] < 0.0f )\r
- continue;\r
- \r
- /* set minimum light */\r
- VectorCopy( luxel, color );\r
-\r
- /* styles are not affected by minlight */\r
- if( lightmapNum == 0 )\r
- {\r
- for( i = 0; i < 3; i++ )\r
- {\r
- if( color[ i ] < minLight[ i ] )\r
- color[ i ] = minLight[ i ];\r
- }\r
- }\r
- \r
- /* get bsp lightmap coords */\r
- ox = x + lm->lightmapX[ lightmapNum ];\r
- oy = y + lm->lightmapY[ lightmapNum ];\r
- offset = (oy * olm->customWidth) + ox;\r
- \r
- /* flag pixel as used */\r
- olm->lightBits[ offset >> 3 ] |= (1 << (offset & 7));\r
- olm->freeLuxels--;\r
- \r
- /* store color */\r
- pixel = olm->bspLightBytes + (((oy * olm->customWidth) + ox) * 3);\r
- ColorToBytes( color, pixel, lm->gamma );\r
- \r
- /* store direction */\r
- if( deluxemap )\r
- {\r
- /* normalize average light direction */\r
- if( VectorNormalize( deluxel, direction ) )\r
- {\r
- /* encode [-1,1] in [0,255] */\r
- pixel = olm->bspDirBytes + (((oy * olm->customWidth) + ox) * 3);\r
- for( i = 0; i < 3; i++ )\r
- {\r
- temp = (direction[ i ] + 1.0f) * 127.5f;\r
- if( temp < 0 )\r
- pixel[ i ] = 0;\r
- else if( temp > 255 )\r
- pixel[ i ] = 255;\r
- else\r
- pixel[ i ] = temp;\r
- }\r
- }\r
- }\r
- }\r
- }\r
- }\r
-}\r
-\r
-\r
-\r
-/*\r
-CompareRawLightmap()\r
-compare function for qsort()\r
-*/\r
-\r
-static int CompareRawLightmap( const void *a, const void *b )\r
-{\r
- rawLightmap_t *alm, *blm;\r
- surfaceInfo_t *aInfo, *bInfo;\r
- int i, min, diff;\r
- \r
- \r
- /* get lightmaps */\r
- alm = &rawLightmaps[ *((int*) a) ];\r
- blm = &rawLightmaps[ *((int*) b) ];\r
- \r
- /* get min number of surfaces */\r
- min = (alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces);\r
- \r
- /* iterate */\r
- for( i = 0; i < min; i++ )\r
- {\r
- /* get surface info */\r
- aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];\r
- bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];\r
- \r
- /* compare shader names */\r
- diff = strcmp( aInfo->si->shader, bInfo->si->shader );\r
- if( diff != 0 )\r
- return diff;\r
- }\r
-\r
- /* test style count */\r
- diff = 0;\r
- for( i = 0; i < MAX_LIGHTMAPS; i++ )\r
- diff += blm->styles[ i ] - alm->styles[ i ];\r
- if( diff )\r
- return diff;\r
- \r
- /* compare size */\r
- diff = (blm->w * blm->h) - (alm->w * alm->h);\r
- if( diff != 0 )\r
- return diff;\r
- \r
- /* must be equivalent */\r
- return 0;\r
-}\r
-\r
-\r
-\r
-/*\r
-StoreSurfaceLightmaps()\r
-stores the surface lightmaps into the bsp as byte rgb triplets\r
-*/\r
-\r
-void StoreSurfaceLightmaps( void )\r
-{\r
- int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;\r
- int style, size, lightmapNum, lightmapNum2;\r
- float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;\r
- vec3_t sample, occludedSample, dirSample;\r
- float *deluxel, *bspDeluxel, *bspDeluxel2;\r
- byte *lb;\r
- int numUsed, numTwins, numTwinLuxels, numStored;\r
- float lmx, lmy, efficiency;\r
- vec3_t color;\r
- bspDrawSurface_t *ds, *parent, dsTemp;\r
- surfaceInfo_t *info;\r
- rawLightmap_t *lm, *lm2;\r
- outLightmap_t *olm;\r
- bspDrawVert_t *dv, *ydv, *dvParent;\r
- char dirname[ 1024 ], filename[ 1024 ];\r
- shaderInfo_t *csi;\r
- char lightmapName[ 128 ];\r
- char *rgbGenValues[ 256 ];\r
- char *alphaGenValues[ 256 ];\r
- \r
- \r
- /* note it */\r
- Sys_Printf( "--- StoreSurfaceLightmaps ---\n");\r
- \r
- /* setup */\r
- strcpy( dirname, source );\r
- StripExtension( dirname );\r
- memset( rgbGenValues, 0, sizeof( rgbGenValues ) );\r
- memset( alphaGenValues, 0, sizeof( alphaGenValues ) );\r
- \r
- /* -----------------------------------------------------------------\r
- average the sampled luxels into the bsp luxels\r
- ----------------------------------------------------------------- */\r
- \r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "Subsampling..." );\r
- \r
- /* walk the list of raw lightmaps */\r
- numUsed = 0;\r
- numTwins = 0;\r
- numTwinLuxels = 0;\r
- for( i = 0; i < numRawLightmaps; i++ )\r
- {\r
- /* get lightmap */\r
- lm = &rawLightmaps[ i ];\r
- \r
- /* walk individual lightmaps */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- /* early outs */\r
- if( lm->superLuxels[ lightmapNum ] == NULL )\r
- continue;\r
- \r
- /* allocate bsp luxel storage */\r
- if( lm->bspLuxels[ lightmapNum ] == NULL )\r
- {\r
- size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );\r
- lm->bspLuxels[ lightmapNum ] = safe_malloc( size );\r
- memset( lm->bspLuxels[ lightmapNum ], 0, size );\r
- }\r
-\r
- /* allocate radiosity lightmap storage */\r
- if( bounce )\r
- {\r
- size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );\r
- if( lm->radLuxels[ lightmapNum ] == NULL )\r
- lm->radLuxels[ lightmapNum ] = safe_malloc( size );\r
- memset( lm->radLuxels[ lightmapNum ], 0, size );\r
- }\r
- \r
- /* average supersampled luxels */\r
- for( y = 0; y < lm->h; y++ )\r
- {\r
- for( x = 0; x < lm->w; x++ )\r
- {\r
- /* subsample */\r
- samples = 0.0f;\r
- occludedSamples = 0.0f;\r
- mappedSamples = 0;\r
- VectorClear( sample );\r
- VectorClear( occludedSample );\r
- VectorClear( dirSample );\r
- for( ly = 0; ly < superSample; ly++ )\r
- {\r
- for( lx = 0; lx < superSample; lx++ )\r
- {\r
- /* sample luxel */\r
- sx = x * superSample + lx;\r
- sy = y * superSample + ly;\r
- luxel = SUPER_LUXEL( lightmapNum, sx, sy );\r
- deluxel = SUPER_DELUXEL( sx, sy );\r
- normal = SUPER_NORMAL( sx, sy );\r
- cluster = SUPER_CLUSTER( sx, sy );\r
- \r
- /* sample deluxemap */\r
- if( deluxemap && lightmapNum == 0 )\r
- VectorAdd( dirSample, deluxel, dirSample );\r
- \r
- /* keep track of used/occluded samples */\r
- if( *cluster != CLUSTER_UNMAPPED )\r
- mappedSamples++;\r
- \r
- /* handle lightmap border? */\r
- if( lightmapBorder && (sx == 0 || sx == (lm->sw - 1) || sy == 0 || sy == (lm->sh - 1) ) && luxel[ 3 ] > 0.0f )\r
- {\r
- VectorSet( sample, 255.0f, 0.0f, 0.0f );\r
- samples += 1.0f;\r
- }\r
- \r
- /* handle debug */\r
- else if( debug && *cluster < 0 )\r
- {\r
- if( *cluster == CLUSTER_UNMAPPED )\r
- VectorSet( luxel, 255, 204, 0 );\r
- else if( *cluster == CLUSTER_OCCLUDED )\r
- VectorSet( luxel, 255, 0, 255 );\r
- else if( *cluster == CLUSTER_FLOODED )\r
- VectorSet( luxel, 0, 32, 255 );\r
- VectorAdd( occludedSample, luxel, occludedSample );\r
- occludedSamples += 1.0f;\r
- }\r
- \r
- /* normal luxel handling */\r
- else if( luxel[ 3 ] > 0.0f )\r
- {\r
- /* handle lit or flooded luxels */\r
- if( *cluster > 0 || *cluster == CLUSTER_FLOODED )\r
- {\r
- VectorAdd( sample, luxel, sample );\r
- samples += luxel[ 3 ];\r
- }\r
- \r
- /* handle occluded or unmapped luxels */\r
- else\r
- {\r
- VectorAdd( occludedSample, luxel, occludedSample );\r
- occludedSamples += luxel[ 3 ];\r
- }\r
- \r
- /* handle style debugging */\r
- if( debug && lightmapNum > 0 && x < 2 && y < 2 )\r
- {\r
- VectorCopy( debugColors[ 0 ], sample );\r
- samples = 1;\r
- }\r
- }\r
- }\r
- }\r
- \r
- /* only use occluded samples if necessary */\r
- if( samples <= 0.0f )\r
- {\r
- VectorCopy( occludedSample, sample );\r
- samples = occludedSamples;\r
- }\r
- \r
- /* get luxels */\r
- luxel = SUPER_LUXEL( lightmapNum, x, y );\r
- deluxel = SUPER_DELUXEL( x, y );\r
- \r
- /* store light direction */\r
- if( deluxemap && lightmapNum == 0 )\r
- VectorCopy( dirSample, deluxel );\r
- \r
- /* store the sample back in super luxels */\r
- if( samples > 0.01f )\r
- {\r
- VectorScale( sample, (1.0f / samples), luxel );\r
- luxel[ 3 ] = 1.0f;\r
- }\r
- \r
- /* if any samples were mapped in any way, store ambient color */\r
- else if( mappedSamples > 0 )\r
- {\r
- if( lightmapNum == 0 )\r
- VectorCopy( ambientColor, luxel );\r
- else\r
- VectorClear( luxel );\r
- luxel[ 3 ] = 1.0f;\r
- }\r
- \r
- /* store a bogus value to be fixed later */ \r
- else\r
- {\r
- VectorClear( luxel );\r
- luxel[ 3 ] = -1.0f;\r
- }\r
- }\r
- }\r
- \r
- /* clean up and store into bsp luxels */\r
- lm->used = 0;\r
- for( y = 0; y < lm->h; y++ )\r
- {\r
- for( x = 0; x < lm->w; x++ )\r
- {\r
- /* get luxels */\r
- luxel = SUPER_LUXEL( lightmapNum, x, y );\r
- deluxel = SUPER_DELUXEL( x, y );\r
- \r
- /* copy light direction */\r
- if( deluxemap && lightmapNum == 0 )\r
- VectorCopy( deluxel, dirSample );\r
- \r
- /* is this a valid sample? */\r
- if( luxel[ 3 ] > 0.0f )\r
- {\r
- VectorCopy( luxel, sample );\r
- samples = luxel[ 3 ];\r
- numUsed++;\r
- lm->used++;\r
- \r
- /* fix negative samples */\r
- for( j = 0; j < 3; j++ )\r
- {\r
- if( sample[ j ] < 0.0f )\r
- sample[ j ] = 0.0f;\r
- }\r
- }\r
- else\r
- {\r
- /* nick an average value from the neighbors */\r
- VectorClear( sample );\r
- VectorClear( dirSample );\r
- samples = 0.0f;\r
- \r
- /* fixme: why is this disabled?? */\r
- for( sy = (y - 1); sy <= (y + 1); sy++ )\r
- {\r
- if( sy < 0 || sy >= lm->h )\r
- continue;\r
- \r
- for( sx = (x - 1); sx <= (x + 1); sx++ )\r
- {\r
- if( sx < 0 || sx >= lm->w || (sx == x && sy == y) )\r
- continue;\r
- \r
- /* get neighbor's particulars */\r
- luxel = SUPER_LUXEL( lightmapNum, sx, sy );\r
- if( luxel[ 3 ] < 0.0f )\r
- continue;\r
- VectorAdd( sample, luxel, sample );\r
- samples += luxel[ 3 ];\r
- }\r
- }\r
- \r
- /* no samples? */\r
- if( samples == 0.0f )\r
- {\r
- VectorSet( sample, -1.0f, -1.0f, -1.0f );\r
- samples = 1.0f;\r
- }\r
- else\r
- {\r
- numUsed++;\r
- lm->used++;\r
- \r
- /* fix negative samples */\r
- for( j = 0; j < 3; j++ )\r
- {\r
- if( sample[ j ] < 0.0f )\r
- sample[ j ] = 0.0f;\r
- }\r
- }\r
- }\r
- \r
- /* scale the sample */\r
- VectorScale( sample, (1.0f / samples), sample );\r
- \r
- /* store the sample in the radiosity luxels */\r
- if( bounce > 0 )\r
- {\r
- radLuxel = RAD_LUXEL( lightmapNum, x, y );\r
- VectorCopy( sample, radLuxel );\r
- \r
- /* if only storing bounced light, early out here */\r
- if( bounceOnly && !bouncing )\r
- continue;\r
- }\r
- \r
- /* store the sample in the bsp luxels */\r
- bspLuxel = BSP_LUXEL( lightmapNum, x, y );\r
- bspDeluxel = BSP_DELUXEL( x, y );\r
- \r
- VectorAdd( bspLuxel, sample, bspLuxel );\r
- if( deluxemap && lightmapNum == 0 )\r
- VectorAdd( bspDeluxel, dirSample, bspDeluxel );\r
- }\r
- }\r
- \r
- /* wrap bsp luxels if necessary */\r
- if( lm->wrap[ 0 ] )\r
- {\r
- for( y = 0; y < lm->h; y++ )\r
- {\r
- bspLuxel = BSP_LUXEL( lightmapNum, 0, y );\r
- bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );\r
- VectorAdd( bspLuxel, bspLuxel2, bspLuxel );\r
- VectorScale( bspLuxel, 0.5f, bspLuxel );\r
- VectorCopy( bspLuxel, bspLuxel2 );\r
- if( deluxemap && lightmapNum == 0 )\r
- {\r
- bspDeluxel = BSP_DELUXEL( 0, y );\r
- bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );\r
- VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );\r
- VectorScale( bspDeluxel, 0.5f, bspDeluxel );\r
- VectorCopy( bspDeluxel, bspDeluxel2 );\r
- }\r
- }\r
- }\r
- if( lm->wrap[ 1 ] )\r
- {\r
- for( x = 0; x < lm->w; x++ )\r
- {\r
- bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );\r
- bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );\r
- VectorAdd( bspLuxel, bspLuxel2, bspLuxel );\r
- VectorScale( bspLuxel, 0.5f, bspLuxel );\r
- VectorCopy( bspLuxel, bspLuxel2 );\r
- if( deluxemap && lightmapNum == 0 )\r
- {\r
- bspDeluxel = BSP_DELUXEL( x, 0 );\r
- bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );\r
- VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );\r
- VectorScale( bspDeluxel, 0.5f, bspDeluxel );\r
- VectorCopy( bspDeluxel, bspDeluxel2 );\r
- }\r
- }\r
- }\r
- }\r
- }\r
- \r
- /* -----------------------------------------------------------------\r
- collapse non-unique lightmaps\r
- ----------------------------------------------------------------- */\r
- \r
- if( noCollapse == qfalse && deluxemap == qfalse )\r
- {\r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "collapsing..." );\r
- \r
- /* set all twin refs to null */\r
- for( i = 0; i < numRawLightmaps; i++ )\r
- {\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- rawLightmaps[ i ].twins[ lightmapNum ] = NULL;\r
- rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;\r
- rawLightmaps[ i ].numStyledTwins = 0;\r
- }\r
- }\r
- \r
- /* walk the list of raw lightmaps */\r
- for( i = 0; i < numRawLightmaps; i++ )\r
- {\r
- /* get lightmap */\r
- lm = &rawLightmaps[ i ];\r
- \r
- /* walk lightmaps */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- /* early outs */\r
- if( lm->bspLuxels[ lightmapNum ] == NULL ||\r
- lm->twins[ lightmapNum ] != NULL )\r
- continue;\r
- \r
- /* find all lightmaps that are virtually identical to this one */\r
- for( j = i + 1; j < numRawLightmaps; j++ )\r
- {\r
- /* get lightmap */\r
- lm2 = &rawLightmaps[ j ];\r
- \r
- /* walk lightmaps */\r
- for( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )\r
- {\r
- /* early outs */\r
- if( lm2->bspLuxels[ lightmapNum2 ] == NULL ||\r
- lm2->twins[ lightmapNum2 ] != NULL )\r
- continue;\r
- \r
- /* compare them */\r
- if( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) )\r
- {\r
- /* merge and set twin */\r
- MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 );\r
- lm2->twins[ lightmapNum2 ] = lm;\r
- lm2->twinNums[ lightmapNum2 ] = lightmapNum;\r
- numTwins++;\r
- numTwinLuxels += (lm->w * lm->h);\r
- \r
- /* count styled twins */\r
- if( lightmapNum > 0 )\r
- lm->numStyledTwins++;\r
- }\r
- }\r
- }\r
- }\r
- }\r
- }\r
- \r
- /* -----------------------------------------------------------------\r
- sort raw lightmaps by shader\r
- ----------------------------------------------------------------- */\r
- \r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "sorting..." );\r
- \r
- /* allocate a new sorted list */\r
- if( sortLightmaps == NULL )\r
- sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );\r
- \r
- /* fill it out and sort it */\r
- for( i = 0; i < numRawLightmaps; i++ )\r
- sortLightmaps[ i ] = i;\r
- qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );\r
- \r
- /* -----------------------------------------------------------------\r
- allocate output lightmaps\r
- ----------------------------------------------------------------- */\r
- \r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "allocating..." );\r
- \r
- /* kill all existing output lightmaps */\r
- if( outLightmaps != NULL )\r
- {\r
- for( i = 0; i < numOutLightmaps; i++ )\r
- {\r
- free( outLightmaps[ i ].lightBits );\r
- free( outLightmaps[ i ].bspLightBytes );\r
- }\r
- free( outLightmaps );\r
- outLightmaps = NULL;\r
- }\r
- \r
- numLightmapShaders = 0;\r
- numOutLightmaps = 0;\r
- numBSPLightmaps = 0;\r
- numExtLightmaps = 0;\r
- \r
- /* find output lightmap */\r
- for( i = 0; i < numRawLightmaps; i++ )\r
- {\r
- lm = &rawLightmaps[ sortLightmaps[ i ] ];\r
- FindOutLightmaps( lm );\r
- }\r
- \r
- /* set output numbers in twinned lightmaps */\r
- for( i = 0; i < numRawLightmaps; i++ )\r
- {\r
- /* get lightmap */\r
- lm = &rawLightmaps[ sortLightmaps[ i ] ];\r
- \r
- /* walk lightmaps */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- /* get twin */\r
- lm2 = lm->twins[ lightmapNum ];\r
- if( lm2 == NULL )\r
- continue;\r
- lightmapNum2 = lm->twinNums[ lightmapNum ];\r
- \r
- /* find output lightmap from twin */\r
- lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];\r
- lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];\r
- lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];\r
- }\r
- }\r
- \r
- /* -----------------------------------------------------------------\r
- store output lightmaps\r
- ----------------------------------------------------------------- */\r
- \r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "storing..." );\r
- \r
- /* count the bsp lightmaps and allocate space */\r
- if( bspLightBytes != NULL )\r
- free( bspLightBytes );\r
- if( numBSPLightmaps == 0 || externalLightmaps )\r
- {\r
- numBSPLightBytes = 0;\r
- bspLightBytes = NULL;\r
- }\r
- else\r
- {\r
- numBSPLightBytes = (numBSPLightmaps * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3);\r
- bspLightBytes = safe_malloc( numBSPLightBytes );\r
- memset( bspLightBytes, 0, numBSPLightBytes );\r
- }\r
- \r
- /* walk the list of output lightmaps */\r
- for( i = 0; i < numOutLightmaps; i++ )\r
- {\r
- /* get output lightmap */\r
- olm = &outLightmaps[ i ];\r
- \r
- /* is this a valid bsp lightmap? */\r
- if( olm->lightmapNum >= 0 && !externalLightmaps )\r
- {\r
- /* copy lighting data */\r
- lb = bspLightBytes + (olm->lightmapNum * LIGHTMAP_HEIGHT * LIGHTMAP_WIDTH * 3);\r
- memcpy( lb, olm->bspLightBytes, LIGHTMAP_HEIGHT * LIGHTMAP_WIDTH * 3 );\r
- \r
- /* copy direction data */\r
- if( deluxemap )\r
- {\r
- lb = bspLightBytes + ((olm->lightmapNum + 1) * LIGHTMAP_HEIGHT * LIGHTMAP_WIDTH * 3);\r
- memcpy( lb, olm->bspDirBytes, LIGHTMAP_HEIGHT * LIGHTMAP_WIDTH * 3 );\r
- }\r
- }\r
- \r
- /* external lightmap? */\r
- if( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps )\r
- {\r
- /* make a directory for the lightmaps */\r
- Q_mkdir( dirname );\r
- \r
- /* set external lightmap number */\r
- olm->extLightmapNum = numExtLightmaps;\r
- \r
- /* write lightmap */\r
- sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );\r
- Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );\r
- WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );\r
- numExtLightmaps++;\r
- \r
- /* write deluxemap */\r
- if( deluxemap )\r
- {\r
- sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );\r
- Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );\r
- WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );\r
- numExtLightmaps++;\r
- \r
- if( debugDeluxemap )\r
- olm->extLightmapNum++;\r
- }\r
- }\r
- }\r
- \r
- if( numExtLightmaps > 0 )\r
- Sys_FPrintf( SYS_VRB, "\n" );\r
- \r
- /* delete unused external lightmaps */\r
- for( i = numExtLightmaps; i; i++ )\r
- {\r
- /* determine if file exists */\r
- sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );\r
- if( !FileExists( filename ) )\r
- break;\r
- \r
- /* delete it */\r
- remove( filename );\r
- }\r
- \r
- /* -----------------------------------------------------------------\r
- project the lightmaps onto the bsp surfaces\r
- ----------------------------------------------------------------- */\r
- \r
- /* note it */\r
- Sys_FPrintf( SYS_VRB, "projecting..." );\r
- \r
- /* walk the list of surfaces */\r
- for( i = 0; i < numBSPDrawSurfaces; i++ )\r
- {\r
- /* get the surface and info */\r
- ds = &bspDrawSurfaces[ i ];\r
- info = &surfaceInfos[ i ];\r
- lm = info->lm;\r
- olm = NULL;\r
- \r
- /* handle surfaces with identical parent */\r
- if( info->parentSurfaceNum >= 0 )\r
- {\r
- /* preserve original data and get parent */\r
- parent = &bspDrawSurfaces[ info->parentSurfaceNum ];\r
- memcpy( &dsTemp, ds, sizeof( *ds ) );\r
- \r
- /* overwrite child with parent data */\r
- memcpy( ds, parent, sizeof( *ds ) );\r
- \r
- /* restore key parts */\r
- ds->fogNum = dsTemp.fogNum;\r
- ds->firstVert = dsTemp.firstVert;\r
- ds->firstIndex = dsTemp.firstIndex;\r
- memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );\r
- \r
- /* set vertex data */\r
- dv = &bspDrawVerts[ ds->firstVert ];\r
- dvParent = &bspDrawVerts[ parent->firstVert ];\r
- for( j = 0; j < ds->numVerts; j++ )\r
- {\r
- memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );\r
- memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );\r
- }\r
- \r
- /* skip the rest */\r
- continue;\r
- }\r
- \r
- /* handle vertex lit or approximated surfaces */\r
- else if( lm == NULL || lm->outLightmapNums[ 0 ] < 0 )\r
- {\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- ds->lightmapNum[ lightmapNum ] = -3;\r
- ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];\r
- }\r
- }\r
- \r
- /* handle lightmapped surfaces */\r
- else\r
- {\r
- /* walk lightmaps */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- /* set style */\r
- ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];\r
- \r
- /* handle unused style */\r
- if( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 )\r
- {\r
- ds->lightmapNum[ lightmapNum ] = -3;\r
- continue;\r
- }\r
- \r
- /* get output lightmap */\r
- olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];\r
- \r
- /* set bsp lightmap number */\r
- ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;\r
- \r
- /* deluxemap debugging makes the deluxemap visible */\r
- if( deluxemap && debugDeluxemap && lightmapNum == 0 )\r
- ds->lightmapNum[ lightmapNum ]++;\r
- \r
- /* calc lightmap origin in texture space */\r
- lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;\r
- lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;\r
- \r
- /* calc lightmap st coords and store lighting values */\r
- dv = &bspDrawVerts[ ds->firstVert ];\r
- ydv = &yDrawVerts[ ds->firstVert ];\r
- for( j = 0; j < ds->numVerts; j++ )\r
- {\r
- dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + (ydv[ j ].lightmap[ 0 ][ 0 ] / (superSample * olm->customWidth));\r
- dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + (ydv[ j ].lightmap[ 0 ][ 1 ] / (superSample * olm->customHeight));\r
- }\r
- }\r
- }\r
- \r
- /* store vertex colors */\r
- dv = &bspDrawVerts[ ds->firstVert ];\r
- for( j = 0; j < ds->numVerts; j++ )\r
- {\r
- /* walk lightmaps */\r
- for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- /* handle unused style */\r
- if( ds->vertexStyles[ lightmapNum ] == LS_NONE )\r
- VectorClear( color );\r
- else\r
- {\r
- /* get vertex color */\r
- luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );\r
- VectorCopy( luxel, color );\r
- \r
- /* set minimum light */\r
- if( lightmapNum == 0 )\r
- {\r
- for( k = 0; k < 3; k++ )\r
- if( color[ k ] < minVertexLight[ k ] )\r
- color[ k ] = minVertexLight[ k ];\r
- }\r
- }\r
- \r
- /* store to bytes */\r
- ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );\r
- }\r
- }\r
- \r
- /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */\r
- if( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) //% info->si->styleMarker > 0 )\r
- {\r
- qboolean dfEqual;\r
- char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];\r
- \r
- \r
- /* setup */\r
- sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );\r
- dv = &bspDrawVerts[ ds->firstVert ];\r
- \r
- /* depthFunc equal? */\r
- if( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED )\r
- dfEqual = qtrue;\r
- else\r
- dfEqual = qfalse;\r
- \r
- /* generate stages for styled lightmaps */\r
- for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
- {\r
- /* early out */\r
- style = lm->styles[ lightmapNum ];\r
- if( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 )\r
- continue;\r
- \r
- /* get output lightmap */\r
- olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];\r
- \r
- /* lightmap name */\r
- if( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] )\r
- strcpy( lightmapName, "$lightmap" );\r
- else\r
- sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );\r
- \r
- /* get rgbgen string */\r
- if( rgbGenValues[ style ] == NULL )\r
- {\r
- sprintf( key, "_style%drgbgen", style );\r
- rgbGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );\r
- if( rgbGenValues[ style ][ 0 ] == '\0' )\r
- rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";\r
- }\r
- rgbGen[ 0 ] = '\0';\r
- if( rgbGenValues[ style ][ 0 ] != '\0' )\r
- sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );\r
- else\r
- rgbGen[ 0 ] = '\0';\r
- \r
- /* get alphagen string */\r
- if( alphaGenValues[ style ] == NULL )\r
- {\r
- sprintf( key, "_style%dalphagen", style );\r
- alphaGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );\r
- }\r
- if( alphaGenValues[ style ][ 0 ] != '\0' )\r
- sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );\r
- else\r
- alphaGen[ 0 ] = '\0';\r
- \r
- /* calculate st offset */\r
- lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];\r
- lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];\r
- \r
- /* create additional stage */\r
- if( lmx == 0.0f && lmy == 0.0f )\r
- {\r
- sprintf( styleStage, "\t{\n"\r
- "\t\tmap %s\n" /* lightmap */\r
- "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"\r
- "%s" /* depthFunc equal */\r
- "%s" /* rgbGen */\r
- "%s" /* alphaGen */\r
- "\t\ttcGen lightmap\n"\r
- "\t}\n",\r
- lightmapName,\r
- (dfEqual ? "\t\tdepthFunc equal\n" : ""),\r
- rgbGen,\r
- alphaGen );\r
- }\r
- else\r
- {\r
- sprintf( styleStage, "\t{\n"\r
- "\t\tmap %s\n" /* lightmap */\r
- "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"\r
- "%s" /* depthFunc equal */\r
- "%s" /* rgbGen */\r
- "%s" /* alphaGen */\r
- "\t\ttcGen lightmap\n"\r
- "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */\r
- "\t}\n",\r
- lightmapName,\r
- (dfEqual ? "\t\tdepthFunc equal\n" : ""),\r
- rgbGen,\r
- alphaGen,\r
- lmx, lmy );\r
- \r
- }\r
- \r
- /* concatenate */\r
- strcat( styleStages, styleStage );\r
- }\r
- \r
- /* create custom shader */\r
- if( info->si->styleMarker == 2 )\r
- csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );\r
- else\r
- csi = CustomShader( info->si, "q3map_styleMarker", styleStages );\r
- \r
- /* emit remap command */\r
- //% EmitVertexRemapShader( csi->shader, info->si->shader );\r
- \r
- /* store it */\r
- //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );\r
- ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );\r
- //% Sys_Printf( ")\n" );\r
- }\r
- \r
- /* devise a custom shader for this surface (fixme: make this work with light styles) */\r
- else if( olm != NULL && lm != NULL && !externalLightmaps &&\r
- (olm->customWidth != LIGHTMAP_WIDTH || olm->customHeight != LIGHTMAP_HEIGHT) )\r
- {\r
- /* get output lightmap */\r
- olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];\r
- \r
- /* do some name mangling */\r
- sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );\r
- \r
- /* create custom shader */\r
- csi = CustomShader( info->si, "$lightmap", lightmapName );\r
- \r
- /* store it */\r
- //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );\r
- ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );\r
- //% Sys_Printf( ")\n" );\r
- }\r
- \r
- /* use the normal plain-jane shader */\r
- else\r
- ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );\r
- }\r
- \r
- /* finish */\r
- Sys_FPrintf( SYS_VRB, "done.\n" );\r
- \r
- /* calc num stored */\r
- numStored = numBSPLightBytes / 3;\r
- efficiency = (numStored <= 0)\r
- ? 0\r
- : (float) numUsed / (float) numStored;\r
- \r
- /* print stats */\r
- Sys_Printf( "%9d luxels used\n", numUsed );\r
- Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );\r
- Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );\r
- Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );\r
- Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );\r
- Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );\r
- Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );\r
- Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );\r
- \r
- /* write map shader file */\r
- WriteMapShaderFile();\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 LIGHTMAPS_YDNAR_C
+
+
+
+/* dependencies */
+#include "q3map2.h"
+#include <glib.h>
+
+
+
+/* -------------------------------------------------------------------------------
+
+ this file contains code that doe lightmap allocation and projection that
+ runs in the -light phase.
+
+ this is handled here rather than in the bsp phase for a few reasons--
+ surfaces are no longer necessarily convex polygons, patches may or may not be
+ planar or have lightmaps projected directly onto control points.
+
+ also, this allows lightmaps to be calculated before being allocated and stored
+ in the bsp. lightmaps that have little high-frequency information are candidates
+ for having their resolutions scaled down.
+
+ ------------------------------------------------------------------------------- */
+
+/*
+ WriteTGA24()
+ based on WriteTGA() from imagelib.c
+ */
+
+void WriteTGA24( char *filename, byte *data, int width, int height, qboolean flip ){
+ int i, c;
+ byte *buffer, *in;
+ FILE *file;
+
+
+ /* allocate a buffer and set it up */
+ buffer = safe_malloc( width * height * 3 + 18 );
+ /* we may also use safe_malloc0 on the whole instead,
+ * this would just be a bit slower */
+ memset( buffer, 0, 18 );
+ buffer[ 2 ] = 2;
+ buffer[ 12 ] = width & 255;
+ buffer[ 13 ] = width >> 8;
+ buffer[ 14 ] = height & 255;
+ buffer[ 15 ] = height >> 8;
+ buffer[ 16 ] = 24;
+
+ /* swap rgb to bgr */
+ c = ( width * height * 3 ) + 18;
+ for ( i = 18; i < c; i += 3 )
+ {
+ buffer[ i ] = data[ i - 18 + 2 ]; /* blue */
+ buffer[ i + 1 ] = data[ i - 18 + 1 ]; /* green */
+ buffer[ i + 2 ] = data[ i - 18 + 0 ]; /* red */
+ }
+
+ /* write it and free the buffer */
+ file = fopen( filename, "wb" );
+ if ( file == NULL ) {
+ Error( "Unable to open %s for writing", filename );
+ }
+
+ /* flip vertically? */
+ if ( flip ) {
+ fwrite( buffer, 1, 18, file );
+ for ( in = buffer + ( ( height - 1 ) * width * 3 ) + 18; in >= buffer; in -= ( width * 3 ) )
+ fwrite( in, 1, ( width * 3 ), file );
+ }
+ else{
+ fwrite( buffer, 1, c, file );
+ }
+
+ /* close the file */
+ fclose( file );
+ free( buffer );
+}
+
+
+
+/*
+ ExportLightmaps()
+ exports the lightmaps as a list of numbered tga images
+ */
+
+void ExportLightmaps( void ){
+ int i;
+ char dirname[ 1024 ], filename[ 1024 ];
+ byte *lightmap;
+
+
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "--- ExportLightmaps ---\n" );
+
+ /* do some path mangling */
+ strcpy( dirname, source );
+ StripExtension( dirname );
+
+ /* sanity check */
+ if ( bspLightBytes == NULL ) {
+ Sys_FPrintf( SYS_WRN, "WARNING: No BSP lightmap data\n" );
+ return;
+ }
+
+ /* make a directory for the lightmaps */
+ Q_mkdir( dirname );
+
+ /* iterate through the lightmaps */
+ for ( i = 0, lightmap = bspLightBytes; lightmap < ( bspLightBytes + numBSPLightBytes ); i++, lightmap += ( game->lightmapSize * game->lightmapSize * 3 ) )
+ {
+ /* write a tga image out */
+ sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
+ Sys_Printf( "Writing %s\n", filename );
+ WriteTGA24( filename, lightmap, game->lightmapSize, game->lightmapSize, qfalse );
+ }
+}
+
+
+
+/*
+ ExportLightmapsMain()
+ exports the lightmaps as a list of numbered tga images
+ */
+
+int ExportLightmapsMain( int argc, char **argv ){
+ /* arg checking */
+ if ( argc < 2 ) {
+ Sys_Printf( "Usage: q3map -export [-v] <mapname>\n" );
+ return 0;
+ }
+
+ /* do some path mangling */
+ strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
+ StripExtension( source );
+ DefaultExtension( source, ".bsp" );
+
+ /* load the bsp */
+ Sys_Printf( "Loading %s\n", source );
+ LoadBSPFile( source );
+
+ /* export the lightmaps */
+ ExportLightmaps();
+
+ /* return to sender */
+ return 0;
+}
+
+
+
+/*
+ ImportLightmapsMain()
+ imports the lightmaps from a list of numbered tga images
+ */
+
+int ImportLightmapsMain( int argc, char **argv ){
+ int i, x, y, len, width, height;
+ char dirname[ 1024 ], filename[ 1024 ];
+ byte *lightmap, *buffer, *pixels, *in, *out;
+
+
+ /* arg checking */
+ if ( argc < 2 ) {
+ Sys_Printf( "Usage: q3map -import [-v] <mapname>\n" );
+ return 0;
+ }
+
+ /* do some path mangling */
+ strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
+ StripExtension( source );
+ DefaultExtension( source, ".bsp" );
+
+ /* load the bsp */
+ Sys_Printf( "Loading %s\n", source );
+ LoadBSPFile( source );
+
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "--- ImportLightmaps ---\n" );
+
+ /* do some path mangling */
+ strcpy( dirname, source );
+ StripExtension( dirname );
+
+ /* sanity check */
+ if ( bspLightBytes == NULL ) {
+ Error( "No lightmap data" );
+ }
+
+ /* make a directory for the lightmaps */
+ Q_mkdir( dirname );
+
+ /* iterate through the lightmaps */
+ for ( i = 0, lightmap = bspLightBytes; lightmap < ( bspLightBytes + numBSPLightBytes ); i++, lightmap += ( game->lightmapSize * game->lightmapSize * 3 ) )
+ {
+ /* read a tga image */
+ sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
+ Sys_Printf( "Loading %s\n", filename );
+ buffer = NULL;
+ len = vfsLoadFile( filename, (void*) &buffer, -1 );
+ if ( len < 0 ) {
+ Sys_FPrintf( SYS_WRN, "WARNING: Unable to load image %s\n", filename );
+ continue;
+ }
+
+ /* parse file into an image */
+ pixels = NULL;
+ LoadTGABuffer( buffer, buffer + len, &pixels, &width, &height );
+ free( buffer );
+
+ /* sanity check it */
+ if ( pixels == NULL ) {
+ Sys_FPrintf( SYS_WRN, "WARNING: Unable to load image %s\n", filename );
+ continue;
+ }
+ if ( width != game->lightmapSize || height != game->lightmapSize ) {
+ Sys_FPrintf( SYS_WRN, "WARNING: Image %s is not the right size (%d, %d) != (%d, %d)\n",
+ filename, width, height, game->lightmapSize, game->lightmapSize );
+ }
+
+ /* copy the pixels */
+ in = pixels;
+ for ( y = 1; y <= game->lightmapSize; y++ )
+ {
+ out = lightmap + ( ( game->lightmapSize - y ) * game->lightmapSize * 3 );
+ for ( x = 0; x < game->lightmapSize; x++, in += 4, out += 3 )
+ VectorCopy( in, out );
+ }
+
+ /* free the image */
+ free( pixels );
+ }
+
+ /* write the bsp */
+ Sys_Printf( "writing %s\n", source );
+ WriteBSPFile( source );
+
+ /* return to sender */
+ return 0;
+}
+
+
+
+/* -------------------------------------------------------------------------------
+
+ this section deals with projecting a lightmap onto a raw drawsurface
+
+ ------------------------------------------------------------------------------- */
+
+/*
+ CompareLightSurface()
+ compare function for qsort()
+ */
+
+static int CompareLightSurface( const void *a, const void *b ){
+ shaderInfo_t *asi, *bsi;
+
+
+ /* get shaders */
+ asi = surfaceInfos[ *( (const int*) a ) ].si;
+ bsi = surfaceInfos[ *( (const int*) b ) ].si;
+
+ /* dummy check */
+ if ( asi == NULL ) {
+ return -1;
+ }
+ if ( bsi == NULL ) {
+ return 1;
+ }
+
+ /* compare shader names */
+ return strcmp( asi->shader, bsi->shader );
+}
+
+
+
+/*
+ FinishRawLightmap()
+ allocates a raw lightmap's necessary buffers
+ */
+
+void FinishRawLightmap( rawLightmap_t *lm ){
+ int i, j, c, size, *sc;
+ float is;
+ surfaceInfo_t *info;
+
+
+ /* sort light surfaces by shader name */
+ qsort( &lightSurfaces[ lm->firstLightSurface ], lm->numLightSurfaces, sizeof( int ), CompareLightSurface );
+
+ /* count clusters */
+ lm->numLightClusters = 0;
+ for ( i = 0; i < lm->numLightSurfaces; i++ )
+ {
+ /* get surface info */
+ info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
+
+ /* add surface clusters */
+ lm->numLightClusters += info->numSurfaceClusters;
+ }
+
+ /* allocate buffer for clusters and copy */
+ lm->lightClusters = safe_malloc( lm->numLightClusters * sizeof( *lm->lightClusters ) );
+ c = 0;
+ for ( i = 0; i < lm->numLightSurfaces; i++ )
+ {
+ /* get surface info */
+ info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
+
+ /* add surface clusters */
+ for ( j = 0; j < info->numSurfaceClusters; j++ )
+ lm->lightClusters[ c++ ] = surfaceClusters[ info->firstSurfaceCluster + j ];
+ }
+
+ /* set styles */
+ lm->styles[ 0 ] = LS_NORMAL;
+ for ( i = 1; i < MAX_LIGHTMAPS; i++ )
+ lm->styles[ i ] = LS_NONE;
+
+ /* set supersampling size */
+ lm->sw = lm->w * superSample;
+ lm->sh = lm->h * superSample;
+
+ /* add to super luxel count */
+ numRawSuperLuxels += ( lm->sw * lm->sh );
+
+ /* manipulate origin/vecs for supersampling */
+ if ( superSample > 1 && lm->vecs != NULL ) {
+ /* calc inverse supersample */
+ is = 1.0f / superSample;
+
+ /* scale the vectors and shift the origin */
+ #if 1
+ /* new code that works for arbitrary supersampling values */
+ VectorMA( lm->origin, -0.5, lm->vecs[ 0 ], lm->origin );
+ VectorMA( lm->origin, -0.5, lm->vecs[ 1 ], lm->origin );
+ VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
+ VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
+ VectorMA( lm->origin, is, lm->vecs[ 0 ], lm->origin );
+ VectorMA( lm->origin, is, lm->vecs[ 1 ], lm->origin );
+ #else
+ /* old code that only worked with a value of 2 */
+ VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
+ VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
+ VectorMA( lm->origin, -is, lm->vecs[ 0 ], lm->origin );
+ VectorMA( lm->origin, -is, lm->vecs[ 1 ], lm->origin );
+ #endif
+ }
+
+ /* allocate bsp lightmap storage */
+ size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
+ if ( lm->bspLuxels[ 0 ] == NULL ) {
+ lm->bspLuxels[ 0 ] = safe_malloc( size );
+ }
+ memset( lm->bspLuxels[ 0 ], 0, size );
+
+ /* allocate radiosity lightmap storage */
+ if ( bounce ) {
+ size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
+ if ( lm->radLuxels[ 0 ] == NULL ) {
+ lm->radLuxels[ 0 ] = safe_malloc( size );
+ }
+ memset( lm->radLuxels[ 0 ], 0, size );
+ }
+
+ /* allocate sampling lightmap storage */
+ size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
+ if ( lm->superLuxels[ 0 ] == NULL ) {
+ lm->superLuxels[ 0 ] = safe_malloc( size );
+ }
+ memset( lm->superLuxels[ 0 ], 0, size );
+
+ /* allocate origin map storage */
+ size = lm->sw * lm->sh * SUPER_ORIGIN_SIZE * sizeof( float );
+ if ( lm->superOrigins == NULL ) {
+ lm->superOrigins = safe_malloc( size );
+ }
+ memset( lm->superOrigins, 0, size );
+
+ /* allocate normal map storage */
+ size = lm->sw * lm->sh * SUPER_NORMAL_SIZE * sizeof( float );
+ if ( lm->superNormals == NULL ) {
+ lm->superNormals = safe_malloc( size );
+ }
+ memset( lm->superNormals, 0, size );
+
+ /* allocate floodlight map storage */
+ size = lm->sw * lm->sh * SUPER_FLOODLIGHT_SIZE * sizeof( float );
+ if ( lm->superFloodLight == NULL ) {
+ lm->superFloodLight = safe_malloc( size );
+ }
+ memset( lm->superFloodLight, 0, size );
+
+ /* allocate cluster map storage */
+ size = lm->sw * lm->sh * sizeof( int );
+ if ( lm->superClusters == NULL ) {
+ lm->superClusters = safe_malloc( size );
+ }
+ size = lm->sw * lm->sh;
+ sc = lm->superClusters;
+ for ( i = 0; i < size; i++ )
+ ( *sc++ ) = CLUSTER_UNMAPPED;
+
+ /* deluxemap allocation */
+ if ( deluxemap ) {
+ /* allocate sampling deluxel storage */
+ size = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
+ if ( lm->superDeluxels == NULL ) {
+ lm->superDeluxels = safe_malloc( size );
+ }
+ memset( lm->superDeluxels, 0, size );
+
+ /* allocate bsp deluxel storage */
+ size = lm->w * lm->h * BSP_DELUXEL_SIZE * sizeof( float );
+ if ( lm->bspDeluxels == NULL ) {
+ lm->bspDeluxels = safe_malloc( size );
+ }
+ memset( lm->bspDeluxels, 0, size );
+ }
+
+ /* add to count */
+ numLuxels += ( lm->sw * lm->sh );
+}
+
+
+
+/*
+ AddPatchToRawLightmap()
+ projects a lightmap for a patch surface
+ since lightmap calculation for surfaces is now handled in a general way (light_ydnar.c),
+ it is no longer necessary for patch verts to fall exactly on a lightmap sample
+ based on AllocateLightmapForPatch()
+ */
+
+qboolean AddPatchToRawLightmap( int num, rawLightmap_t *lm ){
+ bspDrawSurface_t *ds;
+ surfaceInfo_t *info;
+ int x, y;
+ bspDrawVert_t *verts, *a, *b;
+ vec3_t delta;
+ mesh_t src, *subdivided, *mesh;
+ float sBasis, tBasis, s, t;
+ float length, widthTable[ MAX_EXPANDED_AXIS ], heightTable[ MAX_EXPANDED_AXIS ];
+
+
+ /* patches finish a raw lightmap */
+ lm->finished = qtrue;
+
+ /* get surface and info */
+ ds = &bspDrawSurfaces[ num ];
+ info = &surfaceInfos[ num ];
+
+ /* make a temporary 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 );
+
+ /* find the longest distance on each row/column */
+ verts = mesh->verts;
+ memset( widthTable, 0, sizeof( widthTable ) );
+ memset( heightTable, 0, sizeof( heightTable ) );
+ for ( y = 0; y < mesh->height; y++ )
+ {
+ for ( x = 0; x < mesh->width; x++ )
+ {
+ /* get width */
+ if ( x + 1 < mesh->width ) {
+ a = &verts[ ( y * mesh->width ) + x ];
+ b = &verts[ ( y * mesh->width ) + x + 1 ];
+ VectorSubtract( a->xyz, b->xyz, delta );
+ length = VectorLength( delta );
+ if ( length > widthTable[ x ] ) {
+ widthTable[ x ] = length;
+ }
+ }
+
+ /* get height */
+ if ( y + 1 < mesh->height ) {
+ a = &verts[ ( y * mesh->width ) + x ];
+ b = &verts[ ( ( y + 1 ) * mesh->width ) + x ];
+ VectorSubtract( a->xyz, b->xyz, delta );
+ length = VectorLength( delta );
+ if ( length > heightTable[ y ] ) {
+ heightTable[ y ] = length;
+ }
+ }
+ }
+ }
+
+ /* determine lightmap width */
+ length = 0;
+ for ( x = 0; x < ( mesh->width - 1 ); x++ )
+ length += widthTable[ x ];
+ lm->w = lm->sampleSize != 0 ? ceil( length / lm->sampleSize ) + 1 : 0;
+ if ( lm->w < ds->patchWidth ) {
+ lm->w = ds->patchWidth;
+ }
+ if ( lm->w > lm->customWidth ) {
+ lm->w = lm->customWidth;
+ }
+ sBasis = (float) ( lm->w - 1 ) / (float) ( ds->patchWidth - 1 );
+
+ /* determine lightmap height */
+ length = 0;
+ for ( y = 0; y < ( mesh->height - 1 ); y++ )
+ length += heightTable[ y ];
+ lm->h = lm->sampleSize != 0 ? ceil( length / lm->sampleSize ) + 1 : 0;
+ if ( lm->h < ds->patchHeight ) {
+ lm->h = ds->patchHeight;
+ }
+ if ( lm->h > lm->customHeight ) {
+ lm->h = lm->customHeight;
+ }
+ tBasis = (float) ( lm->h - 1 ) / (float) ( ds->patchHeight - 1 );
+
+ /* free the temporary mesh */
+ FreeMesh( mesh );
+
+ /* set the lightmap texture coordinates in yDrawVerts */
+ lm->wrap[ 0 ] = qtrue;
+ lm->wrap[ 1 ] = qtrue;
+ verts = &yDrawVerts[ ds->firstVert ];
+ for ( y = 0; y < ds->patchHeight; y++ )
+ {
+ t = ( tBasis * y ) + 0.5f;
+ for ( x = 0; x < ds->patchWidth; x++ )
+ {
+ s = ( sBasis * x ) + 0.5f;
+ verts[ ( y * ds->patchWidth ) + x ].lightmap[ 0 ][ 0 ] = s * superSample;
+ verts[ ( y * ds->patchWidth ) + x ].lightmap[ 0 ][ 1 ] = t * superSample;
+
+ if ( y == 0 && !VectorCompare( verts[ x ].xyz, verts[ ( ( ds->patchHeight - 1 ) * ds->patchWidth ) + x ].xyz ) ) {
+ lm->wrap[ 1 ] = qfalse;
+ }
+ }
+
+ if ( !VectorCompare( verts[ ( y * ds->patchWidth ) ].xyz, verts[ ( y * ds->patchWidth ) + ( ds->patchWidth - 1 ) ].xyz ) ) {
+ lm->wrap[ 0 ] = qfalse;
+ }
+ }
+
+ /* debug code: */
+ //% Sys_Printf( "wrap S: %d wrap T: %d\n", lm->wrap[ 0 ], lm->wrap[ 1 ] );
+ //% if( lm->w > (ds->lightmapWidth & 0xFF) || lm->h > (ds->lightmapHeight & 0xFF) )
+ //% Sys_Printf( "Patch lightmap: (%3d %3d) > (%3d, %3d)\n", lm->w, lm->h, ds->lightmapWidth & 0xFF, ds->lightmapHeight & 0xFF );
+ //% ds->lightmapWidth = lm->w | (ds->lightmapWidth & 0xFFFF0000);
+ //% ds->lightmapHeight = lm->h | (ds->lightmapHeight & 0xFFFF0000);
+
+ /* add to counts */
+ numPatchesLightmapped++;
+
+ /* return */
+ return qtrue;
+}
+
+
+
+/*
+ AddSurfaceToRawLightmap()
+ projects a lightmap for a surface
+ based on AllocateLightmapForSurface()
+ */
+
+qboolean AddSurfaceToRawLightmap( int num, rawLightmap_t *lm ){
+ bspDrawSurface_t *ds, *ds2;
+ surfaceInfo_t *info;
+ int num2, n, i, axisNum;
+ float s, t, d, len, sampleSize;
+ vec3_t mins, maxs, origin, faxis, size, delta, normalized, vecs[ 2 ];
+ vec4_t plane;
+ bspDrawVert_t *verts;
+
+
+ /* get surface and info */
+ ds = &bspDrawSurfaces[ num ];
+ info = &surfaceInfos[ num ];
+
+ /* add the surface to the raw lightmap */
+ lightSurfaces[ numLightSurfaces++ ] = num;
+ lm->numLightSurfaces++;
+
+ /* does this raw lightmap already have any surfaces? */
+ if ( lm->numLightSurfaces > 1 ) {
+ /* surface and raw lightmap must have the same lightmap projection axis */
+ if ( VectorCompare( info->axis, lm->axis ) == qfalse ) {
+ return qfalse;
+ }
+
+ /* match identical attributes */
+ if ( info->sampleSize != lm->sampleSize ||
+ info->entityNum != lm->entityNum ||
+ info->recvShadows != lm->recvShadows ||
+ info->si->lmCustomWidth != lm->customWidth ||
+ info->si->lmCustomHeight != lm->customHeight ||
+ info->si->lmBrightness != lm->brightness ||
+ info->si->lmFilterRadius != lm->filterRadius ||
+ info->si->splotchFix != lm->splotchFix ) {
+ return qfalse;
+ }
+
+ /* surface bounds must intersect with raw lightmap bounds */
+ for ( i = 0; i < 3; i++ )
+ {
+ if ( info->mins[ i ] > lm->maxs[ i ] ) {
+ return qfalse;
+ }
+ if ( info->maxs[ i ] < lm->mins[ i ] ) {
+ return qfalse;
+ }
+ }
+
+ /* plane check (fixme: allow merging of nonplanars) */
+ if ( info->si->lmMergable == qfalse ) {
+ if ( info->plane == NULL || lm->plane == NULL ) {
+ return qfalse;
+ }
+
+ /* compare planes */
+ for ( i = 0; i < 4; i++ )
+ if ( fabs( info->plane[ i ] - lm->plane[ i ] ) > EQUAL_EPSILON ) {
+ return qfalse;
+ }
+ }
+
+ /* debug code hacking */
+ //% if( lm->numLightSurfaces > 1 )
+ //% return qfalse;
+ }
+
+ /* set plane */
+ if ( info->plane == NULL ) {
+ lm->plane = NULL;
+ }
+
+ /* add surface to lightmap bounds */
+ AddPointToBounds( info->mins, lm->mins, lm->maxs );
+ AddPointToBounds( info->maxs, lm->mins, lm->maxs );
+
+ /* check to see if this is a non-planar patch */
+ if ( ds->surfaceType == MST_PATCH &&
+ lm->axis[ 0 ] == 0.0f && lm->axis[ 1 ] == 0.0f && lm->axis[ 2 ] == 0.0f ) {
+ return AddPatchToRawLightmap( num, lm );
+ }
+
+ /* start with initially requested sample size */
+ sampleSize = lm->sampleSize;
+
+ /* round to the lightmap resolution */
+ for ( i = 0; i < 3; i++ )
+ {
+ mins[ i ] = sampleSize * floor( lm->mins[ i ] / sampleSize );
+ maxs[ i ] = sampleSize * ceil( lm->maxs[ i ] / sampleSize );
+ size[ i ] = ( maxs[ i ] - mins[ i ] ) / sampleSize + 1.0f;
+
+ /* hack (god this sucks) */
+ if ( size[ i ] > lm->customWidth || size[ i ] > lm->customHeight || ( lmLimitSize && size[i] > lmLimitSize ) ) {
+ i = -1;
+ sampleSize += 1.0f;
+ }
+ }
+
+ if ( sampleSize != lm->sampleSize && lmLimitSize == 0 ) {
+ Sys_FPrintf( SYS_VRB,"WARNING: surface at (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) too large for desired samplesize/lightmapsize/lightmapscale combination, increased samplesize from %d to %d\n",
+ info->mins[0],
+ info->mins[1],
+ info->mins[2],
+ info->maxs[0],
+ info->maxs[1],
+ info->maxs[2],
+ lm->sampleSize,
+ (int) sampleSize );
+ }
+
+ /* set actual sample size */
+ lm->actualSampleSize = sampleSize;
+
+ /* fixme: copy rounded mins/maxes to lightmap record? */
+ if ( lm->plane == NULL ) {
+ VectorCopy( mins, lm->mins );
+ VectorCopy( maxs, lm->maxs );
+ VectorCopy( mins, origin );
+ }
+
+ /* set lightmap origin */
+ VectorCopy( lm->mins, origin );
+
+ /* make absolute axis */
+ faxis[ 0 ] = fabs( lm->axis[ 0 ] );
+ faxis[ 1 ] = fabs( lm->axis[ 1 ] );
+ faxis[ 2 ] = fabs( lm->axis[ 2 ] );
+
+ /* clear out lightmap vectors */
+ memset( vecs, 0, sizeof( vecs ) );
+
+ /* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
+ if ( faxis[ 2 ] >= faxis[ 0 ] && faxis[ 2 ] >= faxis[ 1 ] ) {
+ axisNum = 2;
+ lm->w = size[ 0 ];
+ lm->h = size[ 1 ];
+ vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
+ vecs[ 1 ][ 1 ] = 1.0f / sampleSize;
+ }
+ else if ( faxis[ 0 ] >= faxis[ 1 ] && faxis[ 0 ] >= faxis[ 2 ] ) {
+ axisNum = 0;
+ lm->w = size[ 1 ];
+ lm->h = size[ 2 ];
+ vecs[ 0 ][ 1 ] = 1.0f / sampleSize;
+ vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
+ }
+ else
+ {
+ axisNum = 1;
+ lm->w = size[ 0 ];
+ lm->h = size[ 2 ];
+ vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
+ vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
+ }
+
+ /* check for bogus axis */
+ if ( faxis[ axisNum ] == 0.0f ) {
+ Sys_FPrintf( SYS_WRN, "WARNING: ProjectSurfaceLightmap: Chose a 0 valued axis\n" );
+ lm->w = lm->h = 0;
+ return qfalse;
+ }
+
+ /* store the axis number in the lightmap */
+ lm->axisNum = axisNum;
+
+ /* walk the list of surfaces on this raw lightmap */
+ for ( n = 0; n < lm->numLightSurfaces; n++ )
+ {
+ /* get surface */
+ num2 = lightSurfaces[ lm->firstLightSurface + n ];
+ ds2 = &bspDrawSurfaces[ num2 ];
+ verts = &yDrawVerts[ ds2->firstVert ];
+
+ /* set the lightmap texture coordinates in yDrawVerts in [0, superSample * lm->customWidth] space */
+ for ( i = 0; i < ds2->numVerts; i++ )
+ {
+ VectorSubtract( verts[ i ].xyz, origin, delta );
+ s = DotProduct( delta, vecs[ 0 ] ) + 0.5f;
+ t = DotProduct( delta, vecs[ 1 ] ) + 0.5f;
+ verts[ i ].lightmap[ 0 ][ 0 ] = s * superSample;
+ verts[ i ].lightmap[ 0 ][ 1 ] = t * superSample;
+
+ if ( s > (float) lm->w || t > (float) lm->h ) {
+ Sys_FPrintf( SYS_VRB, "WARNING: Lightmap texture coords out of range: S %1.4f > %3d || T %1.4f > %3d\n",
+ s, lm->w, t, lm->h );
+ }
+ }
+ }
+
+ /* get first drawsurface */
+ num2 = lightSurfaces[ lm->firstLightSurface ];
+ ds2 = &bspDrawSurfaces[ num2 ];
+ verts = &yDrawVerts[ ds2->firstVert ];
+
+ /* calculate lightmap origin */
+ if ( VectorLength( ds2->lightmapVecs[ 2 ] ) ) {
+ VectorCopy( ds2->lightmapVecs[ 2 ], plane );
+ }
+ else{
+ VectorCopy( lm->axis, plane );
+ }
+ plane[ 3 ] = DotProduct( verts[ 0 ].xyz, plane );
+
+ VectorCopy( origin, lm->origin );
+ d = DotProduct( lm->origin, plane ) - plane[ 3 ];
+ d /= plane[ axisNum ];
+ lm->origin[ axisNum ] -= d;
+
+ /* legacy support */
+ VectorCopy( lm->origin, ds->lightmapOrigin );
+
+ /* for planar surfaces, create lightmap vectors for st->xyz conversion */
+ if ( VectorLength( ds->lightmapVecs[ 2 ] ) || 1 ) { /* ydnar: can't remember what exactly i was thinking here... */
+ /* allocate space for the vectors */
+ lm->vecs = safe_malloc0( 3 * sizeof( vec3_t ) );
+ VectorCopy( ds->lightmapVecs[ 2 ], lm->vecs[ 2 ] );
+
+ /* project stepped lightmap blocks and subtract to get planevecs */
+ for ( i = 0; i < 2; i++ )
+ {
+ len = VectorNormalize( vecs[ i ], normalized );
+ VectorScale( normalized, ( 1.0 / len ), lm->vecs[ i ] );
+ d = DotProduct( lm->vecs[ i ], plane );
+ d /= plane[ axisNum ];
+ lm->vecs[ i ][ axisNum ] -= d;
+ }
+ }
+ else
+ {
+ /* lightmap vectors are useless on a non-planar surface */
+ lm->vecs = NULL;
+ }
+
+ /* add to counts */
+ if ( ds->surfaceType == MST_PATCH ) {
+ numPatchesLightmapped++;
+ if ( lm->plane != NULL ) {
+ numPlanarPatchesLightmapped++;
+ }
+ }
+ else
+ {
+ if ( lm->plane != NULL ) {
+ numPlanarsLightmapped++;
+ }
+ else{
+ numNonPlanarsLightmapped++;
+ }
+ }
+
+ /* return */
+ return qtrue;
+}
+
+
+
+/*
+ CompareSurfaceInfo()
+ compare function for qsort()
+ */
+
+static int CompareSurfaceInfo( const void *a, const void *b ){
+ surfaceInfo_t *aInfo, *bInfo;
+ int i;
+
+
+ /* get surface info */
+ aInfo = &surfaceInfos[ *( (const int*) a ) ];
+ bInfo = &surfaceInfos[ *( (const int*) b ) ];
+
+ /* model first */
+ if ( aInfo->modelindex < bInfo->modelindex ) {
+ return 1;
+ }
+ else if ( aInfo->modelindex > bInfo->modelindex ) {
+ return -1;
+ }
+
+ /* then lightmap status */
+ if ( aInfo->hasLightmap < bInfo->hasLightmap ) {
+ return 1;
+ }
+ else if ( aInfo->hasLightmap > bInfo->hasLightmap ) {
+ return -1;
+ }
+
+ /* 27: then shader! */
+ if ( aInfo->si < bInfo->si ) {
+ return 1;
+ }
+ else if ( aInfo->si > bInfo->si ) {
+ return -1;
+ }
+
+ /* then lightmap sample size */
+ if ( aInfo->sampleSize < bInfo->sampleSize ) {
+ return 1;
+ }
+ else if ( aInfo->sampleSize > bInfo->sampleSize ) {
+ return -1;
+ }
+
+ /* then lightmap axis */
+ for ( i = 0; i < 3; i++ )
+ {
+ if ( aInfo->axis[ i ] < bInfo->axis[ i ] ) {
+ return 1;
+ }
+ else if ( aInfo->axis[ i ] > bInfo->axis[ i ] ) {
+ return -1;
+ }
+ }
+
+ /* then plane */
+ if ( aInfo->plane == NULL && bInfo->plane != NULL ) {
+ return 1;
+ }
+ else if ( aInfo->plane != NULL && bInfo->plane == NULL ) {
+ return -1;
+ }
+ else if ( aInfo->plane != NULL && bInfo->plane != NULL ) {
+ for ( i = 0; i < 4; i++ )
+ {
+ if ( aInfo->plane[ i ] < bInfo->plane[ i ] ) {
+ return 1;
+ }
+ else if ( aInfo->plane[ i ] > bInfo->plane[ i ] ) {
+ return -1;
+ }
+ }
+ }
+
+ /* then position in world */
+ for ( i = 0; i < 3; i++ )
+ {
+ if ( aInfo->mins[ i ] < bInfo->mins[ i ] ) {
+ return 1;
+ }
+ else if ( aInfo->mins[ i ] > bInfo->mins[ i ] ) {
+ return -1;
+ }
+ }
+
+ /* these are functionally identical (this should almost never happen) */
+ return 0;
+}
+
+
+
+/*
+ SetupSurfaceLightmaps()
+ allocates lightmaps for every surface in the bsp that needs one
+ this depends on yDrawVerts being allocated
+ */
+
+void SetupSurfaceLightmaps( void ){
+ int i, j, k, s,num, num2;
+ bspModel_t *model;
+ bspLeaf_t *leaf;
+ bspDrawSurface_t *ds;
+ surfaceInfo_t *info, *info2;
+ rawLightmap_t *lm;
+ qboolean added;
+ vec3_t mapSize, entityOrigin;
+
+
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n" );
+
+ /* determine supersample amount */
+ if ( superSample < 1 ) {
+ superSample = 1;
+ }
+ else if ( superSample > 8 ) {
+ Sys_FPrintf( SYS_WRN, "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
+ superSample = 8;
+ }
+
+ /* clear map bounds */
+ ClearBounds( mapMins, mapMaxs );
+
+ /* allocate a list of surface clusters */
+ numSurfaceClusters = 0;
+ maxSurfaceClusters = numBSPLeafSurfaces;
+ surfaceClusters = safe_malloc0( maxSurfaceClusters * sizeof( *surfaceClusters ) );
+
+ /* allocate a list for per-surface info */
+ surfaceInfos = safe_malloc0( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
+ for ( i = 0; i < numBSPDrawSurfaces; i++ )
+ surfaceInfos[ i ].childSurfaceNum = -1;
+
+ /* allocate a list of surface indexes to be sorted */
+ sortSurfaces = safe_malloc0( numBSPDrawSurfaces * sizeof( int ) );
+
+ /* walk each model in the bsp */
+ for ( i = 0; i < numBSPModels; i++ )
+ {
+ /* get model */
+ model = &bspModels[ i ];
+
+ /* walk the list of surfaces in this model and fill out the info structs */
+ for ( j = 0; j < model->numBSPSurfaces; j++ )
+ {
+ /* make surface index */
+ num = model->firstBSPSurface + j;
+
+ /* copy index to sort list */
+ sortSurfaces[ num ] = num;
+
+ /* get surface and info */
+ ds = &bspDrawSurfaces[ num ];
+ info = &surfaceInfos[ num ];
+
+ /* set entity origin */
+ if ( ds->numVerts > 0 ) {
+ VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
+ }
+ else{
+ VectorClear( entityOrigin );
+ }
+
+ /* basic setup */
+ info->modelindex = i;
+ info->lm = NULL;
+ info->plane = NULL;
+ info->firstSurfaceCluster = numSurfaceClusters;
+
+ /* get extra data */
+ info->si = GetSurfaceExtraShaderInfo( num );
+ if ( info->si == NULL ) {
+ info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );
+ }
+ info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
+ info->entityNum = GetSurfaceExtraEntityNum( num );
+ info->castShadows = GetSurfaceExtraCastShadows( num );
+ info->recvShadows = GetSurfaceExtraRecvShadows( num );
+ info->sampleSize = GetSurfaceExtraSampleSize( num );
+ info->longestCurve = GetSurfaceExtraLongestCurve( num );
+ info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
+ GetSurfaceExtraLightmapAxis( num, info->axis );
+
+ /* mark parent */
+ if ( info->parentSurfaceNum >= 0 ) {
+ surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
+ }
+
+ /* determine surface bounds */
+ ClearBounds( info->mins, info->maxs );
+ for ( k = 0; k < ds->numVerts; k++ )
+ {
+ AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
+ AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
+ }
+
+ /* find all the bsp clusters the surface falls into */
+ for ( k = 0; k < numBSPLeafs; k++ )
+ {
+ /* get leaf */
+ leaf = &bspLeafs[ k ];
+
+ /* test bbox */
+ if ( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
+ leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
+ leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] ) {
+ continue;
+ }
+
+ /* test leaf surfaces */
+ for ( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
+ {
+ if ( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num ) {
+ if ( numSurfaceClusters >= maxSurfaceClusters ) {
+ Error( "maxSurfaceClusters exceeded" );
+ }
+ surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
+ numSurfaceClusters++;
+ info->numSurfaceClusters++;
+ }
+ }
+ }
+
+ /* determine if surface is planar */
+ if ( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f ) {
+ /* make a plane */
+ info->plane = safe_malloc( 4 * sizeof( float ) );
+ VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
+ info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
+ }
+
+ /* determine if surface requires a lightmap */
+ if ( ds->surfaceType == MST_TRIANGLE_SOUP ||
+ ds->surfaceType == MST_FOLIAGE ||
+ ( info->si->compileFlags & C_VERTEXLIT ) ) {
+ numSurfsVertexLit++;
+ }
+ else
+ {
+ numSurfsLightmapped++;
+ info->hasLightmap = qtrue;
+ }
+ }
+ }
+
+ /* find longest map distance */
+ VectorSubtract( mapMaxs, mapMins, mapSize );
+ maxMapDistance = VectorLength( mapSize );
+
+ /* sort the surfaces info list */
+ qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
+
+ /* allocate a list of surfaces that would go into raw lightmaps */
+ numLightSurfaces = 0;
+ lightSurfaces = safe_malloc0( numSurfsLightmapped * sizeof( int ) );
+
+ /* allocate a list of raw lightmaps */
+ numRawSuperLuxels = 0;
+ numRawLightmaps = 0;
+ rawLightmaps = safe_malloc0( numSurfsLightmapped * sizeof( *rawLightmaps ) );
+
+ /* walk the list of sorted surfaces */
+ for ( i = 0; i < numBSPDrawSurfaces; i++ )
+ {
+ /* get info and attempt early out */
+ num = sortSurfaces[ i ];
+ ds = &bspDrawSurfaces[ num ];
+ info = &surfaceInfos[ num ];
+ if ( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 ) {
+ continue;
+ }
+
+ /* allocate a new raw lightmap */
+ lm = &rawLightmaps[ numRawLightmaps ];
+ numRawLightmaps++;
+
+ /* set it up */
+ lm->splotchFix = info->si->splotchFix;
+ lm->firstLightSurface = numLightSurfaces;
+ lm->numLightSurfaces = 0;
+ /* vortex: multiply lightmap sample size by -samplescale */
+ if ( sampleScale > 0 ) {
+ lm->sampleSize = info->sampleSize * sampleScale;
+ }
+ else{
+ lm->sampleSize = info->sampleSize;
+ }
+ lm->actualSampleSize = lm->sampleSize;
+ lm->entityNum = info->entityNum;
+ lm->recvShadows = info->recvShadows;
+ lm->brightness = info->si->lmBrightness;
+ lm->filterRadius = info->si->lmFilterRadius;
+ VectorCopy( info->si->floodlightRGB, lm->floodlightRGB );
+ lm->floodlightDistance = info->si->floodlightDistance;
+ lm->floodlightIntensity = info->si->floodlightIntensity;
+ lm->floodlightDirectionScale = info->si->floodlightDirectionScale;
+ VectorCopy( info->axis, lm->axis );
+ lm->plane = info->plane;
+ VectorCopy( info->mins, lm->mins );
+ VectorCopy( info->maxs, lm->maxs );
+
+ lm->customWidth = info->si->lmCustomWidth;
+ lm->customHeight = info->si->lmCustomHeight;
+
+ /* add the surface to the raw lightmap */
+ AddSurfaceToRawLightmap( num, lm );
+ info->lm = lm;
+
+ /* do an exhaustive merge */
+ added = qtrue;
+ while ( added )
+ {
+ /* walk the list of surfaces again */
+ added = qfalse;
+ for ( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
+ {
+ /* get info and attempt early out */
+ num2 = sortSurfaces[ j ];
+ info2 = &surfaceInfos[ num2 ];
+ if ( info2->hasLightmap == qfalse || info2->lm != NULL ) {
+ continue;
+ }
+
+ /* add the surface to the raw lightmap */
+ if ( AddSurfaceToRawLightmap( num2, lm ) ) {
+ info2->lm = lm;
+ added = qtrue;
+ }
+ else
+ {
+ /* back up one */
+ lm->numLightSurfaces--;
+ numLightSurfaces--;
+ }
+ }
+ }
+
+ /* finish the lightmap and allocate the various buffers */
+ FinishRawLightmap( lm );
+ }
+
+ /* allocate vertex luxel storage */
+ for ( k = 0; k < MAX_LIGHTMAPS; k++ )
+ {
+ vertexLuxels[ k ] = safe_malloc0( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
+ radVertexLuxels[ k ] = safe_malloc0( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
+ }
+
+ /* emit some stats */
+ Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
+ Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
+ Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
+ Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
+ Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
+ Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
+ Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
+ Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
+}
+
+
+
+/*
+ StitchSurfaceLightmaps()
+ stitches lightmap edges
+ 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
+ */
+
+#define MAX_STITCH_CANDIDATES 32
+#define MAX_STITCH_LUXELS 64
+
+void StitchSurfaceLightmaps( void ){
+ int i, j, x, y, x2, y2, *cluster, *cluster2,
+ numStitched, numCandidates, numLuxels, f, fOld, start;
+ rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
+ float *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
+ sampleSize, average[ 3 ], totalColor, ootc;
+
+
+ /* disabled for now */
+ return;
+
+ /* note it */
+ Sys_Printf( "--- StitchSurfaceLightmaps ---\n" );
+
+ /* init pacifier */
+ fOld = -1;
+ start = I_FloatTime();
+
+ /* walk the list of raw lightmaps */
+ numStitched = 0;
+ for ( i = 0; i < numRawLightmaps; i++ )
+ {
+ /* print pacifier */
+ f = 10 * i / numRawLightmaps;
+ if ( f != fOld ) {
+ fOld = f;
+ Sys_Printf( "%i...", f );
+ }
+
+ /* get lightmap a */
+ a = &rawLightmaps[ i ];
+
+ /* walk rest of lightmaps */
+ numCandidates = 0;
+ for ( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
+ {
+ /* get lightmap b */
+ b = &rawLightmaps[ j ];
+
+ /* test bounding box */
+ if ( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
+ a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
+ a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] ) {
+ continue;
+ }
+
+ /* add candidate */
+ c[ numCandidates++ ] = b;
+ }
+
+ /* walk luxels */
+ for ( y = 0; y < a->sh; y++ )
+ {
+ for ( x = 0; x < a->sw; x++ )
+ {
+ /* ignore unmapped/unlit luxels */
+ lm = a;
+ cluster = SUPER_CLUSTER( x, y );
+ if ( *cluster == CLUSTER_UNMAPPED ) {
+ continue;
+ }
+ luxel = SUPER_LUXEL( 0, x, y );
+ if ( luxel[ 3 ] <= 0.0f ) {
+ continue;
+ }
+
+ /* get particulars */
+ origin = SUPER_ORIGIN( x, y );
+ normal = SUPER_NORMAL( x, y );
+
+ /* walk candidate list */
+ for ( j = 0; j < numCandidates; j++ )
+ {
+ /* get candidate */
+ b = c[ j ];
+ lm = b;
+
+ /* set samplesize to the smaller of the pair */
+ sampleSize = 0.5f * ( a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize );
+
+ /* test bounding box */
+ if ( origin[ 0 ] < ( b->mins[ 0 ] - sampleSize ) || ( origin[ 0 ] > b->maxs[ 0 ] + sampleSize ) ||
+ origin[ 1 ] < ( b->mins[ 1 ] - sampleSize ) || ( origin[ 1 ] > b->maxs[ 1 ] + sampleSize ) ||
+ origin[ 2 ] < ( b->mins[ 2 ] - sampleSize ) || ( origin[ 2 ] > b->maxs[ 2 ] + sampleSize ) ) {
+ continue;
+ }
+
+ /* walk candidate luxels */
+ VectorClear( average );
+ numLuxels = 0;
+ totalColor = 0.0f;
+ for ( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
+ {
+ for ( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
+ {
+ /* ignore same luxels */
+ if ( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 ) {
+ continue;
+ }
+
+ /* ignore unmapped/unlit luxels */
+ cluster2 = SUPER_CLUSTER( x2, y2 );
+ if ( *cluster2 == CLUSTER_UNMAPPED ) {
+ continue;
+ }
+ luxel2 = SUPER_LUXEL( 0, x2, y2 );
+ if ( luxel2[ 3 ] <= 0.0f ) {
+ continue;
+ }
+
+ /* get particulars */
+ origin2 = SUPER_ORIGIN( x2, y2 );
+ normal2 = SUPER_NORMAL( x2, y2 );
+
+ /* test normal */
+ if ( DotProduct( normal, normal2 ) < 0.5f ) {
+ continue;
+ }
+
+ /* test bounds */
+ if ( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
+ fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
+ fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize ) {
+ continue;
+ }
+
+ /* add luxel */
+ //% VectorSet( luxel2, 255, 0, 255 );
+ VectorAdd( average, luxel2, average );
+ totalColor += luxel2[ 3 ];
+ }
+ }
+
+ /* early out */
+ if ( numLuxels == 0 ) {
+ continue;
+ }
+
+ /* scale average */
+ ootc = 1.0f / totalColor;
+ VectorScale( average, ootc, luxel );
+ luxel[ 3 ] = 1.0f;
+ numStitched++;
+ }
+ }
+ }
+ }
+
+ /* emit statistics */
+ Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
+ Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
+}
+
+
+
+/*
+ CompareBSPLuxels()
+ compares two surface lightmaps' bsp luxels, ignoring occluded luxels
+ */
+
+#define SOLID_EPSILON 0.0625
+#define LUXEL_TOLERANCE 0.0025
+#define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */
+
+static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
+ rawLightmap_t *lm;
+ int x, y;
+ double delta, total, rd, gd, bd;
+ float *aLuxel, *bLuxel;
+
+
+ /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
+ if ( ( minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ] ) &&
+ ( ( aNum == 0 && bNum != 0 ) || ( aNum != 0 && bNum == 0 ) ) ) {
+ return qfalse;
+ }
+
+ /* basic tests */
+ if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
+ a->brightness != b->brightness ||
+ a->solid[ aNum ] != b->solid[ bNum ] ||
+ a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
+ return qfalse;
+ }
+
+ /* compare solid color lightmaps */
+ if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
+ /* get deltas */
+ rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
+ gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
+ bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
+
+ /* compare color */
+ if ( rd > SOLID_EPSILON || gd > SOLID_EPSILON || bd > SOLID_EPSILON ) {
+ return qfalse;
+ }
+
+ /* okay */
+ return qtrue;
+ }
+
+ /* compare nonsolid lightmaps */
+ if ( a->w != b->w || a->h != b->h ) {
+ return qfalse;
+ }
+
+ /* compare luxels */
+ delta = 0.0;
+ total = 0.0;
+ for ( y = 0; y < a->h; y++ )
+ {
+ for ( x = 0; x < a->w; x++ )
+ {
+ /* increment total */
+ total += 1.0;
+
+ /* get luxels */
+ lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
+ lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
+
+ /* ignore unused luxels */
+ if ( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 ) {
+ continue;
+ }
+
+ /* get deltas */
+ rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
+ gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
+ bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
+
+ /* 2003-09-27: compare individual luxels */
+ if ( rd > 3.0 || gd > 3.0 || bd > 3.0 ) {
+ return qfalse;
+ }
+
+ /* compare (fixme: take into account perceptual differences) */
+ delta += rd * LUXEL_COLOR_FRAC;
+ delta += gd * LUXEL_COLOR_FRAC;
+ delta += bd * LUXEL_COLOR_FRAC;
+
+ /* is the change too high? */
+ if ( total > 0.0 && ( ( delta / total ) > LUXEL_TOLERANCE ) ) {
+ return qfalse;
+ }
+ }
+ }
+
+ /* made it this far, they must be identical (or close enough) */
+ return qtrue;
+}
+
+
+
+/*
+ MergeBSPLuxels()
+ merges two surface lightmaps' bsp luxels, overwriting occluded luxels
+ */
+
+static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
+ rawLightmap_t *lm;
+ int x, y;
+ float luxel[ 3 ], *aLuxel, *bLuxel;
+
+
+ /* basic tests */
+ if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
+ a->brightness != b->brightness ||
+ a->solid[ aNum ] != b->solid[ bNum ] ||
+ a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
+ return qfalse;
+ }
+
+ /* compare solid lightmaps */
+ if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
+ /* average */
+ VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
+ VectorScale( luxel, 0.5f, luxel );
+
+ /* copy to both */
+ VectorCopy( luxel, a->solidColor[ aNum ] );
+ VectorCopy( luxel, b->solidColor[ bNum ] );
+
+ /* return to sender */
+ return qtrue;
+ }
+
+ /* compare nonsolid lightmaps */
+ if ( a->w != b->w || a->h != b->h ) {
+ return qfalse;
+ }
+
+ /* merge luxels */
+ for ( y = 0; y < a->h; y++ )
+ {
+ for ( x = 0; x < a->w; x++ )
+ {
+ /* get luxels */
+ lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
+ lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
+
+ /* handle occlusion mismatch */
+ if ( aLuxel[ 0 ] < 0.0f ) {
+ VectorCopy( bLuxel, aLuxel );
+ }
+ else if ( bLuxel[ 0 ] < 0.0f ) {
+ VectorCopy( aLuxel, bLuxel );
+ }
+ else
+ {
+ /* average */
+ VectorAdd( aLuxel, bLuxel, luxel );
+ VectorScale( luxel, 0.5f, luxel );
+
+ /* debugging code */
+ //% luxel[ 2 ] += 64.0f;
+
+ /* copy to both */
+ VectorCopy( luxel, aLuxel );
+ VectorCopy( luxel, bLuxel );
+ }
+ }
+ }
+
+ /* done */
+ return qtrue;
+}
+
+
+
+/*
+ ApproximateLuxel()
+ determines if a single luxel is can be approximated with the interpolated vertex rgba
+ */
+
+static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv ){
+ int i, x, y, d, lightmapNum;
+ float *luxel;
+ vec3_t color, vertexColor;
+ byte cb[ 4 ], vcb[ 4 ];
+
+
+ /* find luxel xy coords */
+ x = dv->lightmap[ 0 ][ 0 ] / superSample;
+ y = dv->lightmap[ 0 ][ 1 ] / superSample;
+ if ( x < 0 ) {
+ x = 0;
+ }
+ else if ( x >= lm->w ) {
+ x = lm->w - 1;
+ }
+ if ( y < 0 ) {
+ y = 0;
+ }
+ else if ( y >= lm->h ) {
+ y = lm->h - 1;
+ }
+
+ /* walk list */
+ for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* early out */
+ if ( lm->styles[ lightmapNum ] == LS_NONE ) {
+ continue;
+ }
+
+ /* get luxel */
+ luxel = BSP_LUXEL( lightmapNum, x, y );
+
+ /* ignore occluded luxels */
+ if ( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f ) {
+ return qtrue;
+ }
+
+ /* copy, set min color and compare */
+ VectorCopy( luxel, color );
+ VectorCopy( dv->color[ 0 ], vertexColor );
+
+ /* styles are not affected by minlight */
+ if ( lightmapNum == 0 ) {
+ for ( i = 0; i < 3; i++ )
+ {
+ /* set min color */
+ if ( color[ i ] < minLight[ i ] ) {
+ color[ i ] = minLight[ i ];
+ }
+ if ( vertexColor[ i ] < minLight[ i ] ) { /* note NOT minVertexLight */
+ vertexColor[ i ] = minLight[ i ];
+ }
+ }
+ }
+
+ /* set to bytes */
+ ColorToBytes( color, cb, 1.0f );
+ ColorToBytes( vertexColor, vcb, 1.0f );
+
+ /* compare */
+ for ( i = 0; i < 3; i++ )
+ {
+ d = cb[ i ] - vcb[ i ];
+ if ( d < 0 ) {
+ d *= -1;
+ }
+ if ( d > approximateTolerance ) {
+ return qfalse;
+ }
+ }
+ }
+
+ /* close enough for the girls i date */
+ return qtrue;
+}
+
+
+
+/*
+ ApproximateTriangle()
+ determines if a single triangle can be approximated with vertex rgba
+ */
+
+static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] ){
+ bspDrawVert_t mid, *dv2[ 3 ];
+ int max;
+
+
+ /* approximate the vertexes */
+ if ( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse ) {
+ return qfalse;
+ }
+ if ( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse ) {
+ return qfalse;
+ }
+ if ( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse ) {
+ return qfalse;
+ }
+
+ /* subdivide calc */
+ {
+ int i;
+ float dx, dy, dist, maxDist;
+
+
+ /* find the longest edge and split it */
+ max = -1;
+ maxDist = 0;
+ for ( i = 0; i < 3; i++ )
+ {
+ dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 0 ];
+ dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 1 ];
+ dist = sqrt( ( dx * dx ) + ( dy * dy ) );
+ if ( dist > maxDist ) {
+ maxDist = dist;
+ max = i;
+ }
+ }
+
+ /* try to early out */
+ if ( i < 0 || maxDist < subdivideThreshold ) {
+ return qtrue;
+ }
+ }
+
+ /* split the longest edge and map it */
+ LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
+ if ( ApproximateLuxel( lm, &mid ) == qfalse ) {
+ return qfalse;
+ }
+
+ /* recurse to first triangle */
+ VectorCopy( dv, dv2 );
+ dv2[ max ] = ∣
+ if ( ApproximateTriangle_r( lm, dv2 ) == qfalse ) {
+ return qfalse;
+ }
+
+ /* recurse to second triangle */
+ VectorCopy( dv, dv2 );
+ dv2[ ( max + 1 ) % 3 ] = ∣
+ return ApproximateTriangle_r( lm, dv2 );
+}
+
+
+
+/*
+ ApproximateLightmap()
+ determines if a raw lightmap can be approximated sufficiently with vertex colors
+ */
+
+static qboolean ApproximateLightmap( rawLightmap_t *lm ){
+ int n, num, i, x, y, pw[ 5 ], r;
+ bspDrawSurface_t *ds;
+ surfaceInfo_t *info;
+ mesh_t src, *subdivided, *mesh;
+ bspDrawVert_t *verts, *dv[ 3 ];
+ qboolean approximated;
+
+
+ /* approximating? */
+ if ( approximateTolerance <= 0 ) {
+ return qfalse;
+ }
+
+ /* test for jmonroe */
+ #if 0
+ /* don't approx lightmaps with styled twins */
+ if ( lm->numStyledTwins > 0 ) {
+ return qfalse;
+ }
+
+ /* don't approx lightmaps with styles */
+ for ( i = 1; i < MAX_LIGHTMAPS; i++ )
+ {
+ if ( lm->styles[ i ] != LS_NONE ) {
+ return qfalse;
+ }
+ }
+ #endif
+
+ /* assume reduced until shadow detail is found */
+ approximated = qtrue;
+
+ /* walk the list of surfaces on this raw lightmap */
+ for ( n = 0; n < lm->numLightSurfaces; n++ )
+ {
+ /* get surface */
+ num = lightSurfaces[ lm->firstLightSurface + n ];
+ ds = &bspDrawSurfaces[ num ];
+ info = &surfaceInfos[ num ];
+
+ /* assume not-reduced initially */
+ info->approximated = qfalse;
+
+ /* bail if lightmap doesn't match up */
+ if ( info->lm != lm ) {
+ continue;
+ }
+
+ /* bail if not vertex lit */
+ if ( info->si->noVertexLight ) {
+ continue;
+ }
+
+ /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
+ if ( ( info->maxs[ 0 ] - info->mins[ 0 ] ) <= ( 2.0f * info->sampleSize ) &&
+ ( info->maxs[ 1 ] - info->mins[ 1 ] ) <= ( 2.0f * info->sampleSize ) &&
+ ( info->maxs[ 2 ] - info->mins[ 2 ] ) <= ( 2.0f * info->sampleSize ) ) {
+ info->approximated = qtrue;
+ numSurfsVertexForced++;
+ continue;
+ }
+
+ /* handle the triangles */
+ switch ( ds->surfaceType )
+ {
+ case MST_PLANAR:
+ /* get verts */
+ verts = yDrawVerts + ds->firstVert;
+
+ /* map the triangles */
+ info->approximated = qtrue;
+ for ( i = 0; i < ds->numIndexes && info->approximated; 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 ] ];
+ info->approximated = ApproximateTriangle_r( lm, dv );
+ }
+ 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;
+
+ /* map the mesh quads */
+ info->approximated = qtrue;
+ for ( y = 0; y < ( mesh->height - 1 ) && info->approximated; y++ )
+ {
+ for ( x = 0; x < ( mesh->width - 1 ) && info->approximated; 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 ] ];
+ info->approximated = ApproximateTriangle_r( lm, dv );
+
+ /* get drawverts and map second triangle */
+ dv[ 0 ] = &verts[ pw[ r + 0 ] ];
+ dv[ 1 ] = &verts[ pw[ r + 2 ] ];
+ dv[ 2 ] = &verts[ pw[ r + 3 ] ];
+ if ( info->approximated ) {
+ info->approximated = ApproximateTriangle_r( lm, dv );
+ }
+ }
+ }
+
+ /* free the mesh */
+ FreeMesh( mesh );
+ break;
+
+ default:
+ break;
+ }
+
+ /* reduced? */
+ if ( info->approximated == qfalse ) {
+ approximated = qfalse;
+ }
+ else{
+ numSurfsVertexApproximated++;
+ }
+ }
+
+ /* return */
+ return approximated;
+}
+
+
+
+/*
+ TestOutLightmapStamp()
+ tests a stamp on a given lightmap for validity
+ */
+
+static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y ){
+ int sx, sy, ox, oy, offset;
+ float *luxel;
+
+
+ /* bounds check */
+ if ( x < 0 || y < 0 || ( x + lm->w ) > olm->customWidth || ( y + lm->h ) > olm->customHeight ) {
+ return qfalse;
+ }
+
+ /* solid lightmaps test a 1x1 stamp */
+ if ( lm->solid[ lightmapNum ] ) {
+ offset = ( y * olm->customWidth ) + x;
+ if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
+ return qfalse;
+ }
+ return qtrue;
+ }
+
+ /* test the stamp */
+ for ( sy = 0; sy < lm->h; sy++ )
+ {
+ for ( sx = 0; sx < lm->w; sx++ )
+ {
+ /* get luxel */
+ luxel = BSP_LUXEL( lightmapNum, sx, sy );
+ if ( luxel[ 0 ] < 0.0f ) {
+ continue;
+ }
+
+ /* get bsp lightmap coords and test */
+ ox = x + sx;
+ oy = y + sy;
+ offset = ( oy * olm->customWidth ) + ox;
+ if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
+ return qfalse;
+ }
+ }
+ }
+
+ /* stamp is empty */
+ return qtrue;
+}
+
+
+
+/*
+ SetupOutLightmap()
+ sets up an output lightmap
+ */
+
+static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm ){
+ /* dummy check */
+ if ( lm == NULL || olm == NULL ) {
+ return;
+ }
+
+ /* is this a "normal" bsp-stored lightmap? */
+ if ( ( lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize ) || externalLightmaps ) {
+ olm->lightmapNum = numBSPLightmaps;
+ numBSPLightmaps++;
+
+ /* lightmaps are interleaved with light direction maps */
+ if ( deluxemap ) {
+ numBSPLightmaps++;
+ }
+ }
+ else{
+ olm->lightmapNum = -3;
+ }
+
+ /* set external lightmap number */
+ olm->extLightmapNum = -1;
+
+ /* set it up */
+ olm->numLightmaps = 0;
+ olm->customWidth = lm->customWidth;
+ olm->customHeight = lm->customHeight;
+ olm->freeLuxels = olm->customWidth * olm->customHeight;
+ olm->numShaders = 0;
+
+ /* allocate buffers */
+ olm->lightBits = safe_malloc0( ( olm->customWidth * olm->customHeight / 8 ) + 8 );
+ olm->bspLightBytes = safe_malloc0( olm->customWidth * olm->customHeight * 3 );
+ if ( deluxemap ) {
+ olm->bspDirBytes = safe_malloc0( olm->customWidth * olm->customHeight * 3 );
+ }
+}
+
+
+
+/*
+ FindOutLightmaps()
+ for a given surface lightmap, find output lightmap pages and positions for it
+ */
+
+#define LIGHTMAP_RESERVE_COUNT 1
+static void FindOutLightmaps( rawLightmap_t *lm, qboolean fastAllocate ){
+ int i, j, k, lightmapNum, xMax, yMax, x = -1, y = -1, sx, sy, ox, oy, offset;
+ outLightmap_t *olm;
+ surfaceInfo_t *info;
+ float *luxel, *deluxel;
+ vec3_t color, direction;
+ byte *pixel;
+ qboolean ok;
+ int xIncrement, yIncrement;
+
+ /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
+ for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ lm->outLightmapNums[ lightmapNum ] = -3;
+
+ /* can this lightmap be approximated with vertex color? */
+ if ( ApproximateLightmap( lm ) ) {
+ return;
+ }
+
+ /* walk list */
+ for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* early out */
+ if ( lm->styles[ lightmapNum ] == LS_NONE ) {
+ continue;
+ }
+
+ /* don't store twinned lightmaps */
+ if ( lm->twins[ lightmapNum ] != NULL ) {
+ continue;
+ }
+
+ /* if this is a styled lightmap, try some normalized locations first */
+ ok = qfalse;
+ if ( lightmapNum > 0 && outLightmaps != NULL ) {
+ /* loop twice */
+ for ( j = 0; j < 2; j++ )
+ {
+ /* try identical position */
+ for ( i = 0; i < numOutLightmaps; i++ )
+ {
+ /* get the output lightmap */
+ olm = &outLightmaps[ i ];
+
+ /* simple early out test */
+ if ( olm->freeLuxels < lm->used ) {
+ continue;
+ }
+
+ /* don't store non-custom raw lightmaps on custom bsp lightmaps */
+ if ( olm->customWidth != lm->customWidth ||
+ olm->customHeight != lm->customHeight ) {
+ continue;
+ }
+
+ /* try identical */
+ if ( j == 0 ) {
+ x = lm->lightmapX[ 0 ];
+ y = lm->lightmapY[ 0 ];
+ ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
+ }
+
+ /* try shifting */
+ else
+ {
+ for ( sy = -1; sy <= 1; sy++ )
+ {
+ for ( sx = -1; sx <= 1; sx++ )
+ {
+ x = lm->lightmapX[ 0 ] + sx * ( olm->customWidth >> 1 ); //% lm->w;
+ y = lm->lightmapY[ 0 ] + sy * ( olm->customHeight >> 1 ); //% lm->h;
+ ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
+
+ if ( ok ) {
+ break;
+ }
+ }
+
+ if ( ok ) {
+ break;
+ }
+ }
+ }
+
+ if ( ok ) {
+ break;
+ }
+ }
+
+ if ( ok ) {
+ break;
+ }
+ }
+ }
+
+ /* try normal placement algorithm */
+ if ( ok == qfalse ) {
+ /* reset origin */
+ x = 0;
+ y = 0;
+
+ /* walk the list of lightmap pages */
+ if ( lightmapSearchBlockSize <= 0 || numOutLightmaps < LIGHTMAP_RESERVE_COUNT ) {
+ i = 0;
+ }
+ else{
+ i = ( ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) / lightmapSearchBlockSize ) * lightmapSearchBlockSize;
+ }
+ for ( ; i < numOutLightmaps; i++ )
+ {
+ /* get the output lightmap */
+ olm = &outLightmaps[ i ];
+
+ /* simple early out test */
+ if ( olm->freeLuxels < lm->used ) {
+ continue;
+ }
+
+ /* if fast allocation, skip lightmap files that are more than 90% complete */
+ if ( fastAllocate == qtrue ) {
+ if (olm->freeLuxels < (olm->customWidth * olm->customHeight) / 10) {
+ continue;
+ }
+ }
+
+ /* don't store non-custom raw lightmaps on custom bsp lightmaps */
+ if ( olm->customWidth != lm->customWidth ||
+ olm->customHeight != lm->customHeight ) {
+ continue;
+ }
+
+ /* set maxs */
+ if ( lm->solid[ lightmapNum ] ) {
+ xMax = olm->customWidth;
+ yMax = olm->customHeight;
+ }
+ else
+ {
+ xMax = ( olm->customWidth - lm->w ) + 1;
+ yMax = ( olm->customHeight - lm->h ) + 1;
+ }
+
+ /* if fast allocation, do not test allocation on every pixels, especially for large lightmaps */
+ if ( fastAllocate == qtrue ) {
+ xIncrement = MAX(1, lm->w / 15);
+ yIncrement = MAX(1, lm->h / 15);
+ }
+ else {
+ xIncrement = 1;
+ yIncrement = 1;
+ }
+
+ /* walk the origin around the lightmap */
+ for ( y = 0; y < yMax; y += yIncrement )
+ {
+ for ( x = 0; x < xMax; x += xIncrement )
+ {
+ /* find a fine tract of lauhnd */
+ ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
+
+ if ( ok ) {
+ break;
+ }
+ }
+
+ if ( ok ) {
+ break;
+ }
+ }
+
+ if ( ok ) {
+ break;
+ }
+
+ /* reset x and y */
+ x = 0;
+ y = 0;
+ }
+ }
+
+ /* no match? */
+ if ( ok == qfalse ) {
+ /* allocate LIGHTMAP_RESERVE_COUNT new output lightmaps */
+ numOutLightmaps += LIGHTMAP_RESERVE_COUNT;
+ olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
+ if ( outLightmaps != NULL && numOutLightmaps > LIGHTMAP_RESERVE_COUNT ) {
+ memcpy( olm, outLightmaps, ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) * sizeof( outLightmap_t ) );
+ free( outLightmaps );
+ }
+ outLightmaps = olm;
+
+ /* initialize both out lightmaps */
+ for ( k = numOutLightmaps - LIGHTMAP_RESERVE_COUNT; k < numOutLightmaps; ++k )
+ SetupOutLightmap( lm, &outLightmaps[ k ] );
+
+ /* set out lightmap */
+ i = numOutLightmaps - LIGHTMAP_RESERVE_COUNT;
+ olm = &outLightmaps[ i ];
+
+ /* set stamp xy origin to the first surface lightmap */
+ if ( lightmapNum > 0 ) {
+ x = lm->lightmapX[ 0 ];
+ y = lm->lightmapY[ 0 ];
+ }
+ }
+
+ /* if this is a style-using lightmap, it must be exported */
+ if ( lightmapNum > 0 && game->load != LoadRBSPFile ) {
+ olm->extLightmapNum = 0;
+ }
+
+ /* add the surface lightmap to the bsp lightmap */
+ lm->outLightmapNums[ lightmapNum ] = i;
+ lm->lightmapX[ lightmapNum ] = x;
+ lm->lightmapY[ lightmapNum ] = y;
+ olm->numLightmaps++;
+
+ /* add shaders */
+ for ( i = 0; i < lm->numLightSurfaces; i++ )
+ {
+ /* get surface info */
+ info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
+
+ /* test for shader */
+ for ( j = 0; j < olm->numShaders; j++ )
+ {
+ if ( olm->shaders[ j ] == info->si ) {
+ break;
+ }
+ }
+
+ /* if it doesn't exist, add it */
+ if ( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS ) {
+ olm->shaders[ olm->numShaders ] = info->si;
+ olm->numShaders++;
+ numLightmapShaders++;
+ }
+ }
+
+ /* set maxs */
+ if ( lm->solid[ lightmapNum ] ) {
+ xMax = 1;
+ yMax = 1;
+ }
+ else
+ {
+ xMax = lm->w;
+ yMax = lm->h;
+ }
+
+ /* mark the bits used */
+ for ( y = 0; y < yMax; y++ )
+ {
+ for ( x = 0; x < xMax; x++ )
+ {
+ /* get luxel */
+ luxel = BSP_LUXEL( lightmapNum, x, y );
+ deluxel = BSP_DELUXEL( x, y );
+ if ( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ] ) {
+ continue;
+ }
+
+ /* set minimum light */
+ if ( lm->solid[ lightmapNum ] ) {
+ if ( debug ) {
+ VectorSet( color, 255.0f, 0.0f, 0.0f );
+ }
+ else{
+ VectorCopy( lm->solidColor[ lightmapNum ], color );
+ }
+ }
+ else{
+ VectorCopy( luxel, color );
+ }
+
+ /* styles are not affected by minlight */
+ if ( lightmapNum == 0 ) {
+ for ( i = 0; i < 3; i++ )
+ {
+ if ( color[ i ] < minLight[ i ] ) {
+ color[ i ] = minLight[ i ];
+ }
+ }
+ }
+
+ /* get bsp lightmap coords */
+ ox = x + lm->lightmapX[ lightmapNum ];
+ oy = y + lm->lightmapY[ lightmapNum ];
+ offset = ( oy * olm->customWidth ) + ox;
+
+ /* flag pixel as used */
+ olm->lightBits[ offset >> 3 ] |= ( 1 << ( offset & 7 ) );
+ olm->freeLuxels--;
+
+ /* store color */
+ pixel = olm->bspLightBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
+ ColorToBytes( color, pixel, lm->brightness );
+
+ /* store direction */
+ if ( deluxemap ) {
+ /* normalize average light direction */
+ pixel = olm->bspDirBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
+ VectorScale( deluxel, 1000.0f, direction );
+ VectorNormalize( direction, direction );
+ VectorScale( direction, 127.5f, direction );
+ for ( i = 0; i < 3; i++ )
+ pixel[ i ] = (byte)( 127.5f + direction[ i ] );
+ }
+ }
+ }
+ }
+}
+
+
+
+/*
+ CompareRawLightmap()
+ compare function for qsort()
+ */
+
+static int CompareRawLightmap( const void *a, const void *b ){
+ rawLightmap_t *alm, *blm;
+ surfaceInfo_t *aInfo, *bInfo;
+ int i, min, diff;
+
+
+ /* get lightmaps */
+ alm = &rawLightmaps[ *( (const int*) a ) ];
+ blm = &rawLightmaps[ *( (const int*) b ) ];
+
+ /* get min number of surfaces */
+ min = ( alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces );
+
+ /* iterate */
+ for ( i = 0; i < min; i++ )
+ {
+ /* get surface info */
+ aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
+ bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
+
+ /* compare shader names */
+ diff = strcmp( aInfo->si->shader, bInfo->si->shader );
+ if ( diff != 0 ) {
+ return diff;
+ }
+ }
+
+ /* test style count */
+ diff = 0;
+ for ( i = 0; i < MAX_LIGHTMAPS; i++ )
+ diff += blm->styles[ i ] - alm->styles[ i ];
+ if ( diff ) {
+ return diff;
+ }
+
+ /* compare size */
+ diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
+ if ( diff != 0 ) {
+ return diff;
+ }
+
+ /* must be equivalent */
+ return 0;
+}
+
+
+
+void FillOutLightmap( outLightmap_t *olm ){
+ int x, y;
+ int ofs;
+ vec3_t dir_sum, light_sum;
+ int cnt, filled;
+ byte *lightBitsNew = NULL;
+ byte *lightBytesNew = NULL;
+ byte *dirBytesNew = NULL;
+
+ lightBitsNew = safe_malloc( ( olm->customWidth * olm->customHeight + 8 ) / 8 );
+ lightBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
+ if ( deluxemap ) {
+ dirBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
+ }
+
+ /*
+ memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
+ olm->lightBits[0] |= 1;
+ olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
+ memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
+ olm->bspLightBytes[0] = 255;
+ olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
+ */
+
+ memcpy( lightBitsNew, olm->lightBits, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
+ memcpy( lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3 );
+ if ( deluxemap ) {
+ memcpy( dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3 );
+ }
+
+ for (;; )
+ {
+ filled = 0;
+ for ( y = 0; y < olm->customHeight; ++y )
+ {
+ for ( x = 0; x < olm->customWidth; ++x )
+ {
+ ofs = y * olm->customWidth + x;
+ if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
+ continue;
+ }
+ cnt = 0;
+ VectorClear( dir_sum );
+ VectorClear( light_sum );
+
+ /* try all four neighbors */
+ ofs = ( ( y + olm->customHeight - 1 ) % olm->customHeight ) * olm->customWidth + x;
+ if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
+ ++cnt;
+ VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
+ if ( deluxemap ) {
+ VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
+ }
+ }
+
+ ofs = ( ( y + 1 ) % olm->customHeight ) * olm->customWidth + x;
+ if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
+ ++cnt;
+ VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
+ if ( deluxemap ) {
+ VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
+ }
+ }
+
+ ofs = y * olm->customWidth + ( x + olm->customWidth - 1 ) % olm->customWidth;
+ if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
+ ++cnt;
+ VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
+ if ( deluxemap ) {
+ VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
+ }
+ }
+
+ ofs = y * olm->customWidth + ( x + 1 ) % olm->customWidth;
+ if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
+ ++cnt;
+ VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
+ if ( deluxemap ) {
+ VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
+ }
+ }
+
+ if ( cnt ) {
+ ++filled;
+ ofs = y * olm->customWidth + x;
+ lightBitsNew[ofs >> 3] |= ( 1 << ( ofs & 7 ) );
+ VectorScale( light_sum, 1.0 / cnt, lightBytesNew + ofs * 3 );
+ if ( deluxemap ) {
+ VectorScale( dir_sum, 1.0 / cnt, dirBytesNew + ofs * 3 );
+ }
+ }
+ }
+ }
+
+ if ( !filled ) {
+ break;
+ }
+
+ memcpy( olm->lightBits, lightBitsNew, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
+ memcpy( olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3 );
+ if ( deluxemap ) {
+ memcpy( olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3 );
+ }
+ }
+
+ free( lightBitsNew );
+ free( lightBytesNew );
+ if ( deluxemap ) {
+ free( dirBytesNew );
+ }
+}
+
+
+
+/*
+ StoreSurfaceLightmaps()
+ stores the surface lightmaps into the bsp as byte rgb triplets
+ */
+
+void StoreSurfaceLightmaps( qboolean fastAllocate, qboolean storeForReal ){
+ int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
+ int style, size, lightmapNum, lightmapNum2;
+ float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
+ vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
+ float *deluxel, *bspDeluxel, *bspDeluxel2;
+ byte *lb;
+ int numUsed, numTwins, numTwinLuxels, numStored;
+ float lmx, lmy, efficiency;
+ vec3_t color;
+ bspDrawSurface_t *ds, *parent, dsTemp;
+ surfaceInfo_t *info;
+ rawLightmap_t *lm, *lm2;
+ outLightmap_t *olm;
+ bspDrawVert_t *dv, *ydv, *dvParent;
+ char dirname[ 1024 ], filename[ 1024 ];
+ shaderInfo_t *csi;
+ char lightmapName[ 128 ];
+ const char *rgbGenValues[ 256 ];
+ const char *alphaGenValues[ 256 ];
+
+
+ /* note it */
+ Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
+
+ /* setup */
+ if ( lmCustomDir ) {
+ strcpy( dirname, lmCustomDir );
+ }
+ else
+ {
+ strcpy( dirname, source );
+ StripExtension( dirname );
+ }
+ memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
+ memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
+
+ /* -----------------------------------------------------------------
+ average the sampled luxels into the bsp luxels
+ ----------------------------------------------------------------- */
+
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "Subsampling..." );
+
+ /* walk the list of raw lightmaps */
+ numUsed = 0;
+ numTwins = 0;
+ numTwinLuxels = 0;
+ numSolidLightmaps = 0;
+ for ( i = 0; i < numRawLightmaps; i++ )
+ {
+ /* get lightmap */
+ lm = &rawLightmaps[ i ];
+
+ /* walk individual lightmaps */
+ for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* early outs */
+ if ( lm->superLuxels[ lightmapNum ] == NULL ) {
+ continue;
+ }
+
+ /* allocate bsp luxel storage */
+ if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
+ size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
+ lm->bspLuxels[ lightmapNum ] = safe_malloc0( size );
+ }
+
+ /* allocate radiosity lightmap storage */
+ if ( bounce ) {
+ size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
+ if ( lm->radLuxels[ lightmapNum ] == NULL ) {
+ lm->radLuxels[ lightmapNum ] = safe_malloc( size );
+ }
+ memset( lm->radLuxels[ lightmapNum ], 0, size );
+ }
+
+ /* average supersampled luxels */
+ for ( y = 0; y < lm->h; y++ )
+ {
+ for ( x = 0; x < lm->w; x++ )
+ {
+ /* subsample */
+ samples = 0.0f;
+ occludedSamples = 0.0f;
+ mappedSamples = 0;
+ VectorClear( sample );
+ VectorClear( occludedSample );
+ VectorClear( dirSample );
+ for ( ly = 0; ly < superSample; ly++ )
+ {
+ for ( lx = 0; lx < superSample; lx++ )
+ {
+ /* sample luxel */
+ sx = x * superSample + lx;
+ sy = y * superSample + ly;
+ luxel = SUPER_LUXEL( lightmapNum, sx, sy );
+ deluxel = SUPER_DELUXEL( sx, sy );
+ normal = SUPER_NORMAL( sx, sy );
+ cluster = SUPER_CLUSTER( sx, sy );
+
+ /* sample deluxemap */
+ if ( deluxemap && lightmapNum == 0 ) {
+ VectorAdd( dirSample, deluxel, dirSample );
+ }
+
+ /* keep track of used/occluded samples */
+ if ( *cluster != CLUSTER_UNMAPPED ) {
+ mappedSamples++;
+ }
+
+ /* handle lightmap border? */
+ if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
+ VectorSet( sample, 255.0f, 0.0f, 0.0f );
+ samples += 1.0f;
+ }
+
+ /* handle debug */
+ else if ( debug && *cluster < 0 ) {
+ if ( *cluster == CLUSTER_UNMAPPED ) {
+ VectorSet( luxel, 255, 204, 0 );
+ }
+ else if ( *cluster == CLUSTER_OCCLUDED ) {
+ VectorSet( luxel, 255, 0, 255 );
+ }
+ else if ( *cluster == CLUSTER_FLOODED ) {
+ VectorSet( luxel, 0, 32, 255 );
+ }
+ VectorAdd( occludedSample, luxel, occludedSample );
+ occludedSamples += 1.0f;
+ }
+
+ /* normal luxel handling */
+ else if ( luxel[ 3 ] > 0.0f ) {
+ /* handle lit or flooded luxels */
+ if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
+ VectorAdd( sample, luxel, sample );
+ samples += luxel[ 3 ];
+ }
+
+ /* handle occluded or unmapped luxels */
+ else
+ {
+ VectorAdd( occludedSample, luxel, occludedSample );
+ occludedSamples += luxel[ 3 ];
+ }
+
+ /* handle style debugging */
+ if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
+ VectorCopy( debugColors[ 0 ], sample );
+ samples = 1;
+ }
+ }
+ }
+ }
+
+ /* only use occluded samples if necessary */
+ if ( samples <= 0.0f ) {
+ VectorCopy( occludedSample, sample );
+ samples = occludedSamples;
+ }
+
+ /* get luxels */
+ luxel = SUPER_LUXEL( lightmapNum, x, y );
+ deluxel = SUPER_DELUXEL( x, y );
+
+ /* store light direction */
+ if ( deluxemap && lightmapNum == 0 ) {
+ VectorCopy( dirSample, deluxel );
+ }
+
+ /* store the sample back in super luxels */
+ if ( samples > 0.01f ) {
+ VectorScale( sample, ( 1.0f / samples ), luxel );
+ luxel[ 3 ] = 1.0f;
+ }
+
+ /* if any samples were mapped in any way, store ambient color */
+ else if ( mappedSamples > 0 ) {
+ if ( lightmapNum == 0 ) {
+ VectorCopy( ambientColor, luxel );
+ }
+ else{
+ VectorClear( luxel );
+ }
+ luxel[ 3 ] = 1.0f;
+ }
+
+ /* store a bogus value to be fixed later */
+ else
+ {
+ VectorClear( luxel );
+ luxel[ 3 ] = -1.0f;
+ }
+ }
+ }
+
+ /* setup */
+ lm->used = 0;
+ ClearBounds( colorMins, colorMaxs );
+
+ /* clean up and store into bsp luxels */
+ for ( y = 0; y < lm->h; y++ )
+ {
+ for ( x = 0; x < lm->w; x++ )
+ {
+ /* get luxels */
+ luxel = SUPER_LUXEL( lightmapNum, x, y );
+ deluxel = SUPER_DELUXEL( x, y );
+
+ /* copy light direction */
+ if ( deluxemap && lightmapNum == 0 ) {
+ VectorCopy( deluxel, dirSample );
+ }
+
+ /* is this a valid sample? */
+ if ( luxel[ 3 ] > 0.0f ) {
+ VectorCopy( luxel, sample );
+ samples = luxel[ 3 ];
+ numUsed++;
+ lm->used++;
+
+ /* fix negative samples */
+ for ( j = 0; j < 3; j++ )
+ {
+ if ( sample[ j ] < 0.0f ) {
+ sample[ j ] = 0.0f;
+ }
+ }
+ }
+ else
+ {
+ /* nick an average value from the neighbors */
+ VectorClear( sample );
+ VectorClear( dirSample );
+ samples = 0.0f;
+
+ /* fixme: why is this disabled?? */
+ for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
+ {
+ if ( sy < 0 || sy >= lm->h ) {
+ continue;
+ }
+
+ for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
+ {
+ if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
+ continue;
+ }
+
+ /* get neighbor's particulars */
+ luxel = SUPER_LUXEL( lightmapNum, sx, sy );
+ if ( luxel[ 3 ] < 0.0f ) {
+ continue;
+ }
+ VectorAdd( sample, luxel, sample );
+ samples += luxel[ 3 ];
+ }
+ }
+
+ /* no samples? */
+ if ( samples == 0.0f ) {
+ VectorSet( sample, -1.0f, -1.0f, -1.0f );
+ samples = 1.0f;
+ }
+ else
+ {
+ numUsed++;
+ lm->used++;
+
+ /* fix negative samples */
+ for ( j = 0; j < 3; j++ )
+ {
+ if ( sample[ j ] < 0.0f ) {
+ sample[ j ] = 0.0f;
+ }
+ }
+ }
+ }
+
+ /* scale the sample */
+ VectorScale( sample, ( 1.0f / samples ), sample );
+
+ /* store the sample in the radiosity luxels */
+ if ( bounce > 0 ) {
+ radLuxel = RAD_LUXEL( lightmapNum, x, y );
+ VectorCopy( sample, radLuxel );
+
+ /* if only storing bounced light, early out here */
+ if ( bounceOnly && !bouncing ) {
+ continue;
+ }
+ }
+
+ /* store the sample in the bsp luxels */
+ bspLuxel = BSP_LUXEL( lightmapNum, x, y );
+ bspDeluxel = BSP_DELUXEL( x, y );
+
+ VectorAdd( bspLuxel, sample, bspLuxel );
+ if ( deluxemap && lightmapNum == 0 ) {
+ VectorAdd( bspDeluxel, dirSample, bspDeluxel );
+ }
+
+ /* add color to bounds for solid checking */
+ if ( samples > 0.0f ) {
+ AddPointToBounds( bspLuxel, colorMins, colorMaxs );
+ }
+ }
+ }
+
+ /* set solid color */
+ lm->solid[ lightmapNum ] = qfalse;
+ VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
+ VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
+
+ /* nocollapse prevents solid lightmaps */
+ if ( noCollapse == qfalse ) {
+ /* check solid color */
+ VectorSubtract( colorMaxs, colorMins, sample );
+ if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
+ ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
+ /* set to solid */
+ VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
+ lm->solid[ lightmapNum ] = qtrue;
+ numSolidLightmaps++;
+ }
+
+ /* if all lightmaps aren't solid, then none of them are solid */
+ if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
+ for ( y = 0; y < MAX_LIGHTMAPS; y++ )
+ {
+ if ( lm->solid[ y ] ) {
+ numSolidLightmaps--;
+ }
+ lm->solid[ y ] = qfalse;
+ }
+ }
+ }
+
+ /* wrap bsp luxels if necessary */
+ if ( lm->wrap[ 0 ] ) {
+ for ( y = 0; y < lm->h; y++ )
+ {
+ bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
+ bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
+ VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
+ VectorScale( bspLuxel, 0.5f, bspLuxel );
+ VectorCopy( bspLuxel, bspLuxel2 );
+ if ( deluxemap && lightmapNum == 0 ) {
+ bspDeluxel = BSP_DELUXEL( 0, y );
+ bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
+ VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
+ VectorScale( bspDeluxel, 0.5f, bspDeluxel );
+ VectorCopy( bspDeluxel, bspDeluxel2 );
+ }
+ }
+ }
+ if ( lm->wrap[ 1 ] ) {
+ for ( x = 0; x < lm->w; x++ )
+ {
+ bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
+ bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
+ VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
+ VectorScale( bspLuxel, 0.5f, bspLuxel );
+ VectorCopy( bspLuxel, bspLuxel2 );
+ if ( deluxemap && lightmapNum == 0 ) {
+ bspDeluxel = BSP_DELUXEL( x, 0 );
+ bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
+ VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
+ VectorScale( bspDeluxel, 0.5f, bspDeluxel );
+ VectorCopy( bspDeluxel, bspDeluxel2 );
+ }
+ }
+ }
+ }
+ }
+
+ /* -----------------------------------------------------------------
+ convert modelspace deluxemaps to tangentspace
+ ----------------------------------------------------------------- */
+ /* note it */
+ if ( !bouncing ) {
+ if ( deluxemap && deluxemode == 1 ) {
+ vec3_t worldUp, myNormal, myTangent, myBinormal;
+ float dist;
+
+ Sys_Printf( "converting..." );
+
+ for ( i = 0; i < numRawLightmaps; i++ )
+ {
+ /* get lightmap */
+ lm = &rawLightmaps[ i ];
+
+ /* walk lightmap samples */
+ for ( y = 0; y < lm->sh; y++ )
+ {
+ for ( x = 0; x < lm->sw; x++ )
+ {
+ /* get normal and deluxel */
+ normal = SUPER_NORMAL( x, y );
+ cluster = SUPER_CLUSTER( x, y );
+ bspDeluxel = BSP_DELUXEL( x, y );
+ deluxel = SUPER_DELUXEL( x, y );
+
+ /* get normal */
+ VectorSet( myNormal, normal[0], normal[1], normal[2] );
+
+ /* get tangent vectors */
+ if ( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f ) {
+ if ( myNormal[ 2 ] == 1.0f ) {
+ VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
+ VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
+ }
+ else if ( myNormal[ 2 ] == -1.0f ) {
+ VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
+ VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
+ }
+ }
+ else
+ {
+ VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
+ CrossProduct( myNormal, worldUp, myTangent );
+ VectorNormalize( myTangent, myTangent );
+ CrossProduct( myTangent, myNormal, myBinormal );
+ VectorNormalize( myBinormal, myBinormal );
+ }
+
+ /* project onto plane */
+ dist = -DotProduct( myTangent, myNormal );
+ VectorMA( myTangent, dist, myNormal, myTangent );
+ dist = -DotProduct( myBinormal, myNormal );
+ VectorMA( myBinormal, dist, myNormal, myBinormal );
+
+ /* renormalize */
+ VectorNormalize( myTangent, myTangent );
+ VectorNormalize( myBinormal, myBinormal );
+
+ /* convert modelspace deluxel to tangentspace */
+ dirSample[0] = bspDeluxel[0];
+ dirSample[1] = bspDeluxel[1];
+ dirSample[2] = bspDeluxel[2];
+ VectorNormalize( dirSample, dirSample );
+
+ /* fix tangents to world matrix */
+ if ( myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0 ) {
+ VectorNegate( myTangent, myTangent );
+ }
+
+ /* build tangentspace vectors */
+ bspDeluxel[0] = DotProduct( dirSample, myTangent );
+ bspDeluxel[1] = DotProduct( dirSample, myBinormal );
+ bspDeluxel[2] = DotProduct( dirSample, myNormal );
+ }
+ }
+ }
+ }
+ }
+
+ /* -----------------------------------------------------------------
+ blend lightmaps
+ ----------------------------------------------------------------- */
+
+#ifdef sdfsdfwq312323
+ /* note it */
+ Sys_Printf( "blending..." );
+
+ for ( i = 0; i < numRawLightmaps; i++ )
+ {
+ vec3_t myColor;
+ float myBrightness;
+
+ /* get lightmap */
+ lm = &rawLightmaps[ i ];
+
+ /* walk individual lightmaps */
+ for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* early outs */
+ if ( lm->superLuxels[ lightmapNum ] == NULL ) {
+ continue;
+ }
+
+ /* walk lightmap samples */
+ for ( y = 0; y < lm->sh; y++ )
+ {
+ for ( x = 0; x < lm->sw; x++ )
+ {
+ /* get luxel */
+ bspLuxel = BSP_LUXEL( lightmapNum, x, y );
+
+ /* get color */
+ VectorNormalize( bspLuxel, myColor );
+ myBrightness = VectorLength( bspLuxel );
+ myBrightness *= ( 1 / 127.0f );
+ myBrightness = myBrightness * myBrightness;
+ myBrightness *= 127.0f;
+ VectorScale( myColor, myBrightness, bspLuxel );
+ }
+ }
+ }
+ }
+#endif
+
+ /* -----------------------------------------------------------------
+ collapse non-unique lightmaps
+ ----------------------------------------------------------------- */
+
+ if ( storeForReal && noCollapse == qfalse && deluxemap == qfalse ) {
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "collapsing..." );
+
+ /* set all twin refs to null */
+ for ( i = 0; i < numRawLightmaps; i++ )
+ {
+ for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
+ rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
+ rawLightmaps[ i ].numStyledTwins = 0;
+ }
+ }
+
+ /* walk the list of raw lightmaps */
+ for ( i = 0; i < numRawLightmaps; i++ )
+ {
+ /* get lightmap */
+ lm = &rawLightmaps[ i ];
+
+ /* walk lightmaps */
+ for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* early outs */
+ if ( lm->bspLuxels[ lightmapNum ] == NULL ||
+ lm->twins[ lightmapNum ] != NULL ) {
+ continue;
+ }
+
+ /* find all lightmaps that are virtually identical to this one */
+ for ( j = i + 1; j < numRawLightmaps; j++ )
+ {
+ /* get lightmap */
+ lm2 = &rawLightmaps[ j ];
+
+ /* walk lightmaps */
+ for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
+ {
+ /* early outs */
+ if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
+ lm2->twins[ lightmapNum2 ] != NULL ) {
+ continue;
+ }
+
+ /* compare them */
+ if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
+ /* merge and set twin */
+ if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
+ lm2->twins[ lightmapNum2 ] = lm;
+ lm2->twinNums[ lightmapNum2 ] = lightmapNum;
+ numTwins++;
+ numTwinLuxels += ( lm->w * lm->h );
+
+ /* count styled twins */
+ if ( lightmapNum > 0 ) {
+ lm->numStyledTwins++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* -----------------------------------------------------------------
+ sort raw lightmaps by shader
+ ----------------------------------------------------------------- */
+
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "sorting..." );
+
+ /* allocate a new sorted list */
+ if ( sortLightmaps == NULL ) {
+ sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
+ }
+
+ /* fill it out and sort it */
+ for ( i = 0; i < numRawLightmaps; i++ )
+ sortLightmaps[ i ] = i;
+ qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
+
+ /* -----------------------------------------------------------------
+ allocate output lightmaps
+ ----------------------------------------------------------------- */
+
+ if ( storeForReal ) {
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "allocating..." );
+
+ /* kill all existing output lightmaps */
+ if ( outLightmaps != NULL ) {
+ for ( i = 0; i < numOutLightmaps; i++ )
+ {
+ free( outLightmaps[ i ].lightBits );
+ free( outLightmaps[ i ].bspLightBytes );
+ }
+ free( outLightmaps );
+ outLightmaps = NULL;
+ }
+
+ numLightmapShaders = 0;
+ numOutLightmaps = 0;
+ numBSPLightmaps = 0;
+ numExtLightmaps = 0;
+
+ /* find output lightmap */
+ for ( i = 0; i < numRawLightmaps; i++ )
+ {
+ lm = &rawLightmaps[ sortLightmaps[ i ] ];
+ FindOutLightmaps( lm, fastAllocate );
+ }
+
+ /* set output numbers in twinned lightmaps */
+ for ( i = 0; i < numRawLightmaps; i++ )
+ {
+ /* get lightmap */
+ lm = &rawLightmaps[ sortLightmaps[ i ] ];
+
+ /* walk lightmaps */
+ for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* get twin */
+ lm2 = lm->twins[ lightmapNum ];
+ if ( lm2 == NULL ) {
+ continue;
+ }
+ lightmapNum2 = lm->twinNums[ lightmapNum ];
+
+ /* find output lightmap from twin */
+ lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
+ lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
+ lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
+ }
+ }
+ }
+
+ /* -----------------------------------------------------------------
+ store output lightmaps
+ ----------------------------------------------------------------- */
+
+ if ( storeForReal ) {
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "storing..." );
+
+ /* count the bsp lightmaps and allocate space */
+ if ( bspLightBytes != NULL ) {
+ free( bspLightBytes );
+ }
+ if ( numBSPLightmaps == 0 || externalLightmaps ) {
+ numBSPLightBytes = 0;
+ bspLightBytes = NULL;
+ }
+ else
+ {
+ numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
+ bspLightBytes = safe_malloc0( numBSPLightBytes );
+ }
+
+ /* walk the list of output lightmaps */
+ for ( i = 0; i < numOutLightmaps; i++ )
+ {
+ /* get output lightmap */
+ olm = &outLightmaps[ i ];
+
+ /* fill output lightmap */
+ if ( lightmapFill ) {
+ FillOutLightmap( olm );
+ }
+
+ /* is this a valid bsp lightmap? */
+ if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
+ /* copy lighting data */
+ lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
+ memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
+
+ /* copy direction data */
+ if ( deluxemap ) {
+ lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
+ memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
+ }
+ }
+
+ /* external lightmap? */
+ if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
+ /* make a directory for the lightmaps */
+ Q_mkdir( dirname );
+
+ /* set external lightmap number */
+ olm->extLightmapNum = numExtLightmaps;
+
+ /* write lightmap */
+ sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
+ Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
+ WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
+ numExtLightmaps++;
+
+ /* write deluxemap */
+ if ( deluxemap ) {
+ sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
+ Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
+ WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
+ numExtLightmaps++;
+
+ if ( debugDeluxemap ) {
+ olm->extLightmapNum++;
+ }
+ }
+ }
+ }
+
+ if ( numExtLightmaps > 0 ) {
+ Sys_FPrintf( SYS_VRB, "\n" );
+ }
+
+ /* delete unused external lightmaps */
+ for ( i = numExtLightmaps; i; i++ )
+ {
+ /* determine if file exists */
+ sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
+ if ( !FileExists( filename ) ) {
+ break;
+ }
+
+ /* delete it */
+ remove( filename );
+ }
+ }
+
+ /* -----------------------------------------------------------------
+ project the lightmaps onto the bsp surfaces
+ ----------------------------------------------------------------- */
+
+ if ( storeForReal ) {
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "projecting..." );
+
+ /* walk the list of surfaces */
+ for ( i = 0; i < numBSPDrawSurfaces; i++ )
+ {
+ /* get the surface and info */
+ ds = &bspDrawSurfaces[ i ];
+ info = &surfaceInfos[ i ];
+ lm = info->lm;
+ olm = NULL;
+
+ /* handle surfaces with identical parent */
+ if ( info->parentSurfaceNum >= 0 ) {
+ /* preserve original data and get parent */
+ parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
+ memcpy( &dsTemp, ds, sizeof( *ds ) );
+
+ /* overwrite child with parent data */
+ memcpy( ds, parent, sizeof( *ds ) );
+
+ /* restore key parts */
+ ds->fogNum = dsTemp.fogNum;
+ ds->firstVert = dsTemp.firstVert;
+ ds->firstIndex = dsTemp.firstIndex;
+ memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
+
+ /* set vertex data */
+ dv = &bspDrawVerts[ ds->firstVert ];
+ dvParent = &bspDrawVerts[ parent->firstVert ];
+ for ( j = 0; j < ds->numVerts; j++ )
+ {
+ memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
+ memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
+ }
+
+ /* skip the rest */
+ continue;
+ }
+
+ /* handle vertex lit or approximated surfaces */
+ else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
+ for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ ds->lightmapNum[ lightmapNum ] = -3;
+ ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
+ }
+ }
+
+ /* handle lightmapped surfaces */
+ else
+ {
+ /* walk lightmaps */
+ for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* set style */
+ ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
+
+ /* handle unused style */
+ if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
+ ds->lightmapNum[ lightmapNum ] = -3;
+ continue;
+ }
+
+ /* get output lightmap */
+ olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
+
+ /* set bsp lightmap number */
+ ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
+
+ /* deluxemap debugging makes the deluxemap visible */
+ if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
+ ds->lightmapNum[ lightmapNum ]++;
+ }
+
+ /* calc lightmap origin in texture space */
+ lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
+ lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
+
+ /* calc lightmap st coords */
+ dv = &bspDrawVerts[ ds->firstVert ];
+ ydv = &yDrawVerts[ ds->firstVert ];
+ for ( j = 0; j < ds->numVerts; j++ )
+ {
+ if ( lm->solid[ lightmapNum ] ) {
+ dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
+ dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
+ }
+ else
+ {
+ dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
+ dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
+ }
+ }
+ }
+ }
+
+ /* store vertex colors */
+ dv = &bspDrawVerts[ ds->firstVert ];
+ for ( j = 0; j < ds->numVerts; j++ )
+ {
+ /* walk lightmaps */
+ for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* handle unused style */
+ if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
+ VectorClear( color );
+ }
+ else
+ {
+ /* get vertex color */
+ luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
+ VectorCopy( luxel, color );
+
+ /* set minimum light */
+ if ( lightmapNum == 0 ) {
+ for ( k = 0; k < 3; k++ )
+ if ( color[ k ] < minVertexLight[ k ] ) {
+ color[ k ] = minVertexLight[ k ];
+ }
+ }
+ }
+
+ /* store to bytes */
+ if ( !info->si->noVertexLight ) {
+ ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
+ }
+ }
+ }
+
+ /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
+ if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //% info->si->styleMarker > 0 )
+ qboolean dfEqual;
+ char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
+
+
+ /* setup */
+ sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
+ dv = &bspDrawVerts[ ds->firstVert ];
+
+ /* depthFunc equal? */
+ if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
+ dfEqual = qtrue;
+ }
+ else{
+ dfEqual = qfalse;
+ }
+
+ /* generate stages for styled lightmaps */
+ for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* early out */
+ style = lm->styles[ lightmapNum ];
+ if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
+ continue;
+ }
+
+ /* get output lightmap */
+ olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
+
+ /* lightmap name */
+ if ( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
+ strcpy( lightmapName, "$lightmap" );
+ }
+ else{
+ sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
+ }
+
+ /* get rgbgen string */
+ if ( rgbGenValues[ style ] == NULL ) {
+ sprintf( key, "_style%drgbgen", style );
+ rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
+ if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
+ rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
+ }
+ }
+ rgbGen[ 0 ] = '\0';
+ if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
+ sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
+ }
+ else{
+ rgbGen[ 0 ] = '\0';
+ }
+
+ /* get alphagen string */
+ if ( alphaGenValues[ style ] == NULL ) {
+ sprintf( key, "_style%dalphagen", style );
+ alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
+ }
+ if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
+ sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
+ }
+ else{
+ alphaGen[ 0 ] = '\0';
+ }
+
+ /* calculate st offset */
+ lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
+ lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
+
+ /* create additional stage */
+ if ( lmx == 0.0f && lmy == 0.0f ) {
+ sprintf( styleStage, "\t{\n"
+ "\t\tmap %s\n" /* lightmap */
+ "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
+ "%s" /* depthFunc equal */
+ "%s" /* rgbGen */
+ "%s" /* alphaGen */
+ "\t\ttcGen lightmap\n"
+ "\t}\n",
+ lightmapName,
+ ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
+ rgbGen,
+ alphaGen );
+ }
+ else
+ {
+ sprintf( styleStage, "\t{\n"
+ "\t\tmap %s\n" /* lightmap */
+ "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
+ "%s" /* depthFunc equal */
+ "%s" /* rgbGen */
+ "%s" /* alphaGen */
+ "\t\ttcGen lightmap\n"
+ "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
+ "\t}\n",
+ lightmapName,
+ ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
+ rgbGen,
+ alphaGen,
+ lmx, lmy );
+
+ }
+
+ /* concatenate */
+ strcat( styleStages, styleStage );
+ }
+
+ /* create custom shader */
+ if ( info->si->styleMarker == 2 ) {
+ csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
+ }
+ else{
+ csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
+ }
+
+ /* emit remap command */
+ //% EmitVertexRemapShader( csi->shader, info->si->shader );
+
+ /* store it */
+ //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
+ ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
+ //% Sys_Printf( ")\n" );
+ }
+
+ /* devise a custom shader for this surface (fixme: make this work with light styles) */
+ else if ( olm != NULL && lm != NULL && !externalLightmaps &&
+ ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
+ /* get output lightmap */
+ olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
+
+ /* do some name mangling */
+ sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
+
+ /* create custom shader */
+ csi = CustomShader( info->si, "$lightmap", lightmapName );
+
+ /* store it */
+ //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
+ ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
+ //% Sys_Printf( ")\n" );
+ }
+
+ /* use the normal plain-jane shader */
+ else{
+ ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
+ }
+ }
+ }
+
+ /* finish */
+ Sys_FPrintf( SYS_VRB, "done.\n" );
+
+ /* calc num stored */
+ numStored = numBSPLightBytes / 3;
+ efficiency = ( numStored <= 0 )
+ ? 0
+ : (float) numUsed / (float) numStored;
+
+ if ( storeForReal ) {
+ /* print stats */
+ Sys_Printf( "%9d luxels used\n", numUsed );
+ Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
+ Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
+ Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
+ Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
+ Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
+ Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
+ Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
+ Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
+
+ /* write map shader file */
+ WriteMapShaderFile();
+ }
+}