3 cvar_t r_max_size = {"r_max_size", "2048"};
4 cvar_t r_picmip = {"r_picmip", "0"};
5 cvar_t r_lerpimages = {"r_lerpimages", "1"};
6 cvar_t r_upload = {"r_upload", "1"};
7 cvar_t r_precachetextures = {"r_precachetextures", "1", true};
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()
164 // for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
165 // GL_UploadTexture(glt);
168 void r_textures_shutdown()
172 void R_Textures_Init (void)
174 Cmd_AddCommand("r_texturestats", GL_TextureStats_f);
175 Cvar_RegisterVariable (&r_max_size);
176 Cvar_RegisterVariable (&r_picmip);
177 Cvar_RegisterVariable (&r_lerpimages);
178 Cvar_RegisterVariable (&r_upload);
179 Cvar_RegisterVariable (&r_precachetextures);
184 // 3dfx can only handle 256 wide textures
185 if (!Q_strncasecmp ((char *)gl_renderer, "3dfx",4) || strstr((char *)gl_renderer, "Glide"))
186 Cvar_Set ("r_max_size", "256");
188 gltextures = qmalloc(sizeof(gltexture_t) * MAX_GLTEXTURES);
189 memset(gltextures, 0, sizeof(gltexture_t) * MAX_GLTEXTURES);
190 Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f);
192 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown);
200 int R_FindTexture (char *identifier)
205 for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
207 if (!strcmp (identifier, glt->identifier))
208 return gltextures[i].texnum;
214 void R_ResampleTextureLerpLine (byte *in, byte *out, int inwidth, int outwidth)
216 int j, xi, oldx = 0, f, fstep, l1, l2, endx;
217 fstep = (int) (inwidth*65536.0f/outwidth);
219 for (j = 0,f = 0;j < outwidth;j++, f += fstep)
224 in += (xi - oldx) * 4;
231 *out++ = (byte) ((in[0] * l1 + in[4] * l2) >> 16);
232 *out++ = (byte) ((in[1] * l1 + in[5] * l2) >> 16);
233 *out++ = (byte) ((in[2] * l1 + in[6] * l2) >> 16);
234 *out++ = (byte) ((in[3] * l1 + in[7] * l2) >> 16);
236 else // last pixel of the line has no pixel to lerp to
251 void R_ResampleTexture (void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
253 if (r_lerpimages.value)
255 int i, j, yi, oldy, f, fstep, l1, l2, endy = (inheight-1);
256 byte *inrow, *out, *row1, *row2;
258 fstep = (int) (inheight*65536.0f/outheight);
260 row1 = qmalloc(outwidth*4);
261 row2 = qmalloc(outwidth*4);
264 R_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
265 R_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
266 for (i = 0, f = 0;i < outheight;i++,f += fstep)
271 inrow = (byte *)indata + inwidth*4*yi;
273 memcpy(row1, row2, outwidth*4);
275 R_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
277 R_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
279 memcpy(row2, row1, outwidth*4);
286 for (j = 0;j < outwidth;j++)
288 *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
289 *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
290 *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
291 *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
296 else // last line has no pixels to lerp to
298 for (j = 0;j < outwidth;j++)
314 unsigned frac, fracstep;
315 byte *inrow, *out, *inpix;
318 fracstep = inwidth*0x10000/outwidth;
319 for (i=0 ; i<outheight ; i++)
321 inrow = (byte *)indata + inwidth*(i*inheight/outheight)*4;
322 frac = fracstep >> 1;
323 for (j=0 ; j<outwidth ; j+=4)
325 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = *inpix++ ;frac += fracstep;
326 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = *inpix++ ;frac += fracstep;
327 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = *inpix++ ;frac += fracstep;
328 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = *inpix++ ;frac += fracstep;
334 // in can be the same as out
335 void GL_MipReduce(byte *in, byte *out, int width, int height, int destwidth, int destheight)
337 int x, y, width2, height2, nextrow;
338 if (width > destwidth)
340 if (height > destheight)
344 height2 = height >> 1;
345 nextrow = width << 2;
346 for (y = 0;y < height2;y++)
348 for (x = 0;x < width2;x++)
350 out[0] = (byte) ((in[0] + in[4] + in[nextrow ] + in[nextrow+4]) >> 2);
351 out[1] = (byte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
352 out[2] = (byte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
353 out[3] = (byte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
357 in += nextrow; // skip a line
364 for (y = 0;y < height;y++)
366 for (x = 0;x < width2;x++)
368 out[0] = (byte) ((in[0] + in[4]) >> 1);
369 out[1] = (byte) ((in[1] + in[5]) >> 1);
370 out[2] = (byte) ((in[2] + in[6]) >> 1);
371 out[3] = (byte) ((in[3] + in[7]) >> 1);
380 if (height > destheight)
383 height2 = height >> 1;
384 nextrow = width << 2;
385 for (y = 0;y < height2;y++)
387 for (x = 0;x < width;x++)
389 out[0] = (byte) ((in[0] + in[nextrow ]) >> 1);
390 out[1] = (byte) ((in[1] + in[nextrow+1]) >> 1);
391 out[2] = (byte) ((in[2] + in[nextrow+2]) >> 1);
392 out[3] = (byte) ((in[3] + in[nextrow+3]) >> 1);
396 in += nextrow; // skip a line
400 Sys_Error("GL_MipReduce: desired size already achieved\n");
404 void GL_Upload32(int glslot, byte *data, int width, int height, int flags)
406 int mip, width2, height2, width3, height3, internalformat;
407 byte *gammadata, *buffer;
412 // 3 and 4 are converted by the driver to it's preferred format for the current display mode
414 if (flags & TEXF_ALPHA)
417 // calculate power of 2 size
418 width2 = 1;while (width2 < width) width2 <<= 1;
419 height2 = 1;while (height2 < height) height2 <<= 1;
420 // calculate final size (mipmapped downward to this)
421 width3 = width2 >> (int) r_picmip.value;
422 height3 = height2 >> (int) r_picmip.value;
423 while (width3 > (int) r_max_size.value) width3 >>= 1;
424 while (height3 > (int) r_max_size.value) height3 >>= 1;
425 if (width3 < 1) width3 = 1;
426 if (height3 < 1) height3 = 1;
428 gammadata = qmalloc(width*height*4);
429 buffer = qmalloc(width2*height2*4);
430 if (!gammadata || !buffer)
431 Host_Error("GL_Upload32: out of memory\n");
433 Image_CopyRGBAGamma(data, gammadata, width*height);
435 R_ResampleTexture(gammadata, width, height, buffer, width2, height2);
439 while (width2 > width3 || height2 > height3)
441 GL_MipReduce(buffer, buffer, width2, height2, width3, height3);
445 if (height2 > height3)
449 glBindTexture(GL_TEXTURE_2D, glslot);
451 glTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
452 if (flags & TEXF_MIPMAP)
454 while (width2 > 1 || height2 > 1)
456 GL_MipReduce(buffer, buffer, width2, height2, 1, 1);
463 glTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
466 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
467 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
471 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
472 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
474 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
479 void GL_Upload8 (int glslot, byte *data, int width, int height, int flags)
482 data32 = qmalloc(width*height*4);
483 Image_Copy8bitRGBA(data, data32, width*height, d_8to24table);
484 GL_Upload32(glslot, data32, width, height, flags);
488 void GL_UploadTexture (gltexture_t *glt)
490 if (glt->inputtexels == NULL)
492 if (glt->flags & TEXF_RGBA)
493 GL_Upload32(glt->texnum, glt->inputtexels, glt->width, glt->height, glt->flags);
495 GL_Upload8(glt->texnum, glt->inputtexels, glt->width, glt->height, glt->flags);
496 glt->internalflags |= GLTEXF_UPLOADED;
497 qfree(glt->inputtexels);
498 glt->inputtexels = NULL;
501 int R_CalcTexelDataSize (int width, int height, int mipmapped)
503 int width2, height2, size;
504 width2 = 1;while (width2 < width) width2 <<= 1;
505 height2 = 1;while (height2 < height) height2 <<= 1;
506 // calculate final size (mipmapped downward to this)
507 width2 >>= (int) r_picmip.value;
508 height2 >>= (int) r_picmip.value;
509 while (width2 > (int) r_max_size.value) width2 >>= 1;
510 while (height2 > (int) r_max_size.value) height2 >>= 1;
511 if (width2 < 1) width2 = 1;
512 if (height2 < 1) height2 = 1;
517 while (width2 > 1 || height2 > 1)
519 size += width2 * height2;
525 size++; // count the last 1x1 mipmap
528 size = width2*height2;
540 rtexture_t *R_LoadTexture (char *identifier, int width, int height, byte *data, int flags)
542 int i, bytesperpixel, internalflags, precache;
550 Host_Error("R_LoadTexture: no identifier\n");
552 // clear the alpha flag if the texture has no transparent pixels
553 if (flags & TEXF_ALPHA)
556 if (flags & TEXF_RGBA)
558 for (i = 0;i < width * height;i++)
560 if (data[i * 4 + 3] < 255)
569 for (i = 0;i < width * height;i++)
579 flags &= ~TEXF_ALPHA;
582 if (flags & TEXF_RGBA)
588 if (r_lerpimages.value != 0)
589 internalflags |= GLTEXF_LERPED;
591 // LordHavoc: do a CRC to confirm the data really is the same as previous occurances.
592 crc = CRC_Block(data, width*height*bytesperpixel);
593 // see if the texture is already present
594 for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
596 if (!strcmp (identifier, glt->identifier))
598 // LordHavoc: everyone hates cache mismatchs, so I fixed it
599 if (crc != glt->crc || width != glt->width || height != glt->height || flags != glt->flags)
601 Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n");
602 goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
604 if (internalflags != glt->internalflags)
605 goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
606 return (rtexture_t *)glt;
614 strcpy (glt->identifier, identifier);
619 // LordHavoc: check if there are still slots available
620 if (numgltextures >= MAX_GLTEXTURES)
621 Sys_Error ("GL_LoadTexture: ran out of texture slots (%d)\n", MAX_GLTEXTURES);
622 glt = &gltextures[numgltextures++];
623 glt->texnum = gl_texture_number++;
624 strcpy (glt->identifier, identifier);
627 // LordHavoc: label to drop out of the loop into the setup code
628 GL_LoadTexture_setup:
629 glt->crc = crc; // LordHavoc: used to verify textures are identical
631 glt->height = height;
633 glt->internalflags = internalflags;
635 if (glt->inputtexels)
636 qfree(glt->inputtexels);
637 glt->inputtexeldatasize = width*height*bytesperpixel;
638 glt->inputtexels = qmalloc(glt->inputtexeldatasize);
640 memcpy(glt->inputtexels, data, glt->inputtexeldatasize);
642 glt->texeldatasize = R_CalcTexelDataSize(width, height, flags & TEXF_MIPMAP);
645 if (r_precachetextures.value >= 1)
647 if (flags & TEXF_PRECACHE)
649 if (r_precachetextures.value >= 2)
654 GL_UploadTexture(glt);
656 return (rtexture_t *)glt;
659 // only used for lightmaps
660 int R_GetTextureSlots(int count)
663 i = gl_texture_number;
664 gl_texture_number += count;