This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/vid_gl/r_light.c

1012 lines
26 KiB
C

/*
Copyright (C) 1997-2001 Id Software, Inc.
Copyright (C) 2002-2007 Victor Luchits
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_light.c
#include "r_local.h"
#include "mathlib.h"
#include "matrix_lib.h"
/*
=============================================================================
DYNAMIC LIGHTS BLEND RENDERING
=============================================================================
*/
/*
=============
R_SurfPotentiallyLit
=============
*/
bool R_SurfPotentiallyLit( msurface_t *surf )
{
ref_shader_t *shader;
if( surf->flags & ( SURF_SKY|SURF_NODLIGHT|SURF_NODRAW ) )
return false;
shader = surf->shader;
if( shader->flags & SHADER_SKYPARMS || shader->type == SHADER_FLARE || !shader->num_stages )
return false;
return ( surf->mesh && ( surf->facetype != MST_FLARE ) /* && (surf->facetype != MST_TRISURF)*/ );
}
/*
=============
R_LightBounds
=============
*/
void R_LightBounds( const vec3_t origin, float intensity, vec3_t mins, vec3_t maxs )
{
VectorSet( mins, origin[0] - intensity, origin[1] - intensity, origin[2] - intensity /* - intensity*/ );
VectorSet( maxs, origin[0] + intensity, origin[1] + intensity, origin[2] + intensity /* + intensity*/ );
}
/*
=============
R_AddSurfDlighbits
=============
*/
uint R_AddSurfDlighbits( msurface_t *surf, uint dlightbits )
{
uint k, bit;
dlight_t *lt;
float dist;
for( k = 0, bit = 1, lt = r_dlights; k < r_numDlights; k++, bit <<= 1, lt++ )
{
if( dlightbits & bit )
{
if( surf->facetype == MST_PLANAR )
{
dist = PlaneDiff( lt->origin, surf->plane );
if( dist <= -lt->intensity || dist >= lt->intensity )
dlightbits &= ~bit; // how lucky...
}
else if( surf->facetype == MST_PATCH || surf->facetype == MST_TRISURF )
{
if( !BoundsIntersect( surf->mins, surf->maxs, lt->mins, lt->maxs ))
dlightbits &= ~bit;
}
}
}
return dlightbits;
}
/*
=================
R_AddDynamicLights
=================
*/
void R_AddDynamicLights( uint dlightbits, int state )
{
uint i, j, numTempElems;
bool cullAway;
const dlight_t *light;
const ref_shader_t *shader;
vec3_t tvec, dlorigin, normal;
elem_t tempElemsArray[MAX_ARRAY_ELEMENTS];
float inverseIntensity, *v1, *v2, *v3, dist;
GLfloat xyzFallof[4][4] = {{ 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 0 }};
matrix4x4 m;
r_backacc.numColors = 0;
Matrix4x4_BuildLightIdentity( m );
// we multitexture or texture3D support for dynamic lights
if( !GL_Support( R_TEXTURE_3D_EXT ) && !GL_Support( R_ARB_MULTITEXTURE ))
return;
for( i = 0; i < (uint)( GL_Support( R_TEXTURE_3D_EXT ) ? 1 : 2 ); i++ )
{
GL_SelectTexture( i );
GL_TexEnv( GL_MODULATE );
GL_SetState( state | ( i ? 0 : GLSTATE_BLEND_MTEX ));
GL_SetTexCoordArrayMode( 0 );
GL_EnableTexGen( GL_S, GL_OBJECT_LINEAR );
GL_EnableTexGen( GL_T, GL_OBJECT_LINEAR );
GL_EnableTexGen( GL_R, 0 );
GL_EnableTexGen( GL_Q, 0 );
}
if( GL_Support( R_TEXTURE_3D_EXT ))
{
GL_EnableTexGen( GL_R, GL_OBJECT_LINEAR );
pglDisable( GL_TEXTURE_2D );
pglEnable( GL_TEXTURE_3D );
}
else
{
pglEnable( GL_TEXTURE_2D );
}
for( i = 0, light = r_dlights; i < r_numDlights; i++, light++ )
{
if(!( dlightbits & ( 1<<i )))
continue; // not lit by this light
VectorSubtract( light->origin, RI.currententity->origin, dlorigin );
if( !Matrix3x3_Compare( RI.currententity->axis, matrix3x3_identity ))
{
VectorCopy( dlorigin, tvec );
Matrix3x3_Transform( RI.currententity->axis, tvec, dlorigin );
}
shader = light->shader;
if( shader && ( shader->flags & SHADER_CULL_BACK ))
cullAway = true;
else cullAway = false;
numTempElems = 0;
if( cullAway )
{
for( j = 0; j < r_backacc.numElems; j += 3 )
{
v1 = (float *)(vertsArray + elemsArray[j+0]);
v2 = (float *)(vertsArray + elemsArray[j+1]);
v3 = (float *)(vertsArray + elemsArray[j+2]);
normal[0] = ( v1[1] - v2[1] ) * ( v3[2] - v2[2] ) - ( v1[2] - v2[2] ) * ( v3[1] - v2[1] );
normal[1] = ( v1[2] - v2[2] ) * ( v3[0] - v2[0] ) - ( v1[0] - v2[0] ) * ( v3[2] - v2[2] );
normal[2] = ( v1[0] - v2[0] ) * ( v3[1] - v2[1] ) - ( v1[1] - v2[1] ) * ( v3[0] - v2[0] );
dist = (dlorigin[0] - v1[0]) * normal[0] + (dlorigin[1] - v1[1]) * normal[1] + (dlorigin[2] - v1[2]) * normal[2];
if( dist <= 0 || dist * rsqrt( DotProduct( normal, normal )) >= light->intensity )
continue;
tempElemsArray[numTempElems++] = elemsArray[j+0];
tempElemsArray[numTempElems++] = elemsArray[j+1];
tempElemsArray[numTempElems++] = elemsArray[j+2];
}
if( !numTempElems ) continue;
}
inverseIntensity = 1 / light->intensity;
GL_Bind( 0, tr.dlightTexture );
GL_LoadTexMatrix( m );
pglColor4f( light->color[0], light->color[1], light->color[2], 255 );
xyzFallof[0][0] = inverseIntensity;
xyzFallof[0][3] = -dlorigin[0] * inverseIntensity;
pglTexGenfv( GL_S, GL_OBJECT_PLANE, xyzFallof[0] );
xyzFallof[1][1] = inverseIntensity;
xyzFallof[1][3] = -dlorigin[1] * inverseIntensity;
pglTexGenfv( GL_T, GL_OBJECT_PLANE, xyzFallof[1] );
xyzFallof[2][2] = inverseIntensity;
xyzFallof[2][3] = -dlorigin[2] * inverseIntensity;
if( GL_Support( R_TEXTURE_3D_EXT ))
{
pglTexGenfv( GL_R, GL_OBJECT_PLANE, xyzFallof[2] );
}
else
{
GL_Bind( 1, tr.dlightTexture );
GL_LoadTexMatrix( m );
pglTexGenfv( GL_S, GL_OBJECT_PLANE, xyzFallof[2] );
pglTexGenfv( GL_T, GL_OBJECT_PLANE, xyzFallof[3] );
}
if( numTempElems )
{
if( GL_Support( R_DRAW_RANGEELEMENTS_EXT ))
pglDrawRangeElementsEXT( GL_TRIANGLES, 0, r_backacc.numVerts, numTempElems, GL_UNSIGNED_INT, tempElemsArray );
else pglDrawElements( GL_TRIANGLES, numTempElems, GL_UNSIGNED_INT, tempElemsArray );
}
else
{
if( GL_Support( R_DRAW_RANGEELEMENTS_EXT ))
pglDrawRangeElementsEXT( GL_TRIANGLES, 0, r_backacc.numVerts, r_backacc.numElems, GL_UNSIGNED_INT, elemsArray );
else pglDrawElements( GL_TRIANGLES, r_backacc.numElems, GL_UNSIGNED_INT, elemsArray );
}
}
if( GL_Support( R_TEXTURE_3D_EXT ))
{
GL_EnableTexGen( GL_R, 0 );
pglDisable( GL_TEXTURE_3D );
pglEnable( GL_TEXTURE_2D );
}
else
{
pglDisable( GL_TEXTURE_2D );
GL_SelectTexture( 0 );
}
}
//===================================================================
static ref_shader_t *r_coronaShader;
/*
=================
R_InitCoronas
=================
*/
void R_InitCoronas( void )
{
r_coronaShader = R_LoadShader( "*corona", SHADER_FLARE, true, TF_NOMIPMAP|TF_NOPICMIP|TF_UNCOMPRESSED|TF_CLAMP, SHADER_INVALID );
}
/*
=================
R_DrawCoronas
=================
*/
void R_DrawCoronas( void )
{
unsigned int i;
float dist;
dlight_t *light;
meshbuffer_t *mb;
trace_t tr;
if( r_dynamiclight->integer != 2 )
return;
for( i = 0, light = r_dlights; i < r_numDlights; i++, light++ )
{
dist =
RI.vpn[0] * ( light->origin[0] - RI.viewOrigin[0] ) +
RI.vpn[1] * ( light->origin[1] - RI.viewOrigin[1] ) +
RI.vpn[2] * ( light->origin[2] - RI.viewOrigin[2] );
if( dist < 24.0f )
continue;
dist -= light->intensity;
R_TraceLine( &tr, light->origin, RI.viewOrigin, SURF_NONSOLID );
if( tr.fraction != 1.0f )
continue;
mb = R_AddMeshToList( MB_CORONA, NULL, r_coronaShader, -( (signed int)i + 1 ) );
if( mb )
mb->shaderkey |= ( bound( 1, 0x4000 - (unsigned int)dist, 0x4000 - 1 ) << 12 );
}
}
//===================================================================
/*
===============
R_LightForOrigin
===============
*/
void R_LightForOrigin( const vec3_t origin, vec3_t dir, vec4_t ambient, vec4_t diffuse, float radius )
{
int i, j;
int k, s;
float dot, t[8];
vec3_t vf, vf2, tdir;
int vi[3], elem[4];
vec3_t ambientLocal, diffuseLocal;
vec_t *gridSize, *gridMins;
int *gridBounds;
mgridlight_t **lightarray;
if( !r_worldmodel || !r_worldbrushmodel->lightgrid || !r_worldbrushmodel->numlightgridelems )
{
// get fullbright
VectorSet( ambientLocal, 255, 255, 255 );
VectorSet( diffuseLocal, 255, 255, 255 );
VectorSet( dir, 0.5f, 0.2f, -1.0f );
goto dynamic;
}
else
{
VectorSet( ambientLocal, 0, 0, 0 );
VectorSet( diffuseLocal, 0, 0, 0 );
}
lightarray = r_worldbrushmodel->lightarray;
gridSize = r_worldbrushmodel->gridSize;
gridMins = r_worldbrushmodel->gridMins;
gridBounds = r_worldbrushmodel->gridBounds;
for( i = 0; i < 3; i++ )
{
vf[i] = ( origin[i] - gridMins[i] ) / gridSize[i];
vi[i] = (int)vf[i];
vf[i] = vf[i] - floor( vf[i] );
vf2[i] = 1.0f - vf[i];
}
elem[0] = vi[2] * gridBounds[3] + vi[1] * gridBounds[0] + vi[0];
elem[1] = elem[0] + gridBounds[0];
elem[2] = elem[0] + gridBounds[3];
elem[3] = elem[2] + gridBounds[0];
for( i = 0; i < 4; i++ )
{
if( elem[i] < 0 || elem[i] >= ( r_worldbrushmodel->numlightarrayelems - 1 ))
{
VectorSet( dir, 0.5f, 0.2f, -1.0f );
goto dynamic;
}
}
t[0] = vf2[0] * vf2[1] * vf2[2];
t[1] = vf[0] * vf2[1] * vf2[2];
t[2] = vf2[0] * vf[1] * vf2[2];
t[3] = vf[0] * vf[1] * vf2[2];
t[4] = vf2[0] * vf2[1] * vf[2];
t[5] = vf[0] * vf2[1] * vf[2];
t[6] = vf2[0] * vf[1] * vf[2];
t[7] = vf[0] * vf[1] * vf[2];
VectorClear( dir );
for( i = 0; i < 4; i++ )
{
R_LatLongToNorm( lightarray[elem[i]]->direction, tdir );
VectorScale( tdir, t[i *2], tdir );
for( k = 0; k < LM_STYLES && ( s = lightarray[elem[i]]->styles[k] ) != 255; k++ )
{
dir[0] += r_lightStyles[s].rgb[0] * tdir[0];
dir[1] += r_lightStyles[s].rgb[1] * tdir[1];
dir[2] += r_lightStyles[s].rgb[2] * tdir[2];
}
R_LatLongToNorm( lightarray[elem[i] + 1]->direction, tdir );
VectorScale( tdir, t[i *2+1], tdir );
for( k = 0; k < LM_STYLES && ( s = lightarray[elem[i] + 1]->styles[k] ) != 255; k++ )
{
dir[0] += r_lightStyles[s].rgb[0] * tdir[0];
dir[1] += r_lightStyles[s].rgb[1] * tdir[1];
dir[2] += r_lightStyles[s].rgb[2] * tdir[2];
}
}
for( j = 0; j < 3; j++ )
{
if( ambient )
{
for( i = 0; i < 4; i++ )
{
for( k = 0; k < LM_STYLES; k++ )
{
if( ( s = lightarray[elem[i]]->styles[k] ) != 255 )
ambientLocal[j] += t[i*2] * lightarray[elem[i]]->ambient[k][j] * r_lightStyles[s].rgb[j];
if( ( s = lightarray[elem[i] + 1]->styles[k] ) != 255 )
ambientLocal[j] += t[i*2+1] * lightarray[elem[i] + 1]->ambient[k][j] * r_lightStyles[s].rgb[j];
}
}
}
if( diffuse || radius )
{
for( i = 0; i < 4; i++ )
{
for( k = 0; k < LM_STYLES; k++ )
{
if( ( s = lightarray[elem[i]]->styles[k] ) != 255 )
diffuseLocal[j] += t[i*2] * lightarray[elem[i]]->diffuse[k][j] * r_lightStyles[s].rgb[j];
if( ( s = lightarray[elem[i] + 1]->styles[k] ) != 255 )
diffuseLocal[j] += t[i*2+1] * lightarray[elem[i] + 1]->diffuse[k][j] * r_lightStyles[s].rgb[j];
}
}
}
}
dynamic:
// add dynamic lights
if( radius && r_dynamiclight->integer && r_numDlights )
{
unsigned int lnum;
dlight_t *dl;
float dist, dist2, add;
vec3_t direction;
bool anyDlights = false;
for( lnum = 0, dl = r_dlights; lnum < r_numDlights; lnum++, dl++ )
{
if( !BoundsAndSphereIntersect( dl->mins, dl->maxs, origin, radius ) )
continue;
VectorSubtract( dl->origin, origin, direction );
dist = VectorLength( direction );
if( !dist || dist > dl->intensity + radius )
continue;
if( !anyDlights )
{
VectorNormalizeFast( dir );
anyDlights = true;
}
add = 1.0 - (dist / (dl->intensity + radius));
dist2 = add * 0.5 / dist;
for( i = 0; i < 3; i++ )
{
dot = dl->color[i] * add;
diffuseLocal[i] += dot;
ambientLocal[i] += dot * 0.05;
dir[i] += direction[i] * dist2;
}
}
}
VectorNormalizeFast( dir );
if( ambient )
{
dot = bound( 0.0f, r_lighting_ambientscale->value, 1.0f ) * ( 1 << mapConfig.pow2MapOvrbr ) * ( 1.0 / 255.0 );
for( i = 0; i < 3; i++ )
{
ambient[i] = ambientLocal[i] * dot;
ambient[i] = bound( 0, ambient[i], 1 );
}
ambient[3] = 1.0f;
}
if( diffuse )
{
dot = bound( 0.0f, r_lighting_directedscale->value, 1.0f ) * ( 1 << mapConfig.pow2MapOvrbr ) * ( 1.0 / 255.0 );
for( i = 0; i < 3; i++ )
{
diffuse[i] = diffuseLocal[i] * dot;
diffuse[i] = bound( 0, diffuse[i], 1 );
}
diffuse[3] = 1.0f;
}
}
/*
===============
R_LightForEntity
===============
*/
void R_LightForEntity( ref_entity_t *e, byte *bArray )
{
unsigned int i, lnum;
dlight_t *dl;
unsigned int dlightbits;
float dot, dist;
vec3_t lightDirs[MAX_DLIGHTS], direction, temp;
vec4_t ambient, diffuse;
if(( e->flags & EF_FULLBRIGHT ) || r_fullbright->value )
return;
// probably weird shader, see mpteam4 for example
if( !e->model || ( e->model->type == mod_brush ) || (e->model->type == mod_world ))
return;
R_LightForOrigin( e->lightingOrigin, temp, ambient, diffuse, 0 );
if( e->flags & EF_MINLIGHT )
{
for( i = 0; i < 3; i++ )
if( ambient[i] > 0.01 )
break;
if( i == 3 )
VectorSet( ambient, 0.01f, 0.01f, 0.01f );
}
// rotate direction
Matrix3x3_Transform( e->axis, temp, direction );
// see if we are affected by dynamic lights
dlightbits = 0;
if( r_dynamiclight->integer == 1 && r_numDlights )
{
for( lnum = 0, dl = r_dlights; lnum < r_numDlights; lnum++, dl++ )
{
// translate
VectorSubtract( dl->origin, e->origin, lightDirs[lnum] );
dist = VectorLength( lightDirs[lnum] );
if( !dist || dist > dl->intensity + e->model->radius * e->scale )
continue;
dlightbits |= ( 1<<lnum );
}
}
if( !dlightbits )
{
vec3_t color;
for( i = 0; i < r_backacc.numColors; i++, bArray += 4 )
{
dot = DotProduct( normalsArray[i], direction );
if( dot <= 0 )
VectorCopy( ambient, color );
else
VectorMA( ambient, dot, diffuse, color );
bArray[0] = R_FloatToByte( color[0] );
bArray[1] = R_FloatToByte( color[1] );
bArray[2] = R_FloatToByte( color[2] );
}
}
else
{
float add, intensity8, dot, *cArray, *dir;
vec3_t dlorigin, tempColorsArray[MAX_ARRAY_VERTS];
cArray = tempColorsArray[0];
for( i = 0; i < r_backacc.numColors; i++, cArray += 3 )
{
dot = DotProduct( normalsArray[i], direction );
if( dot <= 0 )
VectorCopy( ambient, cArray );
else
VectorMA( ambient, dot, diffuse, cArray );
}
for( lnum = 0, dl = r_dlights; lnum < r_numDlights; lnum++, dl++ )
{
if( !( dlightbits & ( 1<<lnum ) ) )
continue;
// translate
dir = lightDirs[lnum];
// rotate
Matrix3x3_Transform( e->axis, dir, dlorigin );
intensity8 = dl->intensity * 8 * e->scale;
cArray = tempColorsArray[0];
for( i = 0; i < r_backacc.numColors; i++, cArray += 3 )
{
VectorSubtract( dlorigin, vertsArray[i], dir );
add = DotProduct( normalsArray[i], dir );
if( add > 0 )
{
dot = DotProduct( dir, dir );
add *= ( intensity8 / dot ) * rsqrt( dot );
VectorMA( cArray, add, dl->color, cArray );
}
}
}
cArray = tempColorsArray[0];
for( i = 0; i < r_backacc.numColors; i++, bArray += 4, cArray += 3 )
{
bArray[0] = R_FloatToByte( cArray[0] );
bArray[1] = R_FloatToByte( cArray[1] );
bArray[2] = R_FloatToByte( cArray[2] );
}
}
}
/*
=============================================================================
LIGHTMAP ALLOCATION
=============================================================================
*/
static byte *r_lightmapBuffer;
static int r_lightmapBufferSize;
static int r_numUploadedLightmaps;
static int r_maxLightmapBlockSize;
/*
=======================
R_BuildLightmap
=======================
*/
static void R_BuildLightmap( int w, int h, bool deluxe, const byte *data, byte *dest, int blockWidth )
{
int x, y, bits;
byte *rgba;
float scaled[3], maxScaled;
bool normalize;
if( !data || r_fullbright->integer )
{
if( deluxe )
{
for( y = 0; y < h; y++, dest )
memset( dest + y * blockWidth, 128, w * 4 );
}
else
{
for( y = 0; y < h; y++, dest )
memset( dest + y * blockWidth, 255, w * 4 );
}
return;
}
if( deluxe )
{
bits = 0;
normalize = false;
}
else
{
bits = mapConfig.pow2MapOvrbr;
normalize = true;
}
if( normalize )
{
for( y = 0; y < h; y++ )
{
for( x = 0, rgba = dest + y * blockWidth; x < w; x++, data += LM_BYTES, rgba += 4 )
{
scaled[0] = (float)( (int)data[0] << bits );
scaled[1] = (float)( (int)data[1] << bits );
scaled[2] = (float)( (int)data[2] << bits );
maxScaled = max( max( scaled[0], scaled[1] ), scaled[2] );
if( maxScaled > 255 )
{
maxScaled = 255.0f / maxScaled;
VectorScale( scaled, maxScaled, scaled );
}
rgba[0] = ( byte )( ( bound( 0, scaled[0], 255 ) ) );
rgba[1] = ( byte )( ( bound( 0, scaled[1], 255 ) ) );
rgba[2] = ( byte )( ( bound( 0, scaled[2], 255 ) ) );
}
}
}
else
{
for( y = 0; y < h; y++ )
{
for( x = 0, rgba = dest + y * blockWidth; x < w; x++, data += LM_BYTES, rgba += 4 )
{
rgba[0] = bound( 0, ( (int)data[0] << bits ), 255 );
rgba[1] = bound( 0, ( (int)data[1] << bits ), 255 );
rgba[2] = bound( 0, ( (int)data[2] << bits ), 255 );
}
}
}
}
/*
=======================
R_PackLightmaps
=======================
*/
static int R_PackLightmaps( int num, int w, int h, int size, int stride, bool deluxe, const char *name, const byte *data, mlightmapRect_t *rects )
{
int i, x, y, root;
byte *block;
texture_t *image;
int rectX, rectY, rectSize;
int maxX, maxY, max, xStride;
double tw, th, tx, ty;
mlightmapRect_t *rect;
rgbdata_t r_image;
char uploadName[128];
Mem_Set( &r_image, 0, sizeof( rgbdata_t ));
maxX = r_maxLightmapBlockSize / w;
maxY = r_maxLightmapBlockSize / h;
max = min( maxX, maxY );
MsgDev( D_NOTE, "Packing %i lightmap%s -> ", num, (num > 1) ? "s" : "" );
com.snprintf( uploadName, sizeof( uploadName ), "%s%i", name, r_numUploadedLightmaps );
if( !max || num == 1 || !mapConfig.lightmapsPacking /* || !r_lighting_packlightmaps->integer*/ )
{
// process as it is
R_BuildLightmap( w, h, deluxe, data, r_lightmapBuffer, w * 4 );
r_image.width = w;
r_image.height = h;
r_image.type = PF_RGB_24;
r_image.size = w * h * LM_BYTES;
r_image.flags = 0;
r_image.depth = r_image.numMips = 1;
r_image.palette = NULL;
r_image.buffer = r_lightmapBuffer;
image = R_LoadTexture( uploadName, &r_image, LM_BYTES, TF_NOPICMIP|TF_CLAMP|TF_NOMIPMAP );
tr.lightmapTextures[r_numUploadedLightmaps] = image;
if( rects )
{
rects[0].texNum = r_numUploadedLightmaps;
// this is not a real texture matrix, but who cares?
rects[0].texMatrix[0][0] = 1; rects[0].texMatrix[0][1] = 0;
rects[0].texMatrix[1][0] = 1; rects[0].texMatrix[1][1] = 0;
}
MsgDev( D_NOTE, "\n" );
r_numUploadedLightmaps++;
return 1;
}
// find the nearest square block size
root = ( int )sqrt( num );
if( root > max )
root = max;
// keep row size a power of two
for( i = 1; i < root; i <<= 1 ) ;
if( i > root )
i >>= 1;
root = i;
num -= root * root;
rectX = rectY = root;
if( maxY > maxX )
{
for(; ( num >= root ) && ( rectY < maxY ); rectY++, num -= root ) ;
// sample down if not a power of two
for( y = 1; y < rectY; y <<= 1 ) ;
if( y > rectY )
y >>= 1;
rectY = y;
}
else
{
for(; ( num >= root ) && ( rectX < maxX ); rectX++, num -= root ) ;
// sample down if not a power of two
for( x = 1; x < rectX; x <<= 1 ) ;
if( x > rectX )
x >>= 1;
rectX = x;
}
tw = 1.0 / (double)rectX;
th = 1.0 / (double)rectY;
xStride = w * 4;
rectSize = ( rectX * w ) * ( rectY * h ) * 4;
if( rectSize > r_lightmapBufferSize )
{
if( r_lightmapBuffer )
Mem_Free( r_lightmapBuffer );
r_lightmapBuffer = Mem_Alloc( r_temppool, rectSize );
Mem_Set( r_lightmapBuffer, 255, rectSize );
r_lightmapBufferSize = rectSize;
}
MsgDev( D_NOTE, "%ix%i : %ix%i\n", rectX, rectY, rectX * w, rectY * h );
block = r_lightmapBuffer;
for( y = 0, ty = 0.0, num = 0, rect = rects; y < rectY; y++, ty += th, block += rectX * xStride * h )
{
for( x = 0, tx = 0.0; x < rectX; x++, tx += tw, num++, data += size * stride )
{
R_BuildLightmap( w, h, mapConfig.deluxeMappingEnabled && ( num & 1 ) ? true : false, data, block + x * xStride, rectX * xStride );
// this is not a real texture matrix, but who cares?
if( rects )
{
rect->texMatrix[0][0] = tw; rect->texMatrix[0][1] = tx;
rect->texMatrix[1][0] = th; rect->texMatrix[1][1] = ty;
rect += stride;
}
}
}
r_image.width = rectX * w;
r_image.height = rectY * h;
r_image.type = PF_RGB_24;
r_image.size = r_image.width * r_image.height * LM_BYTES;
r_image.depth = r_image.numMips = 1;
r_image.flags = 0;
r_image.palette = NULL;
r_image.buffer = r_lightmapBuffer;
image = R_LoadTexture( uploadName, &r_image, LM_BYTES, TF_NOPICMIP|TF_UNCOMPRESSED|TF_CLAMP|TF_NOMIPMAP );
tr.lightmapTextures[r_numUploadedLightmaps] = image;
if( rects )
{
for( i = 0, rect = rects; i < num; i++, rect += stride )
rect->texNum = r_numUploadedLightmaps;
}
r_numUploadedLightmaps++;
return num;
}
/*
=======================
R_BuildLightmaps
=======================
*/
void R_BuildLightmaps( int numLightmaps, int w, int h, const byte *data, mlightmapRect_t *rects )
{
int i, j, p, m;
int numBlocks, size, stride;
const byte *lmData;
if( !mapConfig.lightmapsPacking ) size = max( w, h );
else for( size = 1; ( size < r_lighting_maxlmblocksize->integer ) && ( size < glConfig.max_2d_texture_size ); size <<= 1 );
if( mapConfig.deluxeMappingEnabled && ( ( size == w ) || ( size == h ) ) )
{
MsgDev( D_WARN, "Lightmap blocks larger than %ix%i aren't supported, deluxemaps will be disabled\n", size, size );
mapConfig.deluxeMappingEnabled = false;
}
// free old lightmaps
for( i = 0; i < r_numUploadedLightmaps; i++ )
if( tr.lightmapTextures[i] ) R_FreeImage( tr.lightmapTextures[i] );
r_maxLightmapBlockSize = size;
size = w * h * LM_BYTES;
r_lightmapBufferSize = w * h * 4;
r_lightmapBuffer = Mem_Alloc( r_temppool, r_lightmapBufferSize );
r_numUploadedLightmaps = 0;
m = 1;
lmData = data;
stride = 1;
numBlocks = numLightmaps;
if( mapConfig.deluxeMaps && !mapConfig.deluxeMappingEnabled )
{
m = 2;
stride = 2;
numLightmaps /= 2;
}
for( i = 0, j = 0; i < numBlocks; i += p * m, j += p )
p = R_PackLightmaps( numLightmaps - j, w, h, size, stride, false, "*lm", lmData + j * size * stride, &rects[i] );
if( r_lightmapBuffer )
Mem_Free( r_lightmapBuffer );
MsgDev( D_NOTE, "Packed %i lightmap blocks into %i texture%s\n", numBlocks, r_numUploadedLightmaps, (r_numUploadedLightmaps > 1) ? "s" : "" );
}
/*
=============================================================================
LIGHT STYLE MANAGEMENT
=============================================================================
*/
int r_numSuperLightStyles;
superLightStyle_t r_superLightStyles[MAX_SUPER_STYLES];
/*
=======================
R_InitLightStyles
=======================
*/
void R_InitLightStyles( void )
{
int i;
for( i = 0; i < MAX_LIGHTSTYLES; i++ )
{
r_lightStyles[i].rgb[0] = 1;
r_lightStyles[i].rgb[1] = 1;
r_lightStyles[i].rgb[2] = 1;
}
r_numSuperLightStyles = 0;
}
/*
=======================
R_AddSuperLightStyle
=======================
*/
int R_AddSuperLightStyle( const int *lightmaps, const byte *lightmapStyles, const byte *vertexStyles, mlightmapRect_t **lmRects )
{
int i, j;
superLightStyle_t *sls;
for( i = 0, sls = r_superLightStyles; i < r_numSuperLightStyles; i++, sls++ )
{
for( j = 0; j < LM_STYLES; j++ )
if( sls->lightmapNum[j] != lightmaps[j] ||
sls->lightmapStyles[j] != lightmapStyles[j] ||
sls->vertexStyles[j] != vertexStyles[j] )
break;
if( j == LM_STYLES )
return i;
}
if( r_numSuperLightStyles == MAX_SUPER_STYLES )
Host_Error( "R_AddSuperLightStyle: r_numSuperLightStyles == MAX_SUPER_STYLES\n" );
sls->features = 0;
for( j = 0; j < LM_STYLES; j++ )
{
sls->lightmapNum[j] = lightmaps[j];
sls->lightmapStyles[j] = lightmapStyles[j];
sls->vertexStyles[j] = vertexStyles[j];
if( lmRects && lmRects[j] && ( lightmaps[j] != -1 ) )
{
sls->stOffset[j][0] = lmRects[j]->texMatrix[0][0];
sls->stOffset[j][1] = lmRects[j]->texMatrix[1][0];
}
else
{
sls->stOffset[j][0] = 0;
sls->stOffset[j][0] = 0;
}
if( j )
{
if( lightmapStyles[j] != 255 )
sls->features |= ( MF_LMCOORDS << j );
if( vertexStyles[j] != 255 )
sls->features |= ( MF_COLORS << j );
}
}
return r_numSuperLightStyles++;
}
/*
=======================
R_SuperLightStylesCmp
Compare function for qsort
=======================
*/
static int R_SuperLightStylesCmp( superLightStyle_t *sls1, superLightStyle_t *sls2 )
{
int i;
for( i = 0; i < LM_STYLES; i++ )
{ // compare lightmaps
if( sls2->lightmapNum[i] > sls1->lightmapNum[i] )
return 1;
else if( sls1->lightmapNum[i] > sls2->lightmapNum[i] )
return -1;
}
for( i = 0; i < LM_STYLES; i++ )
{ // compare lightmap styles
if( sls2->lightmapStyles[i] > sls1->lightmapStyles[i] )
return 1;
else if( sls1->lightmapStyles[i] > sls2->lightmapStyles[i] )
return -1;
}
for( i = 0; i < LM_STYLES; i++ )
{ // compare vertex styles
if( sls2->vertexStyles[i] > sls1->vertexStyles[i] )
return 1;
else if( sls1->vertexStyles[i] > sls2->vertexStyles[i] )
return -1;
}
return 0; // equal
}
/*
=======================
R_SortSuperLightStyles
=======================
*/
void R_SortSuperLightStyles( void )
{
qsort( r_superLightStyles, r_numSuperLightStyles, sizeof( superLightStyle_t ), ( int ( * )( const void *, const void * ) )R_SuperLightStylesCmp );
}