Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

r_surf.c (14181B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 // r_surf.c: surface-related refresh code
     21 
     22 #include "r_local.h"
     23 
     24 drawsurf_t	r_drawsurf;
     25 
     26 int				lightleft, sourcesstep, blocksize, sourcetstep;
     27 int				lightdelta, lightdeltastep;
     28 int				lightright, lightleftstep, lightrightstep, blockdivshift;
     29 unsigned		blockdivmask;
     30 void			*prowdestbase;
     31 unsigned char	*pbasesource;
     32 int				surfrowbytes;	// used by ASM files
     33 unsigned		*r_lightptr;
     34 int				r_stepback;
     35 int				r_lightwidth;
     36 int				r_numhblocks, r_numvblocks;
     37 unsigned char	*r_source, *r_sourcemax;
     38 
     39 void R_DrawSurfaceBlock8_mip0 (void);
     40 void R_DrawSurfaceBlock8_mip1 (void);
     41 void R_DrawSurfaceBlock8_mip2 (void);
     42 void R_DrawSurfaceBlock8_mip3 (void);
     43 
     44 static void	(*surfmiptable[4])(void) = {
     45 	R_DrawSurfaceBlock8_mip0,
     46 	R_DrawSurfaceBlock8_mip1,
     47 	R_DrawSurfaceBlock8_mip2,
     48 	R_DrawSurfaceBlock8_mip3
     49 };
     50 
     51 void R_BuildLightMap (void);
     52 extern	unsigned		blocklights[1024];	// allow some very large lightmaps
     53 
     54 float           surfscale;
     55 qboolean        r_cache_thrash;         // set if surface cache is thrashing
     56 
     57 int         sc_size;
     58 surfcache_t	*sc_rover, *sc_base;
     59 
     60 /*
     61 ===============
     62 R_TextureAnimation
     63 
     64 Returns the proper texture for a given time and base texture
     65 ===============
     66 */
     67 image_t *R_TextureAnimation (mtexinfo_t *tex)
     68 {
     69 	int		c;
     70 
     71 	if (!tex->next)
     72 		return tex->image;
     73 
     74 	c = currententity->frame % tex->numframes;
     75 	while (c)
     76 	{
     77 		tex = tex->next;
     78 		c--;
     79 	}
     80 
     81 	return tex->image;
     82 }
     83 
     84 
     85 /*
     86 ===============
     87 R_DrawSurface
     88 ===============
     89 */
     90 void R_DrawSurface (void)
     91 {
     92 	unsigned char	*basetptr;
     93 	int				smax, tmax, twidth;
     94 	int				u;
     95 	int				soffset, basetoffset, texwidth;
     96 	int				horzblockstep;
     97 	unsigned char	*pcolumndest;
     98 	void			(*pblockdrawer)(void);
     99 	image_t			*mt;
    100 
    101 	surfrowbytes = r_drawsurf.rowbytes;
    102 
    103 	mt = r_drawsurf.image;
    104 	
    105 	r_source = mt->pixels[r_drawsurf.surfmip];
    106 	
    107 // the fractional light values should range from 0 to (VID_GRADES - 1) << 16
    108 // from a source range of 0 - 255
    109 	
    110 	texwidth = mt->width >> r_drawsurf.surfmip;
    111 
    112 	blocksize = 16 >> r_drawsurf.surfmip;
    113 	blockdivshift = 4 - r_drawsurf.surfmip;
    114 	blockdivmask = (1 << blockdivshift) - 1;
    115 	
    116 	r_lightwidth = (r_drawsurf.surf->extents[0]>>4)+1;
    117 
    118 	r_numhblocks = r_drawsurf.surfwidth >> blockdivshift;
    119 	r_numvblocks = r_drawsurf.surfheight >> blockdivshift;
    120 
    121 //==============================
    122 
    123 	pblockdrawer = surfmiptable[r_drawsurf.surfmip];
    124 // TODO: only needs to be set when there is a display settings change
    125 	horzblockstep = blocksize;
    126 
    127 	smax = mt->width >> r_drawsurf.surfmip;
    128 	twidth = texwidth;
    129 	tmax = mt->height >> r_drawsurf.surfmip;
    130 	sourcetstep = texwidth;
    131 	r_stepback = tmax * twidth;
    132 
    133 	r_sourcemax = r_source + (tmax * smax);
    134 
    135 	soffset = r_drawsurf.surf->texturemins[0];
    136 	basetoffset = r_drawsurf.surf->texturemins[1];
    137 
    138 // << 16 components are to guarantee positive values for %
    139 	soffset = ((soffset >> r_drawsurf.surfmip) + (smax << 16)) % smax;
    140 	basetptr = &r_source[((((basetoffset >> r_drawsurf.surfmip) 
    141 		+ (tmax << 16)) % tmax) * twidth)];
    142 
    143 	pcolumndest = r_drawsurf.surfdat;
    144 
    145 	for (u=0 ; u<r_numhblocks; u++)
    146 	{
    147 		r_lightptr = blocklights + u;
    148 
    149 		prowdestbase = pcolumndest;
    150 
    151 		pbasesource = basetptr + soffset;
    152 
    153 		(*pblockdrawer)();
    154 
    155 		soffset = soffset + blocksize;
    156 		if (soffset >= smax)
    157 			soffset = 0;
    158 
    159 		pcolumndest += horzblockstep;
    160 	}
    161 }
    162 
    163 
    164 //=============================================================================
    165 
    166 #if	!id386
    167 
    168 /*
    169 ================
    170 R_DrawSurfaceBlock8_mip0
    171 ================
    172 */
    173 void R_DrawSurfaceBlock8_mip0 (void)
    174 {
    175 	int				v, i, b, lightstep, lighttemp, light;
    176 	unsigned char	pix, *psource, *prowdest;
    177 
    178 	psource = pbasesource;
    179 	prowdest = prowdestbase;
    180 
    181 	for (v=0 ; v<r_numvblocks ; v++)
    182 	{
    183 	// FIXME: make these locals?
    184 	// FIXME: use delta rather than both right and left, like ASM?
    185 		lightleft = r_lightptr[0];
    186 		lightright = r_lightptr[1];
    187 		r_lightptr += r_lightwidth;
    188 		lightleftstep = (r_lightptr[0] - lightleft) >> 4;
    189 		lightrightstep = (r_lightptr[1] - lightright) >> 4;
    190 
    191 		for (i=0 ; i<16 ; i++)
    192 		{
    193 			lighttemp = lightleft - lightright;
    194 			lightstep = lighttemp >> 4;
    195 
    196 			light = lightright;
    197 
    198 			for (b=15; b>=0; b--)
    199 			{
    200 				pix = psource[b];
    201 				prowdest[b] = ((unsigned char *)vid.colormap)
    202 						[(light & 0xFF00) + pix];
    203 				light += lightstep;
    204 			}
    205 	
    206 			psource += sourcetstep;
    207 			lightright += lightrightstep;
    208 			lightleft += lightleftstep;
    209 			prowdest += surfrowbytes;
    210 		}
    211 
    212 		if (psource >= r_sourcemax)
    213 			psource -= r_stepback;
    214 	}
    215 }
    216 
    217 
    218 /*
    219 ================
    220 R_DrawSurfaceBlock8_mip1
    221 ================
    222 */
    223 void R_DrawSurfaceBlock8_mip1 (void)
    224 {
    225 	int				v, i, b, lightstep, lighttemp, light;
    226 	unsigned char	pix, *psource, *prowdest;
    227 
    228 	psource = pbasesource;
    229 	prowdest = prowdestbase;
    230 
    231 	for (v=0 ; v<r_numvblocks ; v++)
    232 	{
    233 	// FIXME: make these locals?
    234 	// FIXME: use delta rather than both right and left, like ASM?
    235 		lightleft = r_lightptr[0];
    236 		lightright = r_lightptr[1];
    237 		r_lightptr += r_lightwidth;
    238 		lightleftstep = (r_lightptr[0] - lightleft) >> 3;
    239 		lightrightstep = (r_lightptr[1] - lightright) >> 3;
    240 
    241 		for (i=0 ; i<8 ; i++)
    242 		{
    243 			lighttemp = lightleft - lightright;
    244 			lightstep = lighttemp >> 3;
    245 
    246 			light = lightright;
    247 
    248 			for (b=7; b>=0; b--)
    249 			{
    250 				pix = psource[b];
    251 				prowdest[b] = ((unsigned char *)vid.colormap)
    252 						[(light & 0xFF00) + pix];
    253 				light += lightstep;
    254 			}
    255 	
    256 			psource += sourcetstep;
    257 			lightright += lightrightstep;
    258 			lightleft += lightleftstep;
    259 			prowdest += surfrowbytes;
    260 		}
    261 
    262 		if (psource >= r_sourcemax)
    263 			psource -= r_stepback;
    264 	}
    265 }
    266 
    267 
    268 /*
    269 ================
    270 R_DrawSurfaceBlock8_mip2
    271 ================
    272 */
    273 void R_DrawSurfaceBlock8_mip2 (void)
    274 {
    275 	int				v, i, b, lightstep, lighttemp, light;
    276 	unsigned char	pix, *psource, *prowdest;
    277 
    278 	psource = pbasesource;
    279 	prowdest = prowdestbase;
    280 
    281 	for (v=0 ; v<r_numvblocks ; v++)
    282 	{
    283 	// FIXME: make these locals?
    284 	// FIXME: use delta rather than both right and left, like ASM?
    285 		lightleft = r_lightptr[0];
    286 		lightright = r_lightptr[1];
    287 		r_lightptr += r_lightwidth;
    288 		lightleftstep = (r_lightptr[0] - lightleft) >> 2;
    289 		lightrightstep = (r_lightptr[1] - lightright) >> 2;
    290 
    291 		for (i=0 ; i<4 ; i++)
    292 		{
    293 			lighttemp = lightleft - lightright;
    294 			lightstep = lighttemp >> 2;
    295 
    296 			light = lightright;
    297 
    298 			for (b=3; b>=0; b--)
    299 			{
    300 				pix = psource[b];
    301 				prowdest[b] = ((unsigned char *)vid.colormap)
    302 						[(light & 0xFF00) + pix];
    303 				light += lightstep;
    304 			}
    305 	
    306 			psource += sourcetstep;
    307 			lightright += lightrightstep;
    308 			lightleft += lightleftstep;
    309 			prowdest += surfrowbytes;
    310 		}
    311 
    312 		if (psource >= r_sourcemax)
    313 			psource -= r_stepback;
    314 	}
    315 }
    316 
    317 
    318 /*
    319 ================
    320 R_DrawSurfaceBlock8_mip3
    321 ================
    322 */
    323 void R_DrawSurfaceBlock8_mip3 (void)
    324 {
    325 	int				v, i, b, lightstep, lighttemp, light;
    326 	unsigned char	pix, *psource, *prowdest;
    327 
    328 	psource = pbasesource;
    329 	prowdest = prowdestbase;
    330 
    331 	for (v=0 ; v<r_numvblocks ; v++)
    332 	{
    333 	// FIXME: make these locals?
    334 	// FIXME: use delta rather than both right and left, like ASM?
    335 		lightleft = r_lightptr[0];
    336 		lightright = r_lightptr[1];
    337 		r_lightptr += r_lightwidth;
    338 		lightleftstep = (r_lightptr[0] - lightleft) >> 1;
    339 		lightrightstep = (r_lightptr[1] - lightright) >> 1;
    340 
    341 		for (i=0 ; i<2 ; i++)
    342 		{
    343 			lighttemp = lightleft - lightright;
    344 			lightstep = lighttemp >> 1;
    345 
    346 			light = lightright;
    347 
    348 			for (b=1; b>=0; b--)
    349 			{
    350 				pix = psource[b];
    351 				prowdest[b] = ((unsigned char *)vid.colormap)
    352 						[(light & 0xFF00) + pix];
    353 				light += lightstep;
    354 			}
    355 	
    356 			psource += sourcetstep;
    357 			lightright += lightrightstep;
    358 			lightleft += lightleftstep;
    359 			prowdest += surfrowbytes;
    360 		}
    361 
    362 		if (psource >= r_sourcemax)
    363 			psource -= r_stepback;
    364 	}
    365 }
    366 
    367 #endif
    368 
    369 
    370 //============================================================================
    371 
    372 
    373 /*
    374 ================
    375 R_InitCaches
    376 
    377 ================
    378 */
    379 void R_InitCaches (void)
    380 {
    381 	int		size;
    382 	int		pix;
    383 
    384 	// calculate size to allocate
    385 	if (sw_surfcacheoverride->value)
    386 	{
    387 		size = sw_surfcacheoverride->value;
    388 	}
    389 	else
    390 	{
    391 		size = SURFCACHE_SIZE_AT_320X240;
    392 
    393 		pix = vid.width*vid.height;
    394 		if (pix > 64000)
    395 			size += (pix-64000)*3;
    396 	}		
    397 
    398 	// round up to page size
    399 	size = (size + 8191) & ~8191;
    400 
    401 	ri.Con_Printf (PRINT_ALL,"%ik surface cache\n", size/1024);
    402 
    403 	sc_size = size;
    404 	sc_base = (surfcache_t *)malloc(size);
    405 	sc_rover = sc_base;
    406 	
    407 	sc_base->next = NULL;
    408 	sc_base->owner = NULL;
    409 	sc_base->size = sc_size;
    410 }
    411 
    412 
    413 /*
    414 ==================
    415 D_FlushCaches
    416 ==================
    417 */
    418 void D_FlushCaches (void)
    419 {
    420 	surfcache_t     *c;
    421 	
    422 	if (!sc_base)
    423 		return;
    424 
    425 	for (c = sc_base ; c ; c = c->next)
    426 	{
    427 		if (c->owner)
    428 			*c->owner = NULL;
    429 	}
    430 	
    431 	sc_rover = sc_base;
    432 	sc_base->next = NULL;
    433 	sc_base->owner = NULL;
    434 	sc_base->size = sc_size;
    435 }
    436 
    437 /*
    438 =================
    439 D_SCAlloc
    440 =================
    441 */
    442 surfcache_t     *D_SCAlloc (int width, int size)
    443 {
    444 	surfcache_t             *new;
    445 	qboolean                wrapped_this_time;
    446 
    447 	if ((width < 0) || (width > 256))
    448 		ri.Sys_Error (ERR_FATAL,"D_SCAlloc: bad cache width %d\n", width);
    449 
    450 	if ((size <= 0) || (size > 0x10000))
    451 		ri.Sys_Error (ERR_FATAL,"D_SCAlloc: bad cache size %d\n", size);
    452 	
    453 	size = (int)&((surfcache_t *)0)->data[size];
    454 	size = (size + 3) & ~3;
    455 	if (size > sc_size)
    456 		ri.Sys_Error (ERR_FATAL,"D_SCAlloc: %i > cache size of %i",size, sc_size);
    457 
    458 // if there is not size bytes after the rover, reset to the start
    459 	wrapped_this_time = false;
    460 
    461 	if ( !sc_rover || (byte *)sc_rover - (byte *)sc_base > sc_size - size)
    462 	{
    463 		if (sc_rover)
    464 		{
    465 			wrapped_this_time = true;
    466 		}
    467 		sc_rover = sc_base;
    468 	}
    469 		
    470 // colect and free surfcache_t blocks until the rover block is large enough
    471 	new = sc_rover;
    472 	if (sc_rover->owner)
    473 		*sc_rover->owner = NULL;
    474 	
    475 	while (new->size < size)
    476 	{
    477 	// free another
    478 		sc_rover = sc_rover->next;
    479 		if (!sc_rover)
    480 			ri.Sys_Error (ERR_FATAL,"D_SCAlloc: hit the end of memory");
    481 		if (sc_rover->owner)
    482 			*sc_rover->owner = NULL;
    483 			
    484 		new->size += sc_rover->size;
    485 		new->next = sc_rover->next;
    486 	}
    487 
    488 // create a fragment out of any leftovers
    489 	if (new->size - size > 256)
    490 	{
    491 		sc_rover = (surfcache_t *)( (byte *)new + size);
    492 		sc_rover->size = new->size - size;
    493 		sc_rover->next = new->next;
    494 		sc_rover->width = 0;
    495 		sc_rover->owner = NULL;
    496 		new->next = sc_rover;
    497 		new->size = size;
    498 	}
    499 	else
    500 		sc_rover = new->next;
    501 	
    502 	new->width = width;
    503 // DEBUG
    504 	if (width > 0)
    505 		new->height = (size - sizeof(*new) + sizeof(new->data)) / width;
    506 
    507 	new->owner = NULL;              // should be set properly after return
    508 
    509 	if (d_roverwrapped)
    510 	{
    511 		if (wrapped_this_time || (sc_rover >= d_initial_rover))
    512 			r_cache_thrash = true;
    513 	}
    514 	else if (wrapped_this_time)
    515 	{       
    516 		d_roverwrapped = true;
    517 	}
    518 
    519 	return new;
    520 }
    521 
    522 
    523 /*
    524 =================
    525 D_SCDump
    526 =================
    527 */
    528 void D_SCDump (void)
    529 {
    530 	surfcache_t             *test;
    531 
    532 	for (test = sc_base ; test ; test = test->next)
    533 	{
    534 		if (test == sc_rover)
    535 			ri.Con_Printf (PRINT_ALL,"ROVER:\n");
    536 		ri.Con_Printf (PRINT_ALL,"%p : %i bytes     %i width\n",test, test->size, test->width);
    537 	}
    538 }
    539 
    540 //=============================================================================
    541 
    542 // if the num is not a power of 2, assume it will not repeat
    543 
    544 int     MaskForNum (int num)
    545 {
    546 	if (num==128)
    547 		return 127;
    548 	if (num==64)
    549 		return 63;
    550 	if (num==32)
    551 		return 31;
    552 	if (num==16)
    553 		return 15;
    554 	return 255;
    555 }
    556 
    557 int D_log2 (int num)
    558 {
    559 	int     c;
    560 	
    561 	c = 0;
    562 	
    563 	while (num>>=1)
    564 		c++;
    565 	return c;
    566 }
    567 
    568 //=============================================================================
    569 
    570 /*
    571 ================
    572 D_CacheSurface
    573 ================
    574 */
    575 surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel)
    576 {
    577 	surfcache_t     *cache;
    578 
    579 //
    580 // if the surface is animating or flashing, flush the cache
    581 //
    582 	r_drawsurf.image = R_TextureAnimation (surface->texinfo);
    583 	r_drawsurf.lightadj[0] = r_newrefdef.lightstyles[surface->styles[0]].white*128;
    584 	r_drawsurf.lightadj[1] = r_newrefdef.lightstyles[surface->styles[1]].white*128;
    585 	r_drawsurf.lightadj[2] = r_newrefdef.lightstyles[surface->styles[2]].white*128;
    586 	r_drawsurf.lightadj[3] = r_newrefdef.lightstyles[surface->styles[3]].white*128;
    587 	
    588 //
    589 // see if the cache holds apropriate data
    590 //
    591 	cache = surface->cachespots[miplevel];
    592 
    593 	if (cache && !cache->dlight && surface->dlightframe != r_framecount
    594 			&& cache->image == r_drawsurf.image
    595 			&& cache->lightadj[0] == r_drawsurf.lightadj[0]
    596 			&& cache->lightadj[1] == r_drawsurf.lightadj[1]
    597 			&& cache->lightadj[2] == r_drawsurf.lightadj[2]
    598 			&& cache->lightadj[3] == r_drawsurf.lightadj[3] )
    599 		return cache;
    600 
    601 //
    602 // determine shape of surface
    603 //
    604 	surfscale = 1.0 / (1<<miplevel);
    605 	r_drawsurf.surfmip = miplevel;
    606 	r_drawsurf.surfwidth = surface->extents[0] >> miplevel;
    607 	r_drawsurf.rowbytes = r_drawsurf.surfwidth;
    608 	r_drawsurf.surfheight = surface->extents[1] >> miplevel;
    609 	
    610 //
    611 // allocate memory if needed
    612 //
    613 	if (!cache)     // if a texture just animated, don't reallocate it
    614 	{
    615 		cache = D_SCAlloc (r_drawsurf.surfwidth,
    616 						   r_drawsurf.surfwidth * r_drawsurf.surfheight);
    617 		surface->cachespots[miplevel] = cache;
    618 		cache->owner = &surface->cachespots[miplevel];
    619 		cache->mipscale = surfscale;
    620 	}
    621 	
    622 	if (surface->dlightframe == r_framecount)
    623 		cache->dlight = 1;
    624 	else
    625 		cache->dlight = 0;
    626 
    627 	r_drawsurf.surfdat = (pixel_t *)cache->data;
    628 	
    629 	cache->image = r_drawsurf.image;
    630 	cache->lightadj[0] = r_drawsurf.lightadj[0];
    631 	cache->lightadj[1] = r_drawsurf.lightadj[1];
    632 	cache->lightadj[2] = r_drawsurf.lightadj[2];
    633 	cache->lightadj[3] = r_drawsurf.lightadj[3];
    634 
    635 //
    636 // draw and light the surface texture
    637 //
    638 	r_drawsurf.surf = surface;
    639 
    640 	c_surf++;
    641 
    642 	// calculate the lightings
    643 	R_BuildLightMap ();
    644 	
    645 	// rasterize the surface into the cache
    646 	R_DrawSurface ();
    647 
    648 	return cache;
    649 }
    650 
    651