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