xash3d-fwgs/ref_soft/r_surf.c
Gleb Mazovetskiy 5e0a0765ce Trim all trailing whitespace
The `.editorconfig` file in this repo is configured to trim all trailing
whitespace regardless of whether the line is modified.

Trims all trailing whitespace in the repository to make the codebase easier
to work with in editors that respect `.editorconfig`.

`git blame` becomes less useful on these lines but it already isn't very useful.

Commands:

```
find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+
find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+
```
2021-01-04 20:55:10 +03:00

1432 lines
32 KiB
C

/*
Copyright (C) 1997-2001 Id Software, Inc.
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_surf.c: surface-related refresh code
#include "r_local.h"
#include "mod_local.h"
drawsurf_t r_drawsurf;
uint lightleft, sourcesstep, blocksize, sourcetstep;
uint lightdelta, lightdeltastep;
uint lightright, lightleftstep, lightrightstep, blockdivshift;
unsigned blockdivmask;
void *prowdestbase;
pixel_t *pbasesource;
int surfrowbytes; // used by ASM files
unsigned *r_lightptr;
int r_stepback;
int r_lightwidth;
int r_numhblocks, r_numvblocks;
pixel_t *r_source, *r_sourcemax;
void R_DrawSurfaceBlock8_mip0 (void);
void R_DrawSurfaceBlock8_mip1 (void);
void R_DrawSurfaceBlock8_mip2 (void);
void R_DrawSurfaceBlock8_mip3 (void);
void R_DrawSurfaceBlock8_Generic (void);
void R_DrawSurfaceBlock8_World (void);
static float worldlux_s, worldlux_t;
static void (*surfmiptable[4])(void) = {
R_DrawSurfaceBlock8_mip0,
R_DrawSurfaceBlock8_mip1,
R_DrawSurfaceBlock8_mip2,
R_DrawSurfaceBlock8_mip3
};
//void R_BuildLightMap (void);
extern unsigned blocklights[10240]; // allow some very large lightmaps
float surfscale;
qboolean r_cache_thrash; // set if surface cache is thrashing
int sc_size;
surfcache_t *sc_rover, *sc_base;
static int rtable[MOD_FRAMES][MOD_FRAMES];
#if 1
static void R_BuildLightMap( void );
/*
===============
R_AddDynamicLights
===============
*/
void R_AddDynamicLights( msurface_t *surf )
{
float dist, rad, minlight;
int lnum, s, t, sd, td, smax, tmax;
float sl, tl, sacc, tacc;
vec3_t impact, origin_l;
mextrasurf_t *info = surf->info;
int sample_frac = 1.0;
float sample_size;
mtexinfo_t *tex;
dlight_t *dl;
uint *bl;
// no dlighted surfaces here
//if( !R_CountSurfaceDlights( surf )) return;
sample_size = gEngfuncs.Mod_SampleSizeForFace( surf );
smax = (info->lightextents[0] / sample_size) + 1;
tmax = (info->lightextents[1] / sample_size) + 1;
tex = surf->texinfo;
if( FBitSet( tex->flags, TEX_WORLD_LUXELS ))
{
if( surf->texinfo->faceinfo )
sample_frac = surf->texinfo->faceinfo->texture_step;
else if( FBitSet( surf->texinfo->flags, TEX_EXTRA_LIGHTMAP ))
sample_frac = LM_SAMPLE_EXTRASIZE;
else sample_frac = LM_SAMPLE_SIZE;
}
for( lnum = 0; lnum < MAX_DLIGHTS; lnum++ )
{
if( !FBitSet( surf->dlightbits, BIT( lnum )))
continue; // not lit by this light
dl = gEngfuncs.GetDynamicLight( lnum );
// transform light origin to local bmodel space
if( !tr.modelviewIdentity )
Matrix4x4_VectorITransform( RI.objectMatrix, dl->origin, origin_l );
else
VectorCopy( dl->origin, origin_l );
rad = dl->radius;
dist = PlaneDiff( origin_l, surf->plane );
rad -= fabs( dist );
// rad is now the highest intensity on the plane
minlight = dl->minlight;
if( rad < minlight )
continue;
minlight = rad - minlight;
if( surf->plane->type < 3 )
{
VectorCopy( origin_l, impact );
impact[surf->plane->type] -= dist;
}
else VectorMA( origin_l, -dist, surf->plane->normal, impact );
sl = DotProduct( impact, info->lmvecs[0] ) + info->lmvecs[0][3] - info->lightmapmins[0];
tl = DotProduct( impact, info->lmvecs[1] ) + info->lmvecs[1][3] - info->lightmapmins[1];
bl = blocklights;
for( t = 0, tacc = 0; t < tmax; t++, tacc += sample_size )
{
td = (tl - tacc) * sample_frac;
if( td < 0 ) td = -td;
for( s = 0, sacc = 0; s < smax; s++, sacc += sample_size, bl += 1 )
{
sd = (sl - sacc) * sample_frac;
if( sd < 0 ) sd = -sd;
if( sd > td ) dist = sd + (td >> 1);
else dist = td + (sd >> 1);
if( dist < minlight )
{
//printf("dlight %f\n", dist);
//*(void**)0 = 0;
bl[0] += ((int)((rad - dist) * 256) * gEngfuncs.LightToTexGamma( (dl->color.r + dl->color.g + dl->color.b ) / 3) * 3) / 256;
//bl[1] += ((int)((rad - dist) * 256) * 2.5) / 256;
//bl[2] += ((int)((rad - dist) * 256) * 2.5) / 256;
}
}
}
}
}
/*
=================
R_BuildLightmap
Combine and scale multiple lightmaps into the floating
format in r_blocklights
=================
*/
static void R_BuildLightMap( void )
{
int smax, tmax;
uint *bl, scale;
int i, map, size, s, t;
int sample_size;
msurface_t *surf = r_drawsurf.surf;
mextrasurf_t *info = surf->info;
color24 *lm;
qboolean dynamic = 0;
sample_size = gEngfuncs.Mod_SampleSizeForFace( surf );
smax = ( info->lightextents[0] / sample_size ) + 1;
tmax = ( info->lightextents[1] / sample_size ) + 1;
//smax = (surf->extents[0]>>4)+1;
//tmax = (surf->extents[1]>>4)+1;
size = smax * tmax;
if( surf->flags & SURF_CONVEYOR )
{
smax = ( info->lightextents[0] * 3 / sample_size ) + 1;
size = smax * tmax;
memset( blocklights, 0xff, sizeof( uint ) * size );
return;
}
lm = surf->samples;
memset( blocklights, 0, sizeof( uint ) * size );
// add all the lightmaps
for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ )
{
scale = tr.lightstylevalue[surf->styles[map]];
for( i = 0, bl = blocklights; i < size; i++, bl += 1, lm++ )
{
bl[0] += gEngfuncs.LightToTexGamma( lm->r ) * scale;
bl[0] += gEngfuncs.LightToTexGamma( lm->g ) * scale;
bl[0] += gEngfuncs.LightToTexGamma( lm->b ) * scale;
//printf("test\n");
//bl[1] += gEngfuncs.LightToTexGamma( lm->g ) * scale;
//bl[2] += gEngfuncs.LightToTexGamma( lm->b ) * scale;
}
}
// add all the dynamic lights
if( surf->dlightframe == tr.framecount )
R_AddDynamicLights( surf );
// Put into texture format
//stride -= (smax << 2);
//bl = blocklights;
/*for( t = 0; t < tmax; t++, dest += stride )
{
for( s = 0; s < smax; s++ )
{
dest[0] = Q_min((bl[0] >> 7), 255 );
//dest[1] = Q_min((bl[1] >> 7), 255 );
//dest[2] = Q_min((bl[2] >> 7), 255 );
//dest[3] = 255;
bl += 3;
dest += 4;
}
}*/
// bound, invert, and shift
for (i=0 ; i<size ; i++)
{
t = (int)blocklights[i];
if (t < 0)
t = 0;
if( t > 65535 * 3 )
t = 65535 * 3;
t = t / 2048 / 3;//(255*256 - t) >> (8 - VID_CBITS);
//if (t < (1 << 6))
//t = (1 << 6);
t = t << 8;
blocklights[i] = t;
}
}
#else
/*
===============
R_BuildLightMap
Combine and scale multiple lightmaps into the 8.8 format in blocklights
===============
*/
void R_BuildLightMap (void)
{
int smax, tmax;
int t;
int i, size;
byte *lightmap;
unsigned scale;
int maps;
msurface_t *surf;
surf = r_drawsurf.surf;
//smax = (surf->extents[0]>>4)+1;
//tmax = (surf->extents[1]>>4)+1;
mextrasurf_t *info = surf->info;
int sample_size = gEngfuncs.Mod_SampleSizeForFace( surf );
smax = ( info->lightextents[0] / sample_size ) + 1;
tmax = ( info->lightextents[1] / sample_size ) + 1;
size = smax*tmax;
if (r_fullbright->value )
{
for (i=0 ; i<size ; i++)
blocklights[i] = 0;
return;
}
// clear to no light
for (i=0 ; i<size ; i++)
blocklights[i] = 0;
// add all the lightmaps
lightmap = surf->samples;
if (lightmap)
for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ;
maps++)
{
scale = r_drawsurf.lightadj[maps]; // 8.8 fraction
for (i=0 ; i<size ; i++)
{
blocklights[i] += lightmap[i*3] * 2.5; // * scale;
blocklights[i] += lightmap[i*3+1] * 2.5; // * scale;
blocklights[i] += lightmap[i*3+2] * 2.5; // * scale;
}
lightmap += size; // skip to next lightmap
}
// add all the dynamic lights
//if (surf->dlightframe == r_framecount)
//R_AddDynamicLights ();
// bound, invert, and shift
/*for (i=0 ; i<size ; i++)
{
t = (int)blocklights[i];
if (t < 0)
t = 0;
t = (255*256 - t) >> (8 - VID_CBITS);
if (t < (1 << 6))
t = (1 << 6);
blocklights[i] = t;
}*/
for (i=0 ; i<size ; i++)
{
t = (int)blocklights[i];
if (t < 0)
t = 0;
if( t > 767 )
t = 767;
t = t * 31 / 256/3;//(255*256 - t) >> (8 - VID_CBITS);
//if (t < (1 << 6))
//t = (1 << 6);
t = t << 8;
blocklights[i] = t;
}
}
#endif
void R_InitRandomTable( void )
{
int tu, tv;
// make random predictable
gEngfuncs.COM_SetRandomSeed( 255 );
for( tu = 0; tu < MOD_FRAMES; tu++ )
{
for( tv = 0; tv < MOD_FRAMES; tv++ )
{
rtable[tu][tv] = gEngfuncs.COM_RandomLong( 0, 0x7FFF );
}
}
gEngfuncs.COM_SetRandomSeed( 0 );
}
/*
===============
R_TextureAnim
Returns the proper texture for a given time and base texture, do not process random tiling
===============
*/
texture_t *R_TextureAnim( texture_t *b )
{
texture_t *base = b;
int count, reletive;
if( RI.currententity->curstate.frame )
{
if( base->alternate_anims )
base = base->alternate_anims;
}
if( !base->anim_total )
return base;
if( base->name[0] == '-' )
{
return b; // already tiled
}
else
{
int speed;
// Quake1 textures uses 10 frames per second
if( FBitSet( R_GetTexture( base->gl_texturenum )->flags, TF_QUAKEPAL ))
speed = 10;
else speed = 20;
reletive = (int)(gpGlobals->time * speed) % base->anim_total;
}
count = 0;
while( base->anim_min > reletive || base->anim_max <= reletive )
{
base = base->anim_next;
if( !base || ++count > MOD_FRAMES )
return b;
}
return base;
}
/*
===============
R_TextureAnimation
Returns the proper texture for a given time and surface
===============
*/
texture_t *R_TextureAnimation( msurface_t *s )
{
texture_t *base = s->texinfo->texture;
int count, reletive;
if( RI.currententity && RI.currententity->curstate.frame )
{
if( base->alternate_anims )
base = base->alternate_anims;
}
if( !base->anim_total )
return base;
if( base->name[0] == '-' )
{
int tx = (int)((s->texturemins[0] + (base->width << 16)) / base->width) % MOD_FRAMES;
int ty = (int)((s->texturemins[1] + (base->height << 16)) / base->height) % MOD_FRAMES;
reletive = rtable[tx][ty] % base->anim_total;
}
else
{
int speed;
// Quake1 textures uses 10 frames per second
if( FBitSet( R_GetTexture( base->gl_texturenum )->flags, TF_QUAKEPAL ))
speed = 10;
else speed = 20;
reletive = (int)(gpGlobals->time * speed) % base->anim_total;
}
count = 0;
while( base->anim_min > reletive || base->anim_max <= reletive )
{
base = base->anim_next;
if( !base || ++count > MOD_FRAMES )
return s->texinfo->texture;
}
return base;
}
/*
===============
R_DrawSurface
===============
*/
void R_DrawSurface (void)
{
pixel_t *basetptr;
int smax, tmax, twidth;
int u;
int soffset, basetoffset, texwidth;
int horzblockstep;
pixel_t *pcolumndest;
void (*pblockdrawer)(void);
image_t *mt;
uint sample_size, sample_bits, sample_pot;
surfrowbytes = r_drawsurf.rowbytes;
sample_size = LM_SAMPLE_SIZE_AUTO(r_drawsurf.surf);
if( sample_size == 16 )
sample_bits = 4, sample_pot = sample_size;
else
{
sample_bits = tr.sample_bits;
if( sample_bits == -1 )
{
sample_bits = 0;
for( sample_pot = 1; sample_pot < sample_size; sample_pot <<= 1, sample_bits++ );
}
else
sample_pot = 1 << sample_bits;
}
mt = r_drawsurf.image;
r_source = mt->pixels[r_drawsurf.surfmip];
// the fractional light values should range from 0 to (VID_GRADES - 1) << 16
// from a source range of 0 - 255
texwidth = mt->width >> r_drawsurf.surfmip;
blocksize = sample_pot >> r_drawsurf.surfmip;
blockdivshift = sample_bits - r_drawsurf.surfmip;
blockdivmask = (1 << blockdivshift) - 1;
if( sample_size == 16 )
r_lightwidth = ( r_drawsurf.surf->info->lightextents[0]>>4)+1;
else
r_lightwidth = ( r_drawsurf.surf->info->lightextents[0] / sample_size ) + 1;
r_numhblocks = r_drawsurf.surfwidth >> blockdivshift;
r_numvblocks = r_drawsurf.surfheight >> blockdivshift;
//==============================
if( sample_size == 16 )
pblockdrawer = surfmiptable[r_drawsurf.surfmip];
else
pblockdrawer = R_DrawSurfaceBlock8_Generic;
// TODO: only needs to be set when there is a display settings change
horzblockstep = blocksize;
smax = mt->width >> r_drawsurf.surfmip;
twidth = texwidth;
tmax = mt->height >> r_drawsurf.surfmip;
sourcetstep = texwidth;
r_stepback = tmax * twidth;
r_sourcemax = r_source + (tmax * smax);
// glitchy and slow way to draw some lightmap
if( r_drawsurf.surf->texinfo->flags & TEX_WORLD_LUXELS )
{
worldlux_s = r_drawsurf.surf->extents[0] / r_drawsurf.surf->info->lightextents[0];
worldlux_t = r_drawsurf.surf->extents[1] / r_drawsurf.surf->info->lightextents[1];
if( worldlux_s == 0 )
worldlux_s = 1;
if( worldlux_t == 0 )
worldlux_t = 1;
soffset = r_drawsurf.surf->texturemins[0];
basetoffset = r_drawsurf.surf->texturemins[1];
//soffset = r_drawsurf.surf->info->lightmapmins[0] * worldlux_s;
//basetoffset = r_drawsurf.surf->info->lightmapmins[1] * worldlux_t;
// << 16 components are to guarantee positive values for %
soffset = ((soffset >> r_drawsurf.surfmip) + (smax << 16)) % smax;
basetptr = &r_source[((((basetoffset >> r_drawsurf.surfmip)
+ (tmax << 16)) % tmax) * twidth)];
pcolumndest = r_drawsurf.surfdat;
for (u=0 ; u<r_numhblocks; u++)
{
r_lightptr = blocklights + (int)(u/ (worldlux_s+0.5f));
prowdestbase = pcolumndest;
pbasesource = basetptr + soffset;
R_DrawSurfaceBlock8_World();
soffset = soffset + blocksize;
if (soffset >= smax)
soffset = 0;
pcolumndest += horzblockstep;
}
return;
}
soffset = r_drawsurf.surf->info->lightmapmins[0];
basetoffset = r_drawsurf.surf->info->lightmapmins[1];
// << 16 components are to guarantee positive values for %
soffset = ((soffset >> r_drawsurf.surfmip) + (smax << 16)) % smax;
basetptr = &r_source[((((basetoffset >> r_drawsurf.surfmip)
+ (tmax << 16)) % tmax) * twidth)];
pcolumndest = r_drawsurf.surfdat;
for (u=0 ; u<r_numhblocks; u++)
{
r_lightptr = blocklights + u;
prowdestbase = pcolumndest;
pbasesource = basetptr + soffset;
(*pblockdrawer)();
soffset = soffset + blocksize;
if (soffset >= smax)
soffset = 0;
pcolumndest += horzblockstep;
}
// test what if we have very slow cache building
//usleep(10000);
}
//=============================================================================
#if !id386
#define BLEND_LM(pix, light) vid.colormap[(pix >> 3) | ((light & 0x1f00) << 5)] | ( pix & 7 );
/*
================
R_DrawSurfaceBlock8_World
Does not draw lightmap correclty, but scale it correctly. Better than nothing
================
*/
void R_DrawSurfaceBlock8_World (void)
{
int v, i, b;
uint lightstep, lighttemp, light;
pixel_t pix, *psource, *prowdest;
int lightpos = 0;
psource = pbasesource;
prowdest = prowdestbase;
for (v=0 ; v<r_numvblocks ; v++)
{
// FIXME: make these locals?
// FIXME: use delta rather than both right and left, like ASM?
lightleft = r_lightptr[(lightpos/r_lightwidth) * r_lightwidth];
lightright = r_lightptr[(lightpos/r_lightwidth) * r_lightwidth+1];
lightpos += r_lightwidth / worldlux_s;
lightleftstep = (r_lightptr[(lightpos/r_lightwidth) * r_lightwidth] - lightleft) >> (4-r_drawsurf.surfmip);
lightrightstep =(r_lightptr[(lightpos/r_lightwidth) * r_lightwidth+1] - lightright) >> (4-r_drawsurf.surfmip);
for (i=0 ; i<blocksize ; i++)
{
lighttemp = lightleft - lightright;
lightstep = lighttemp >> (4-r_drawsurf.surfmip);
light = lightright;
for (b=blocksize-1; b>=0; b--)
{
//pix = psource[(uint)(b * worldlux_s)];
pix = psource[b];
prowdest[b] = BLEND_LM(pix, light);
if( pix == TRANSPARENT_COLOR )
prowdest[b] = TRANSPARENT_COLOR;
//((unsigned char *)vid.colormap)
//[(light & 0xFF00) + pix];
light += lightstep;
}
psource += sourcetstep;
lightright += lightrightstep;
lightleft += lightleftstep;
prowdest += surfrowbytes;
}
if (psource >= r_sourcemax)
psource -= r_stepback;
}
}
/*
================
R_DrawSurfaceBlock8_Generic
================
*/
void R_DrawSurfaceBlock8_Generic (void)
{
int v, i, b;
uint lightstep, lighttemp, light;
pixel_t pix, *psource, *prowdest;
psource = pbasesource;
prowdest = prowdestbase;
for (v=0 ; v<r_numvblocks ; v++)
{
// FIXME: make these locals?
// FIXME: use delta rather than both right and left, like ASM?
lightleft = r_lightptr[0];
lightright = r_lightptr[1];
r_lightptr += r_lightwidth;
lightleftstep = (r_lightptr[0] - lightleft) >> (4-r_drawsurf.surfmip);
lightrightstep = (r_lightptr[1] - lightright) >> (4-r_drawsurf.surfmip);
for (i=0 ; i<blocksize ; i++)
{
lighttemp = lightleft - lightright;
lightstep = lighttemp >> (4-r_drawsurf.surfmip);
light = lightright;
for (b=blocksize-1; b>=0; b--)
{
pix = psource[b];
prowdest[b] = BLEND_LM(pix, light);
if( pix == TRANSPARENT_COLOR )
prowdest[b] = TRANSPARENT_COLOR;
//((unsigned char *)vid.colormap)
//[(light & 0xFF00) + pix];
light += lightstep;
}
psource += sourcetstep;
lightright += lightrightstep;
lightleft += lightleftstep;
prowdest += surfrowbytes;
}
if (psource >= r_sourcemax)
psource -= r_stepback;
}
}
/*
================
R_DrawSurfaceBlock8_mip0
================
*/
void R_DrawSurfaceBlock8_mip0 (void)
{
int v, i, b;
uint lightstep, lighttemp, light;
pixel_t pix, *psource, *prowdest;
psource = pbasesource;
prowdest = prowdestbase;
for (v=0 ; v<r_numvblocks ; v++)
{
// FIXME: make these locals?
// FIXME: use delta rather than both right and left, like ASM?
lightleft = r_lightptr[0];
lightright = r_lightptr[1];
r_lightptr += r_lightwidth;
lightleftstep = (r_lightptr[0] - lightleft) >> 4;
lightrightstep = (r_lightptr[1] - lightright) >> 4;
for (i=0 ; i<16 ; i++)
{
lighttemp = lightleft - lightright;
lightstep = lighttemp >> 4;
light = lightright;
for (b=15; b>=0; b--)
{
pix = psource[b];
prowdest[b] = BLEND_LM(pix, light);
if( pix == TRANSPARENT_COLOR )
prowdest[b] = TRANSPARENT_COLOR;
// pix;
//((unsigned char *)vid.colormap)
//[(light & 0xFF00) + pix];
light += lightstep;
}
psource += sourcetstep;
lightright += lightrightstep;
lightleft += lightleftstep;
prowdest += surfrowbytes;
}
if (psource >= r_sourcemax)
psource -= r_stepback;
}
}
/*
================
R_DrawSurfaceBlock8_mip1
================
*/
void R_DrawSurfaceBlock8_mip1 (void)
{
int v, i, b;
uint lightstep, lighttemp, light;
pixel_t pix, *psource, *prowdest;
psource = pbasesource;
prowdest = prowdestbase;
for (v=0 ; v<r_numvblocks ; v++)
{
// FIXME: make these locals?
// FIXME: use delta rather than both right and left, like ASM?
lightleft = r_lightptr[0];
lightright = r_lightptr[1];
r_lightptr += r_lightwidth;
lightleftstep = (r_lightptr[0] - lightleft) >> 3;
lightrightstep = (r_lightptr[1] - lightright) >> 3;
for (i=0 ; i<8 ; i++)
{
lighttemp = lightleft - lightright;
lightstep = lighttemp >> 3;
light = lightright;
for (b=7; b>=0; b--)
{
pix = psource[b];
prowdest[b] = BLEND_LM(pix, light);
//((unsigned char *)vid.colormap)
//[(light & 0xFF00) + pix];
light += lightstep;
}
psource += sourcetstep;
lightright += lightrightstep;
lightleft += lightleftstep;
prowdest += surfrowbytes;
}
if (psource >= r_sourcemax)
psource -= r_stepback;
}
}
/*
================
R_DrawSurfaceBlock8_mip2
================
*/
void R_DrawSurfaceBlock8_mip2 (void)
{
int v, i, b;
uint lightstep, lighttemp, light;
pixel_t pix, *psource, *prowdest;
psource = pbasesource;
prowdest = prowdestbase;
for (v=0 ; v<r_numvblocks ; v++)
{
// FIXME: make these locals?
// FIXME: use delta rather than both right and left, like ASM?
lightleft = r_lightptr[0];
lightright = r_lightptr[1];
r_lightptr += r_lightwidth;
lightleftstep = (r_lightptr[0] - lightleft) >> 2;
lightrightstep = (r_lightptr[1] - lightright) >> 2;
for (i=0 ; i<4 ; i++)
{
lighttemp = lightleft - lightright;
lightstep = lighttemp >> 2;
light = lightright;
for (b=3; b>=0; b--)
{
pix = psource[b];
prowdest[b] = BLEND_LM(pix, light);;
//((unsigned char *)vid.colormap)
//[(light & 0xFF00) + pix];
light += lightstep;
}
psource += sourcetstep;
lightright += lightrightstep;
lightleft += lightleftstep;
prowdest += surfrowbytes;
}
if (psource >= r_sourcemax)
psource -= r_stepback;
}
}
/*
================
R_DrawSurfaceBlock8_mip3
================
*/
void R_DrawSurfaceBlock8_mip3 (void)
{
int v, i, b;
uint lightstep, lighttemp, light;
pixel_t pix, *psource, *prowdest;
psource = pbasesource;
prowdest = prowdestbase;
for (v=0 ; v<r_numvblocks ; v++)
{
// FIXME: make these locals?
// FIXME: use delta rather than both right and left, like ASM?
lightleft = r_lightptr[0];
lightright = r_lightptr[1];
r_lightptr += r_lightwidth;
lightleftstep = (r_lightptr[0] - lightleft) >> 1;
lightrightstep = (r_lightptr[1] - lightright) >> 1;
for (i=0 ; i<2 ; i++)
{
lighttemp = lightleft - lightright;
lightstep = lighttemp >> 1;
light = lightright;
for (b=1; b>=0; b--)
{
pix = psource[b];
prowdest[b] = BLEND_LM(pix, light);;
//((unsigned char *)vid.colormap)
//[(light & 0xFF00) + pix];
light += lightstep;
}
psource += sourcetstep;
lightright += lightrightstep;
lightleft += lightleftstep;
prowdest += surfrowbytes;
}
if (psource >= r_sourcemax)
psource -= r_stepback;
}
}
#endif
//============================================================================
/*
================
R_InitCaches
================
*/
void R_InitCaches (void)
{
int size;
int pix;
// calculate size to allocate
if (sw_surfcacheoverride->value)
{
size = sw_surfcacheoverride->value;
}
else
{
size = SURFCACHE_SIZE_AT_320X240 * 2;
pix = vid.width * vid.height * 2;
if (pix > 64000)
size += (pix-64000)*3;
}
// round up to page size
size = (size + 8191) & ~8191;
gEngfuncs.Con_Printf ("%s surface cache\n", Q_memprint(size));
sc_size = size;
if( sc_base )
{
D_FlushCaches( );
Mem_Free( sc_base );
}
sc_base = (surfcache_t *)Mem_Calloc(r_temppool,size);
sc_rover = sc_base;
sc_base->next = NULL;
sc_base->owner = NULL;
sc_base->size = sc_size;
}
/*
==================
D_FlushCaches
==================
*/
void D_FlushCaches( void )
{
surfcache_t *c;
// if newmap, surfaces already freed
if( !tr.map_unload )
{
for(c = sc_base ; c ; c = c->next )
{
if ( c->owner )
*c->owner = NULL;
}
}
sc_rover = sc_base;
sc_base->next = NULL;
sc_base->owner = NULL;
sc_base->size = sc_size;
}
/*
=================
D_SCAlloc
=================
*/
surfcache_t *D_SCAlloc (int width, int size)
{
surfcache_t *new;
qboolean wrapped_this_time;
if ((width < 0) )// || (width > 256))
gEngfuncs.Host_Error ("D_SCAlloc: bad cache width %d\n", width);
if ((size <= 0) || (size > 0x10000000))
gEngfuncs.Host_Error ("D_SCAlloc: bad cache size %d\n", size);
size = (int)&((surfcache_t *)0)->data[size];
size = (size + 3) & ~3;
if (size > sc_size)
gEngfuncs.Host_Error ("D_SCAlloc: %i > cache size of %i",size, sc_size);
// if there is not size bytes after the rover, reset to the start
wrapped_this_time = false;
if ( !sc_rover || (byte *)sc_rover - (byte *)sc_base > sc_size - size)
{
if (sc_rover)
{
wrapped_this_time = true;
}
sc_rover = sc_base;
}
// colect and free surfcache_t blocks until the rover block is large enough
new = sc_rover;
if (sc_rover->owner)
*sc_rover->owner = NULL;
while (new->size < size)
{
// free another
sc_rover = sc_rover->next;
if (!sc_rover)
gEngfuncs.Host_Error ("D_SCAlloc: hit the end of memory");
if (sc_rover->owner)
*sc_rover->owner = NULL;
new->size += sc_rover->size;
new->next = sc_rover->next;
}
// create a fragment out of any leftovers
if (new->size - size > 256)
{
sc_rover = (surfcache_t *)( (byte *)new + size);
sc_rover->size = new->size - size;
sc_rover->next = new->next;
sc_rover->width = 0;
sc_rover->owner = NULL;
new->next = sc_rover;
new->size = size;
}
else
sc_rover = new->next;
new->width = width;
// DEBUG
if (width > 0)
new->height = (size - sizeof(*new) + sizeof(new->data)) / width;
new->owner = NULL; // should be set properly after return
if (d_roverwrapped)
{
if (wrapped_this_time || (sc_rover >= d_initial_rover))
r_cache_thrash = true;
}
else if (wrapped_this_time)
{
d_roverwrapped = true;
}
return new;
}
/*
=================
D_SCDump
=================
*/
void D_SCDump (void)
{
surfcache_t *test;
for (test = sc_base ; test ; test = test->next)
{
if (test == sc_rover)
gEngfuncs.Con_Printf ("ROVER:\n");
gEngfuncs.Con_Printf ("%p : %i bytes %i width\n",test, test->size, test->width);
}
}
//=============================================================================
// if the num is not a power of 2, assume it will not repeat
int MaskForNum (int num)
{
if (num==128)
return 127;
if (num==64)
return 63;
if (num==32)
return 31;
if (num==16)
return 15;
return 255;
}
int D_log2 (int num)
{
int c;
c = 0;
while (num>>=1)
c++;
return c;
}
//=============================================================================
void R_DecalComputeBasis( msurface_t *surf, int flags, vec3_t textureSpaceBasis[3] );
void R_DrawSurfaceDecals( void )
{
msurface_t *fa = r_drawsurf.surf;
decal_t *p;
for( p = fa->pdecals; p; p = p->pnext)
{
pixel_t *dest, *source;
vec4_t textureU, textureV;
image_t *tex = R_GetTexture( p->texture );
int s1 = 0,t1 = 0, s2 = tex->width, t2 = tex->height;
unsigned int height;
unsigned int f, fstep;
int skip;
pixel_t *buffer;
qboolean transparent;
int x, y, u,v, sv, w, h;
vec3_t basis[3];
Vector4Copy( fa->texinfo->vecs[0], textureU );
Vector4Copy( fa->texinfo->vecs[1], textureV );
R_DecalComputeBasis( fa, 0, basis );
w = fabs( tex->width * DotProduct( textureU, basis[0] )) +
fabs( tex->height * DotProduct( textureU, basis[1] ));
h = fabs( tex->width * DotProduct( textureV, basis[0] )) +
fabs( tex->height * DotProduct( textureV, basis[1] ));
// project decal center into the texture space of the surface
x = DotProduct( p->position, textureU ) + textureU[3] - fa->texturemins[0] - w/2;
y = DotProduct( p->position, textureV ) + textureV[3] - fa->texturemins[1] - h/2;
x = x >> r_drawsurf.surfmip;
y = y >> r_drawsurf.surfmip;
w = w >> r_drawsurf.surfmip;
h = h >> r_drawsurf.surfmip;
if( w < 1 || h < 1 )
continue;
if( x < 0 )
{
s1 += (-x)*(s2-s1) / w;
x = 0;
}
if( x + w > r_drawsurf.surfwidth )
{
s2 -= (x + w - r_drawsurf.surfwidth) * (s2 - s1)/ w ;
w = r_drawsurf.surfwidth - x;
}
if( y + h > r_drawsurf.surfheight )
{
t2 -= (y + h - r_drawsurf.surfheight) * (t2 - t1) / h;
h = r_drawsurf.surfheight - y;
}
if( s1 < 0 )
s1 = 0;
if( t1 < 0 )
t1 = 0;
if( s2 > tex->width )
s2 = tex->width;
if( t2 > tex->height )
t2 = tex->height;
if( !tex->pixels[0] || s1 >= s2 || t1 >= t2 || !w )
continue;
if( tex->alpha_pixels )
{
buffer = tex->alpha_pixels;
transparent = true;
}
else
buffer = tex->pixels[0];
height = h;
if (y < 0)
{
skip = -y;
height += y;
y = 0;
}
else
skip = 0;
dest = ((pixel_t*)r_drawsurf.surfdat) + y * r_drawsurf.rowbytes + x;
for (v=0 ; v<height ; v++)
{
//int alpha1 = vid.alpha;
sv = (skip + v)*(t2-t1)/h + t1;
source = buffer + sv*tex->width + s1;
{
f = 0;
fstep = (s2-s1)*0x10000/w;
if( w == s2 - s1 )
fstep = 0x10000;
for (u=0 ; u<w ; u++)
{
pixel_t src = source[f>>16];
int alpha = 7;
f += fstep;
if( transparent )
{
alpha &= src >> (16 - 3);
src = src << 3;
}
if( alpha <= 0 )
continue;
if( alpha < 7) // && (vid.rendermode == kRenderTransAlpha || vid.rendermode == kRenderTransTexture ) )
{
pixel_t screen = dest[u]; // | 0xff & screen & src ;
if( screen == TRANSPARENT_COLOR )
continue;
dest[u] = BLEND_ALPHA( alpha, src, screen);
}
else
dest[u] = src;
}
}
dest += r_drawsurf.rowbytes;
}
}
}
/*
================
D_CacheSurface
================
*/
surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel)
{
surfcache_t *cache;
int maps;
//
// if the surface is animating or flashing, flush the cache
//
r_drawsurf.image = R_GetTexture(R_TextureAnimation (surface)->gl_texturenum);
// does not support conveyors with world luxels now
if( surface->texinfo->flags & TEX_WORLD_LUXELS )
surface->flags &= ~SURF_CONVEYOR;
if( surface->flags & SURF_CONVEYOR)
{
if( miplevel >= 1)
{
surface->extents[0] = surface->info->lightextents[0] * LM_SAMPLE_SIZE_AUTO( r_drawsurf.surf ) * 2 ;
surface->info->lightmapmins[0] = -surface->info->lightextents[0] * LM_SAMPLE_SIZE_AUTO( r_drawsurf.surf );
}
else
{
surface->extents[0] = surface->info->lightextents[0] * LM_SAMPLE_SIZE_AUTO( r_drawsurf.surf ) ;
surface->info->lightmapmins[0] = -surface->info->lightextents[0] * LM_SAMPLE_SIZE_AUTO( r_drawsurf.surf )/2;
}
}
/// todo: port this
//r_drawsurf.lightadj[0] = r_newrefdef.lightstyles[surface->styles[0]].white*128;
//r_drawsurf.lightadj[1] = r_newrefdef.lightstyles[surface->styles[1]].white*128;
//r_drawsurf.lightadj[2] = r_newrefdef.lightstyles[surface->styles[2]].white*128;
//r_drawsurf.lightadj[3] = r_newrefdef.lightstyles[surface->styles[3]].white*128;
//
// see if the cache holds apropriate data
//
cache = CACHESPOT(surface)[miplevel];
// check for lightmap modification
for( maps = 0; maps < MAXLIGHTMAPS && surface->styles[maps] != 255; maps++ )
{
if( tr.lightstylevalue[surface->styles[maps]] != surface->cached_light[maps] )
{
surface->dlightframe = tr.framecount;
}
}
if (cache && !cache->dlight && surface->dlightframe != tr.framecount
&& cache->image == r_drawsurf.image
&& cache->lightadj[0] == r_drawsurf.lightadj[0]
&& cache->lightadj[1] == r_drawsurf.lightadj[1]
&& cache->lightadj[2] == r_drawsurf.lightadj[2]
&& cache->lightadj[3] == r_drawsurf.lightadj[3] )
return cache;
if( surface->dlightframe == tr.framecount )
{
int i;
// invalidate dlight cache
for( i = 0; i < 4; i++)
{
if( CACHESPOT(surface)[i] )
CACHESPOT(surface)[i]->image = NULL;
}
}
//
// determine shape of surface
//
surfscale = 1.0 / (1<<miplevel);
r_drawsurf.surfmip = miplevel;
if( surface->flags & SURF_CONVEYOR )
r_drawsurf.surfwidth = surface->extents[0] >> miplevel;
else
r_drawsurf.surfwidth = surface->info->lightextents[0] >> miplevel;
r_drawsurf.rowbytes = r_drawsurf.surfwidth;
r_drawsurf.surfheight = surface->info->lightextents[1] >> miplevel;
// use texture space if world luxels used
if( surface->texinfo->flags & TEX_WORLD_LUXELS )
{
r_drawsurf.surfwidth = surface->extents[0] >> miplevel;
r_drawsurf.rowbytes = r_drawsurf.surfwidth;
r_drawsurf.surfheight = surface->extents[1] >> miplevel;
}
//
// allocate memory if needed
//
if (!cache) // if a texture just animated, don't reallocate it
{
cache = D_SCAlloc (r_drawsurf.surfwidth,
r_drawsurf.surfwidth * r_drawsurf.surfheight * 2);
CACHESPOT(surface)[miplevel] = cache;
cache->owner = &CACHESPOT(surface)[miplevel];
cache->mipscale = surfscale;
}
if (surface->dlightframe == tr.framecount)
cache->dlight = 1;
else
cache->dlight = 0;
r_drawsurf.surfdat = (pixel_t *)cache->data;
cache->image = r_drawsurf.image;
cache->lightadj[0] = r_drawsurf.lightadj[0];
cache->lightadj[1] = r_drawsurf.lightadj[1];
cache->lightadj[2] = r_drawsurf.lightadj[2];
cache->lightadj[3] = r_drawsurf.lightadj[3];
for( maps = 0; maps < MAXLIGHTMAPS && surface->styles[maps] != 255; maps++ )
{
surface->cached_light[maps] = tr.lightstylevalue[surface->styles[maps]];
}
//
// draw and light the surface texture
//
r_drawsurf.surf = surface;
//c_surf++;
// calculate the lightings
R_BuildLightMap ( );
// rasterize the surface into the cache
R_DrawSurface ();
R_DrawSurfaceDecals();
return cache;
}