mirror of
https://github.com/UberGames/GtkRadiant.git
synced 2024-11-26 13:51:38 +00:00
ab3a99dbbe
git-svn-id: svn://svn.icculus.org/gtkradiant/GtkRadiant/branches/ZeroRadiant.ab@184 8a3a26a2-13c4-0310-b231-cf6edde360e5
2997 lines
80 KiB
C
2997 lines
80 KiB
C
/*
|
|
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"
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------------
|
|
|
|
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 );
|
|
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_Printf( "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 += (LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3) )
|
|
{
|
|
/* write a tga image out */
|
|
sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
|
|
Sys_Printf( "Writing %s\n", filename );
|
|
WriteTGA24( filename, lightmap, LIGHTMAP_WIDTH, LIGHTMAP_HEIGHT, qfalse );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
ExportLightmapsMain()
|
|
exports the lightmaps as a list of numbered tga images
|
|
*/
|
|
|
|
int ExportLightmapsMain( int argc, char **argv )
|
|
{
|
|
/* arg checking */
|
|
if( argc < 1 )
|
|
{
|
|
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 < 1 )
|
|
{
|
|
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 += (LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 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_Printf( "WARNING: Unable to load image %s\n", filename );
|
|
continue;
|
|
}
|
|
|
|
/* parse file into an image */
|
|
pixels = NULL;
|
|
LoadTGABuffer( buffer, &pixels, &width, &height );
|
|
free( buffer );
|
|
|
|
/* sanity check it */
|
|
if( pixels == NULL )
|
|
{
|
|
Sys_Printf( "WARNING: Unable to load image %s\n", filename );
|
|
continue;
|
|
}
|
|
if( width != LIGHTMAP_WIDTH || height != LIGHTMAP_HEIGHT )
|
|
Sys_Printf( "WARNING: Image %s is not the right size (%d, %d) != (%d, %d)\n",
|
|
filename, width, height, LIGHTMAP_WIDTH, LIGHTMAP_HEIGHT );
|
|
|
|
/* copy the pixels */
|
|
in = pixels;
|
|
for( y = 1; y <= LIGHTMAP_HEIGHT; y++ )
|
|
{
|
|
out = lightmap + ((LIGHTMAP_HEIGHT - y) * LIGHTMAP_WIDTH * 3);
|
|
for( x = 0; x < LIGHTMAP_WIDTH; 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[ *((int*) a) ].si;
|
|
bsi = surfaceInfos[ *((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 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 = ceil( length / lm->sampleSize ) + 1;
|
|
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 = ceil( length / lm->sampleSize ) + 1;
|
|
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, *info2;
|
|
int num2, n, i, axisNum;
|
|
float s, t, d, len, sampleSize;
|
|
vec3_t mins, maxs, origin, faxis, size, exactSize, 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->lmGamma != lm->gamma ||
|
|
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++ )
|
|
{
|
|
exactSize[ i ] = lm->maxs[ i ] - lm->mins[ 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 )
|
|
{
|
|
i = -1;
|
|
sampleSize += 1.0f;
|
|
}
|
|
}
|
|
|
|
/* 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_Printf( "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 ];
|
|
info2 = &surfaceInfos[ 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 ];
|
|
info2 = &surfaceInfos[ 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_malloc( 3 * sizeof( vec3_t ) );
|
|
memset( lm->vecs, 0, 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[ *((int*) a) ];
|
|
bInfo = &surfaceInfos[ *((int*) b) ];
|
|
|
|
/* model first */
|
|
if( aInfo->model < bInfo->model )
|
|
return 1;
|
|
else if( aInfo->model > bInfo->model )
|
|
return -1;
|
|
|
|
/* then lightmap status */
|
|
if( aInfo->hasLightmap < bInfo->hasLightmap )
|
|
return 1;
|
|
else if( aInfo->hasLightmap > bInfo->hasLightmap )
|
|
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, *ds2;
|
|
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_Printf( "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_malloc( maxSurfaceClusters * sizeof( *surfaceClusters ) );
|
|
memset( surfaceClusters, 0, maxSurfaceClusters * sizeof( *surfaceClusters ) );
|
|
|
|
/* allocate a list for per-surface info */
|
|
surfaceInfos = safe_malloc( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
|
|
memset( surfaceInfos, 0, numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
|
|
for( i = 0; i < numBSPDrawSurfaces; i++ )
|
|
surfaceInfos[ i ].childSurfaceNum = -1;
|
|
|
|
/* allocate a list of surface indexes to be sorted */
|
|
sortSurfaces = safe_malloc( numBSPDrawSurfaces * sizeof( int ) );
|
|
memset( sortSurfaces, 0, 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->model = model;
|
|
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_malloc( numSurfsLightmapped * sizeof( int ) );
|
|
memset( lightSurfaces, 0, numSurfsLightmapped * sizeof( int ) );
|
|
|
|
/* allocate a list of raw lightmaps */
|
|
numRawSuperLuxels = 0;
|
|
numRawLightmaps = 0;
|
|
rawLightmaps = safe_malloc( numSurfsLightmapped * sizeof( *rawLightmaps ) );
|
|
memset( rawLightmaps, 0, 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;
|
|
lm->sampleSize = info->sampleSize;
|
|
lm->actualSampleSize = info->sampleSize;
|
|
lm->entityNum = info->entityNum;
|
|
lm->recvShadows = info->recvShadows;
|
|
lm->gamma = info->si->lmGamma;
|
|
lm->filterRadius = info->si->lmFilterRadius;
|
|
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 ];
|
|
ds2 = &bspDrawSurfaces[ num2 ];
|
|
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_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
|
|
memset( vertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
|
|
radVertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
|
|
memset( radVertexLuxels[ k ], 0, 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, *luxels[ MAX_STITCH_LUXELS ];
|
|
|
|
|
|
/* 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 );
|
|
luxels[ numLuxels++ ] = luxel2;
|
|
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 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;
|
|
|
|
/* compare */
|
|
if( a->w != b->w || a->h != b->h ||
|
|
a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
|
|
a->gamma != b->gamma ||
|
|
a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )
|
|
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 void MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )
|
|
{
|
|
rawLightmap_t *lm;
|
|
int x, y;
|
|
float luxel[ 3 ], *aLuxel, *bLuxel;
|
|
|
|
|
|
/* compare */
|
|
if( a->w != b->w || a->h != b->h ||
|
|
a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
|
|
a->gamma != b->gamma ||
|
|
a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )
|
|
return;
|
|
|
|
/* 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 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
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 ];
|
|
|
|
/* bail if lightmap doesn't match up */
|
|
if( info->lm != lm )
|
|
continue;
|
|
|
|
/* assume reduced initially */
|
|
info->approximated = qtrue;
|
|
|
|
/* 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) )
|
|
{
|
|
numSurfsVertexForced++;
|
|
continue;
|
|
}
|
|
|
|
/* handle the triangles */
|
|
switch( ds->surfaceType )
|
|
{
|
|
case MST_PLANAR:
|
|
/* get verts */
|
|
verts = yDrawVerts + ds->firstVert;
|
|
|
|
/* map the triangles */
|
|
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 */
|
|
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;
|
|
|
|
/* 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 == LIGHTMAP_WIDTH && lm->customHeight == LIGHTMAP_HEIGHT) || 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_malloc( (olm->customWidth * olm->customHeight / 8) + 8 );
|
|
memset( olm->lightBits, 0, (olm->customWidth * olm->customHeight / 8) + 8 );
|
|
olm->bspLightBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
|
|
memset( olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3 );
|
|
if( deluxemap )
|
|
{
|
|
olm->bspDirBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
|
|
memset( olm->bspDirBytes, 0, olm->customWidth * olm->customHeight * 3 );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
FindOutLightmaps()
|
|
for a given surface lightmap, find output lightmap pages and positions for it
|
|
*/
|
|
|
|
static void FindOutLightmaps( rawLightmap_t *lm )
|
|
{
|
|
int i, j, lightmapNum, xMax, yMax, x, y, sx, sy, ox, oy, offset, temp;
|
|
outLightmap_t *olm;
|
|
surfaceInfo_t *info;
|
|
float *luxel, *deluxel;
|
|
vec3_t color, direction;
|
|
byte *pixel;
|
|
qboolean ok;
|
|
|
|
|
|
/* 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 */
|
|
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;
|
|
|
|
/* set maxs */
|
|
xMax = (olm->customWidth - lm->w) + 1;
|
|
yMax = (olm->customHeight - lm->h) + 1;
|
|
|
|
/* walk the origin around the lightmap */
|
|
for( y = 0; y < yMax; y++ )
|
|
{
|
|
for( x = 0; x < xMax; x++ )
|
|
{
|
|
/* 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 two new output lightmaps */
|
|
numOutLightmaps += 2;
|
|
olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
|
|
if( outLightmaps != NULL && numOutLightmaps > 2 )
|
|
{
|
|
memcpy( olm, outLightmaps, (numOutLightmaps - 2) * sizeof( outLightmap_t ) );
|
|
free( outLightmaps );
|
|
}
|
|
outLightmaps = olm;
|
|
|
|
/* initialize both out lightmaps */
|
|
SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 2 ] );
|
|
SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 1 ] );
|
|
|
|
/* set out lightmap */
|
|
i = numOutLightmaps - 2;
|
|
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 )
|
|
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++;
|
|
}
|
|
}
|
|
|
|
/* mark the bits used */
|
|
for( y = 0; y < lm->h; y++ )
|
|
{
|
|
for( x = 0; x < lm->w; x++ )
|
|
{
|
|
/* get luxel */
|
|
luxel = BSP_LUXEL( lightmapNum, x, y );
|
|
deluxel = BSP_DELUXEL( x, y );
|
|
if( luxel[ 0 ] < 0.0f )
|
|
continue;
|
|
|
|
/* set minimum light */
|
|
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->gamma );
|
|
|
|
/* store direction */
|
|
if( deluxemap )
|
|
{
|
|
/* normalize average light direction */
|
|
if( VectorNormalize( deluxel, direction ) )
|
|
{
|
|
/* encode [-1,1] in [0,255] */
|
|
pixel = olm->bspDirBytes + (((oy * olm->customWidth) + ox) * 3);
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
temp = (direction[ i ] + 1.0f) * 127.5f;
|
|
if( temp < 0 )
|
|
pixel[ i ] = 0;
|
|
else if( temp > 255 )
|
|
pixel[ i ] = 255;
|
|
else
|
|
pixel[ i ] = temp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
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[ *((int*) a) ];
|
|
blm = &rawLightmaps[ *((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;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
StoreSurfaceLightmaps()
|
|
stores the surface lightmaps into the bsp as byte rgb triplets
|
|
*/
|
|
|
|
void StoreSurfaceLightmaps( void )
|
|
{
|
|
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;
|
|
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 ];
|
|
char *rgbGenValues[ 256 ];
|
|
char *alphaGenValues[ 256 ];
|
|
|
|
|
|
/* note it */
|
|
Sys_Printf( "--- StoreSurfaceLightmaps ---\n");
|
|
|
|
/* setup */
|
|
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;
|
|
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_malloc( size );
|
|
memset( lm->bspLuxels[ lightmapNum ], 0, 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* clean up and store into bsp luxels */
|
|
lm->used = 0;
|
|
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 );
|
|
}
|
|
}
|
|
|
|
/* 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 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -----------------------------------------------------------------
|
|
collapse non-unique lightmaps
|
|
----------------------------------------------------------------- */
|
|
|
|
if( 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 */
|
|
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
|
|
----------------------------------------------------------------- */
|
|
|
|
/* 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 );
|
|
}
|
|
|
|
/* 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
|
|
----------------------------------------------------------------- */
|
|
|
|
/* 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 * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3);
|
|
bspLightBytes = safe_malloc( numBSPLightBytes );
|
|
memset( bspLightBytes, 0, numBSPLightBytes );
|
|
}
|
|
|
|
/* walk the list of output lightmaps */
|
|
for( i = 0; i < numOutLightmaps; i++ )
|
|
{
|
|
/* get output lightmap */
|
|
olm = &outLightmaps[ i ];
|
|
|
|
/* is this a valid bsp lightmap? */
|
|
if( olm->lightmapNum >= 0 && !externalLightmaps )
|
|
{
|
|
/* copy lighting data */
|
|
lb = bspLightBytes + (olm->lightmapNum * LIGHTMAP_HEIGHT * LIGHTMAP_WIDTH * 3);
|
|
memcpy( lb, olm->bspLightBytes, LIGHTMAP_HEIGHT * LIGHTMAP_WIDTH * 3 );
|
|
|
|
/* copy direction data */
|
|
if( deluxemap )
|
|
{
|
|
lb = bspLightBytes + ((olm->lightmapNum + 1) * LIGHTMAP_HEIGHT * LIGHTMAP_WIDTH * 3);
|
|
memcpy( lb, olm->bspDirBytes, LIGHTMAP_HEIGHT * LIGHTMAP_WIDTH * 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
|
|
----------------------------------------------------------------- */
|
|
|
|
/* 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 and store lighting values */
|
|
dv = &bspDrawVerts[ ds->firstVert ];
|
|
ydv = &yDrawVerts[ ds->firstVert ];
|
|
for( j = 0; j < ds->numVerts; j++ )
|
|
{
|
|
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 */
|
|
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 ] = (char*) 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 ] = (char*) 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 != LIGHTMAP_WIDTH || olm->customHeight != LIGHTMAP_HEIGHT) )
|
|
{
|
|
/* 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;
|
|
|
|
/* 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 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();
|
|
}
|
|
|
|
|
|
|
|
|