/* 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 "quatlib.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 ============= */ unsigned int R_AddSurfDlighbits( msurface_t *surf, unsigned int dlightbits ) { unsigned int 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( unsigned int dlightbits, int state ) { unsigned int 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; const mat4x4_t m = { 0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 0.5, 0, 0.5, 0.5, 0.5, 1 }; GLfloat xyzFallof[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 0 } }; r_backacc.numColors = 0; // 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<origin, RI.currententity->origin, dlorigin ); if( !Matrix_Compare( RI.currententity->axis, axis_identity ) ) { VectorCopy( dlorigin, tvec ); Matrix_TransformVector( 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 * Q_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; int vi[3], elem[4]; float dot, t[8]; vec3_t vf, vf2, tdir; vec3_t ambientLocal, diffuseLocal; vec_t *gridSize, *gridMins; int *gridBounds; mgridlight_t **lightarray; VectorSet( ambientLocal, 0, 0, 0 ); VectorSet( diffuseLocal, 0, 0, 0 ); if( !r_worldmodel /* || (RI.refdef.rdflags & RDF_NOWORLDMODEL)*/ || !r_worldbrushmodel->lightgrid || !r_worldbrushmodel->numlightgridelems ) { VectorSet( dir, 0.5f, 0.2f, -1.0f ); goto dynamic; } 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 & RF_FULLBRIGHT ) || r_fullbright->value ) return; // probably weird shader, see mpteam4 for example if( !e->model || ( e->model->type == mod_brush ) ) return; R_LightForOrigin( e->lightingOrigin, temp, ambient, diffuse, 0 ); if( e->flags & EF_MINLIGHT ) { for( i = 0; i < 3; i++ ) if( ambient[i] > 0.1 ) break; if( i == 3 ) VectorSet( ambient, 0.1f, 0.1f, 0.1f ); } // rotate direction Matrix_TransformVector( 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<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 ) *Q_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 ); 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 ); } /* ============================================================================= 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 ); }