3 cvar_t r_max_size = {0, "r_max_size", "2048"};
4 cvar_t r_picmip = {0, "r_picmip", "0"};
5 cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1"};
6 cvar_t r_upload = {0, "r_upload", "1"};
7 cvar_t r_precachetextures = {CVAR_SAVE, "r_precachetextures", "1"};
9 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR; //NEAREST;
10 int gl_filter_max = GL_LINEAR;
18 #define GLTEXF_LERPED 1
19 #define GLTEXF_UPLOADED 2
24 int texnum; // GL texture slot number
25 int texeldatasize; // computed memory usage of this texture (including mipmaps, expansion to 32bit, etc)
26 byte *inputtexels; // copy of the original texture supplied to the upload function, for re-uploading or deferred uploads (non-precached)
27 int inputtexeldatasize; // size of the original texture
28 unsigned short width, height;
29 // LordHavoc: CRC to identify cache mismatchs
31 int flags; // the requested flags when the texture was supplied to the upload function
32 int internalflags; // internal notes (lerped, etc)
35 #define MAX_GLTEXTURES 4096
36 gltexture_t *gltextures;
37 unsigned int numgltextures = 0, gl_texture_number = 1;
39 void GL_UploadTexture(gltexture_t *t);
41 int R_GetTexture(rtexture_t *rt)
46 glt = (gltexture_t *)rt;
47 if (!(glt->internalflags & GLTEXF_UPLOADED))
49 GL_UploadTexture(glt);
50 if (!(glt->internalflags & GLTEXF_UPLOADED))
51 Host_Error("R_GetTexture: unable to upload texture\n");
59 int minimize, maximize;
64 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
65 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
66 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
67 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
68 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
69 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
77 void Draw_TextureMode_f (void)
84 for (i=0 ; i< 6 ; i++)
85 if (gl_filter_min == modes[i].minimize)
87 Con_Printf ("%s\n", modes[i].name);
90 Con_Printf ("current filter is unknown???\n");
94 for (i=0 ; i< 6 ; i++)
96 if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) )
101 Con_Printf ("bad filter name\n");
105 gl_filter_min = modes[i].minimize;
106 gl_filter_max = modes[i].maximize;
110 // change all the existing mipmap texture objects
111 for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
113 if (glt->flags & TEXF_MIPMAP)
115 glBindTexture(GL_TEXTURE_2D, glt->texnum);
116 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
117 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
122 void GL_TextureStats_Print(char *name, int total, int total2, int loaded, int crc, int mip, int alpha, int total2valid)
126 Con_Printf("%5iK %c%5iK%c %04X %s %s %s %s\n", total, total2valid ? ' ' : '(', total2, total2valid ? ' ' : ')', crc, loaded ? "loaded" : " ", mip ? "mip" : " ", alpha ? "alpha" : " ", name);
129 void GL_TextureStats_PrintTotal(void)
131 int i, t = 0, p = 0, loaded = 0, loadedt = 0, loadedp = 0;
133 for (i = 0, glt = gltextures;i < numgltextures;i++, glt++)
135 t += glt->texeldatasize;
136 p += glt->inputtexeldatasize;
137 if (glt->internalflags & GLTEXF_UPLOADED)
140 loadedt += glt->texeldatasize;
141 loadedp += glt->inputtexeldatasize;
144 Con_Printf("total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", numgltextures, t / 1048576.0, p / 1048576.0, loaded, loadedt / 1048576.0, loadedp / 1048576.0, numgltextures - loaded, (t - loadedt) / 1048576.0, (p - loadedp) / 1048576.0);
147 void GL_TextureStats_f(void)
151 Con_Printf("kbytes original crc loaded mip alpha name\n");
152 for (i = 0, glt = gltextures;i < numgltextures;i++, glt++)
153 GL_TextureStats_Print(glt->identifier, (glt->texeldatasize + 1023) / 1024, (glt->inputtexeldatasize + 1023) / 1024, glt->internalflags & GLTEXF_UPLOADED, glt->crc, glt->flags & TEXF_MIPMAP, glt->flags & TEXF_ALPHA, glt->inputtexels != NULL);
154 GL_TextureStats_PrintTotal();
157 char engineversion[40];
159 //void GL_UploadTexture (gltexture_t *glt);
160 void r_textures_start(void)
164 // for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
165 // GL_UploadTexture(glt);
168 void r_textures_shutdown(void)
172 void r_textures_newmap(void)
176 void R_Textures_Init (void)
178 Cmd_AddCommand("r_texturestats", GL_TextureStats_f);
179 Cvar_RegisterVariable (&r_max_size);
180 Cvar_RegisterVariable (&r_picmip);
181 Cvar_RegisterVariable (&r_lerpimages);
182 Cvar_RegisterVariable (&r_upload);
183 Cvar_RegisterVariable (&r_precachetextures);
188 // 3dfx can only handle 256 wide textures
189 if (!Q_strncasecmp ((char *)gl_renderer, "3dfx",4) || strstr((char *)gl_renderer, "Glide"))
190 Cvar_Set ("r_max_size", "256");
192 gltextures = qmalloc(sizeof(gltexture_t) * MAX_GLTEXTURES);
193 memset(gltextures, 0, sizeof(gltexture_t) * MAX_GLTEXTURES);
194 Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f);
196 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
204 int R_FindTexture (char *identifier)
209 for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
211 if (!strcmp (identifier, glt->identifier))
212 return gltextures[i].texnum;
218 void R_ResampleTextureLerpLine (byte *in, byte *out, int inwidth, int outwidth)
220 int j, xi, oldx = 0, f, fstep, endx;
221 fstep = (int) (inwidth*65536.0f/outwidth);
223 for (j = 0,f = 0;j < outwidth;j++, f += fstep)
228 in += (xi - oldx) * 4;
233 int lerp = f & 0xFFFF;
234 *out++ = (byte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]);
235 *out++ = (byte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]);
236 *out++ = (byte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]);
237 *out++ = (byte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]);
239 else // last pixel of the line has no pixel to lerp to
254 void R_ResampleTexture (void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
256 if (r_lerpimages.value)
258 int i, j, yi, oldy, f, fstep, endy = (inheight-1);
259 byte *inrow, *out, *row1, *row2;
261 fstep = (int) (inheight*65536.0f/outheight);
263 row1 = qmalloc(outwidth*4);
264 row2 = qmalloc(outwidth*4);
267 R_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
268 R_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
269 for (i = 0, f = 0;i < outheight;i++,f += fstep)
274 int lerp = f & 0xFFFF;
277 inrow = (byte *)indata + inwidth*4*yi;
279 memcpy(row1, row2, outwidth*4);
281 R_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
282 R_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
288 out[ 0] = (byte) ((((row2[ 0] - row1[ 0]) * lerp) >> 16) + row1[ 0]);
289 out[ 1] = (byte) ((((row2[ 1] - row1[ 1]) * lerp) >> 16) + row1[ 1]);
290 out[ 2] = (byte) ((((row2[ 2] - row1[ 2]) * lerp) >> 16) + row1[ 2]);
291 out[ 3] = (byte) ((((row2[ 3] - row1[ 3]) * lerp) >> 16) + row1[ 3]);
292 out[ 4] = (byte) ((((row2[ 4] - row1[ 4]) * lerp) >> 16) + row1[ 4]);
293 out[ 5] = (byte) ((((row2[ 5] - row1[ 5]) * lerp) >> 16) + row1[ 5]);
294 out[ 6] = (byte) ((((row2[ 6] - row1[ 6]) * lerp) >> 16) + row1[ 6]);
295 out[ 7] = (byte) ((((row2[ 7] - row1[ 7]) * lerp) >> 16) + row1[ 7]);
296 out[ 8] = (byte) ((((row2[ 8] - row1[ 8]) * lerp) >> 16) + row1[ 8]);
297 out[ 9] = (byte) ((((row2[ 9] - row1[ 9]) * lerp) >> 16) + row1[ 9]);
298 out[10] = (byte) ((((row2[10] - row1[10]) * lerp) >> 16) + row1[10]);
299 out[11] = (byte) ((((row2[11] - row1[11]) * lerp) >> 16) + row1[11]);
300 out[12] = (byte) ((((row2[12] - row1[12]) * lerp) >> 16) + row1[12]);
301 out[13] = (byte) ((((row2[13] - row1[13]) * lerp) >> 16) + row1[13]);
302 out[14] = (byte) ((((row2[14] - row1[14]) * lerp) >> 16) + row1[14]);
303 out[15] = (byte) ((((row2[15] - row1[15]) * lerp) >> 16) + row1[15]);
311 out[ 0] = (byte) ((((row2[ 0] - row1[ 0]) * lerp) >> 16) + row1[ 0]);
312 out[ 1] = (byte) ((((row2[ 1] - row1[ 1]) * lerp) >> 16) + row1[ 1]);
313 out[ 2] = (byte) ((((row2[ 2] - row1[ 2]) * lerp) >> 16) + row1[ 2]);
314 out[ 3] = (byte) ((((row2[ 3] - row1[ 3]) * lerp) >> 16) + row1[ 3]);
315 out[ 4] = (byte) ((((row2[ 4] - row1[ 4]) * lerp) >> 16) + row1[ 4]);
316 out[ 5] = (byte) ((((row2[ 5] - row1[ 5]) * lerp) >> 16) + row1[ 5]);
317 out[ 6] = (byte) ((((row2[ 6] - row1[ 6]) * lerp) >> 16) + row1[ 6]);
318 out[ 7] = (byte) ((((row2[ 7] - row1[ 7]) * lerp) >> 16) + row1[ 7]);
325 out[ 0] = (byte) ((((row2[ 0] - row1[ 0]) * lerp) >> 16) + row1[ 0]);
326 out[ 1] = (byte) ((((row2[ 1] - row1[ 1]) * lerp) >> 16) + row1[ 1]);
327 out[ 2] = (byte) ((((row2[ 2] - row1[ 2]) * lerp) >> 16) + row1[ 2]);
328 out[ 3] = (byte) ((((row2[ 3] - row1[ 3]) * lerp) >> 16) + row1[ 3]);
340 inrow = (byte *)indata + inwidth*4*yi;
342 memcpy(row1, row2, outwidth*4);
344 R_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
347 memcpy(out, row1, outwidth * 4);
356 unsigned frac, fracstep;
357 // relies on int being 4 bytes
361 fracstep = inwidth*0x10000/outwidth;
362 for (i = 0;i < outheight;i++)
364 inrow = (int *)indata + inwidth*(i*inheight/outheight);
365 frac = fracstep >> 1;
369 out[0] = inrow[frac >> 16];frac += fracstep;
370 out[1] = inrow[frac >> 16];frac += fracstep;
371 out[2] = inrow[frac >> 16];frac += fracstep;
372 out[3] = inrow[frac >> 16];frac += fracstep;
378 out[0] = inrow[frac >> 16];frac += fracstep;
379 out[1] = inrow[frac >> 16];frac += fracstep;
384 out[0] = inrow[frac >> 16];frac += fracstep;
391 // in can be the same as out
392 void GL_MipReduce(byte *in, byte *out, int width, int height, int destwidth, int destheight)
394 int x, y, width2, height2, nextrow;
395 if (width > destwidth)
397 if (height > destheight)
401 height2 = height >> 1;
402 nextrow = width << 2;
403 for (y = 0;y < height2;y++)
405 for (x = 0;x < width2;x++)
407 out[0] = (byte) ((in[0] + in[4] + in[nextrow ] + in[nextrow+4]) >> 2);
408 out[1] = (byte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
409 out[2] = (byte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
410 out[3] = (byte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
414 in += nextrow; // skip a line
421 for (y = 0;y < height;y++)
423 for (x = 0;x < width2;x++)
425 out[0] = (byte) ((in[0] + in[4]) >> 1);
426 out[1] = (byte) ((in[1] + in[5]) >> 1);
427 out[2] = (byte) ((in[2] + in[6]) >> 1);
428 out[3] = (byte) ((in[3] + in[7]) >> 1);
437 if (height > destheight)
440 height2 = height >> 1;
441 nextrow = width << 2;
442 for (y = 0;y < height2;y++)
444 for (x = 0;x < width;x++)
446 out[0] = (byte) ((in[0] + in[nextrow ]) >> 1);
447 out[1] = (byte) ((in[1] + in[nextrow+1]) >> 1);
448 out[2] = (byte) ((in[2] + in[nextrow+2]) >> 1);
449 out[3] = (byte) ((in[3] + in[nextrow+3]) >> 1);
453 in += nextrow; // skip a line
457 Sys_Error("GL_MipReduce: desired size already achieved\n");
461 void GL_Upload32(int glslot, byte *data, int width, int height, int flags)
463 int mip, width2, height2, width3, height3, internalformat;
464 byte *gammadata, *buffer;
469 // 3 and 4 are converted by the driver to it's preferred format for the current display mode
471 if (flags & TEXF_ALPHA)
474 // calculate power of 2 size
475 width2 = 1;while (width2 < width) width2 <<= 1;
476 height2 = 1;while (height2 < height) height2 <<= 1;
477 // calculate final size (mipmapped downward to this)
478 width3 = width2 >> (int) r_picmip.value;
479 height3 = height2 >> (int) r_picmip.value;
480 while (width3 > (int) r_max_size.value) width3 >>= 1;
481 while (height3 > (int) r_max_size.value) height3 >>= 1;
482 if (width3 < 1) width3 = 1;
483 if (height3 < 1) height3 = 1;
485 gammadata = qmalloc(width*height*4);
486 buffer = qmalloc(width2*height2*4);
487 if (!gammadata || !buffer)
488 Host_Error("GL_Upload32: out of memory\n");
490 Image_CopyRGBAGamma(data, gammadata, width*height);
492 R_ResampleTexture(gammadata, width, height, buffer, width2, height2);
496 while (width2 > width3 || height2 > height3)
498 GL_MipReduce(buffer, buffer, width2, height2, width3, height3);
502 if (height2 > height3)
506 glBindTexture(GL_TEXTURE_2D, glslot);
508 glTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
509 if (flags & TEXF_MIPMAP)
511 while (width2 > 1 || height2 > 1)
513 GL_MipReduce(buffer, buffer, width2, height2, 1, 1);
520 glTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
523 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
524 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
528 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
529 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
531 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
536 void GL_Upload8 (int glslot, byte *data, int width, int height, int flags)
539 data32 = qmalloc(width*height*4);
540 Image_Copy8bitRGBA(data, data32, width*height, d_8to24table);
541 GL_Upload32(glslot, data32, width, height, flags);
545 void GL_UploadTexture (gltexture_t *glt)
547 if (glt->inputtexels == NULL)
549 if (glt->flags & TEXF_RGBA)
550 GL_Upload32(glt->texnum, glt->inputtexels, glt->width, glt->height, glt->flags);
552 GL_Upload8(glt->texnum, glt->inputtexels, glt->width, glt->height, glt->flags);
553 glt->internalflags |= GLTEXF_UPLOADED;
554 qfree(glt->inputtexels);
555 glt->inputtexels = NULL;
558 int R_CalcTexelDataSize (int width, int height, int mipmapped)
560 int width2, height2, size;
561 width2 = 1;while (width2 < width) width2 <<= 1;
562 height2 = 1;while (height2 < height) height2 <<= 1;
563 // calculate final size (mipmapped downward to this)
564 width2 >>= (int) r_picmip.value;
565 height2 >>= (int) r_picmip.value;
566 while (width2 > (int) r_max_size.value) width2 >>= 1;
567 while (height2 > (int) r_max_size.value) height2 >>= 1;
568 if (width2 < 1) width2 = 1;
569 if (height2 < 1) height2 = 1;
574 while (width2 > 1 || height2 > 1)
576 size += width2 * height2;
582 size++; // count the last 1x1 mipmap
585 size = width2*height2;
597 rtexture_t *R_LoadTexture (char *identifier, int width, int height, byte *data, int flags)
599 int i, bytesperpixel, internalflags, precache;
603 if (cls.state == ca_dedicated)
607 Host_Error("R_LoadTexture: no identifier\n");
609 Host_Error("R_LoadTexture: \"%s\" has no data\n", identifier);
611 // clear the alpha flag if the texture has no transparent pixels
612 if (flags & TEXF_ALPHA)
615 if (flags & TEXF_RGBA)
617 for (i = 0;i < width * height;i++)
619 if (data[i * 4 + 3] < 255)
628 for (i = 0;i < width * height;i++)
638 flags &= ~TEXF_ALPHA;
641 if (flags & TEXF_RGBA)
647 if (r_lerpimages.value != 0)
648 internalflags |= GLTEXF_LERPED;
650 // LordHavoc: do a CRC to confirm the data really is the same as previous occurances.
651 crc = CRC_Block(data, width*height*bytesperpixel);
652 // see if the texture is already present
653 for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
655 if (!strcmp (identifier, glt->identifier))
657 // LordHavoc: everyone hates cache mismatchs, so I fixed it
658 if (crc != glt->crc || width != glt->width || height != glt->height || flags != glt->flags)
660 Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n");
661 goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
663 if (internalflags != glt->internalflags)
664 goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
665 return (rtexture_t *)glt;
673 strcpy (glt->identifier, identifier);
678 // LordHavoc: check if there are still slots available
679 if (numgltextures >= MAX_GLTEXTURES)
680 Sys_Error ("GL_LoadTexture: ran out of texture slots (%d)\n", MAX_GLTEXTURES);
681 glt = &gltextures[numgltextures++];
682 glt->texnum = gl_texture_number++;
683 strcpy (glt->identifier, identifier);
686 // LordHavoc: label to drop out of the loop into the setup code
687 GL_LoadTexture_setup:
688 glt->crc = crc; // LordHavoc: used to verify textures are identical
690 glt->height = height;
692 glt->internalflags = internalflags;
694 if (glt->inputtexels)
695 qfree(glt->inputtexels);
696 glt->inputtexeldatasize = width*height*bytesperpixel;
697 glt->inputtexels = qmalloc(glt->inputtexeldatasize);
699 memcpy(glt->inputtexels, data, glt->inputtexeldatasize);
701 glt->texeldatasize = R_CalcTexelDataSize(width, height, flags & TEXF_MIPMAP);
704 if (flags & TEXF_ALWAYSPRECACHE)
706 else if (r_precachetextures.value >= 1)
708 if (flags & TEXF_PRECACHE)
710 if (r_precachetextures.value >= 2)
715 GL_UploadTexture(glt);
717 return (rtexture_t *)glt;
720 // only used for lightmaps
721 int R_GetTextureSlots(int count)
724 i = gl_texture_number;
725 gl_texture_number += count;