3 cvar_t gl_max_size = {"gl_max_size", "2048"};
4 cvar_t gl_picmip = {"gl_picmip", "0"};
5 cvar_t gl_lerpimages = {"gl_lerpimages", "1"};
6 cvar_t r_upload = {"r_upload", "1"};
8 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR; //NEAREST;
9 int gl_filter_max = GL_LINEAR;
22 byte *texels[MAXMIPS];
23 unsigned short texelsize[MAXMIPS][2];
24 unsigned short width, height;
25 // LordHavoc: CRC to identify cache mismatchs
30 char lerped; // whether this texture was uploaded with or without interpolation
31 char inuse; // cleared during texture purge when loading new level
35 #define MAX_GLTEXTURES 4096
36 gltexture_t *gltextures;
42 int minimize, maximize;
46 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
47 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
48 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
49 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
50 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
51 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
59 void Draw_TextureMode_f (void)
66 for (i=0 ; i< 6 ; i++)
67 if (gl_filter_min == modes[i].minimize)
69 Con_Printf ("%s\n", modes[i].name);
72 Con_Printf ("current filter is unknown???\n");
76 for (i=0 ; i< 6 ; i++)
78 if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) )
83 Con_Printf ("bad filter name\n");
87 gl_filter_min = modes[i].minimize;
88 gl_filter_max = modes[i].maximize;
92 // change all the existing mipmap texture objects
93 for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
97 glBindTexture(GL_TEXTURE_2D, glt->texnum);
98 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
99 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
104 void GL_TextureStats_Print(char *name, int total, int crc, int mip, int alpha)
110 while (name[c] && c < 28)
112 // no need to pad since the name was moved to last
116 Con_Printf("%5i %04X %s %s %s\n", total, crc, mip ? "yes" : "no ", alpha ? "yes " : "no ", n);
119 void GL_TextureStats_f(void)
121 int i, s = 0, sc = 0, t = 0;
123 Con_Printf("kbytes crc mip alpha name\n");
124 for (i = 0, glt = gltextures;i < numgltextures;i++, glt++)
126 GL_TextureStats_Print(glt->identifier, (glt->texeldatasize + 512) >> 10, glt->crc, glt->mipmap, glt->alpha);
127 t += glt->texeldatasize;
128 if (glt->identifier[0] == '&')
131 s += glt->texeldatasize;
134 Con_Printf("%i textures, totalling %.3fMB, %i are (usually) unnecessary model skins totalling %.3fMB\n", numgltextures, t / 1048576.0, sc, s / 1048576.0);
137 void GL_TextureStats_PrintTotal(void)
139 int i, s = 0, sc = 0, t = 0;
141 for (i = 0, glt = gltextures;i < numgltextures;i++, glt++)
143 t += glt->texeldatasize;
144 if (glt->identifier[0] == '&')
147 s += glt->texeldatasize;
150 Con_Printf("%i textures, totalling %.3fMB, %i are (usually) unnecessary model skins totalling %.3fMB\n", numgltextures, t / 1048576.0, sc, s / 1048576.0);
153 char engineversion[40];
155 //void GL_UploadTexture (gltexture_t *glt);
156 void gl_textures_start()
160 // for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
161 // GL_UploadTexture(glt);
164 void gl_textures_shutdown()
168 void GL_Textures_Init (void)
170 Cmd_AddCommand("r_texturestats", GL_TextureStats_f);
171 Cvar_RegisterVariable (&gl_max_size);
172 Cvar_RegisterVariable (&gl_picmip);
173 Cvar_RegisterVariable (&gl_lerpimages);
174 Cvar_RegisterVariable (&r_upload);
179 // 3dfx can only handle 256 wide textures
180 if (!Q_strncasecmp ((char *)gl_renderer, "3dfx",4) || strstr((char *)gl_renderer, "Glide"))
181 Cvar_Set ("gl_max_size", "256");
183 gltextures = qmalloc(sizeof(gltexture_t) * MAX_GLTEXTURES);
184 memset(gltextures, 0, sizeof(gltexture_t) * MAX_GLTEXTURES);
185 Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f);
187 R_RegisterModule("GL_Textures", gl_textures_start, gl_textures_shutdown);
195 int GL_FindTexture (char *identifier)
200 for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
202 if (!strcmp (identifier, glt->identifier))
203 return gltextures[i].texnum;
209 void GL_ResampleTextureLerpLine (byte *in, byte *out, int inwidth, int outwidth)
211 int j, xi, oldx = 0, f, fstep, l1, l2, endx;
212 fstep = (int) (inwidth*65536.0f/outwidth);
214 for (j = 0,f = 0;j < outwidth;j++, f += fstep)
219 in += (xi - oldx) * 4;
226 *out++ = (byte) ((in[0] * l1 + in[4] * l2) >> 16);
227 *out++ = (byte) ((in[1] * l1 + in[5] * l2) >> 16);
228 *out++ = (byte) ((in[2] * l1 + in[6] * l2) >> 16);
229 *out++ = (byte) ((in[3] * l1 + in[7] * l2) >> 16);
231 else // last pixel of the line has no pixel to lerp to
246 void GL_ResampleTexture (void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
248 if (gl_lerpimages.value)
250 int i, j, yi, oldy, f, fstep, l1, l2, endy = (inheight-1);
251 byte *inrow, *out, *row1, *row2;
253 fstep = (int) (inheight*65536.0f/outheight);
255 row1 = qmalloc(outwidth*4);
256 row2 = qmalloc(outwidth*4);
259 GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
260 GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
261 for (i = 0, f = 0;i < outheight;i++,f += fstep)
266 inrow = (byte *)indata + inwidth*4*yi;
268 memcpy(row1, row2, outwidth*4);
270 GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
272 GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
274 memcpy(row2, row1, outwidth*4);
281 for (j = 0;j < outwidth;j++)
283 *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
284 *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
285 *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
286 *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
291 else // last line has no pixels to lerp to
293 for (j = 0;j < outwidth;j++)
309 unsigned frac, fracstep;
310 byte *inrow, *out, *inpix;
313 fracstep = inwidth*0x10000/outwidth;
314 for (i=0 ; i<outheight ; i++)
316 inrow = (byte *)indata + inwidth*(i*inheight/outheight)*4;
317 frac = fracstep >> 1;
318 for (j=0 ; j<outwidth ; j+=4)
320 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = *inpix++ ;frac += fracstep;
321 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = *inpix++ ;frac += fracstep;
322 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = *inpix++ ;frac += fracstep;
323 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = *inpix++ ;frac += fracstep;
329 void GL_FreeTexels(gltexture_t *glt)
332 qfree(glt->texels[0]);
336 void GL_AllocTexels(gltexture_t *glt, int width, int height, int mipmapped)
341 glt->texelsize[0][0] = width;
342 glt->texelsize[0][1] = height;
346 w = width;h = height;
350 glt->texelsize[i][0] = w;
351 glt->texelsize[i][1] = h;
352 glt->texels[i++] = (void *)size;
365 glt->texeldatasize = size;
367 glt->texels[i++] = NULL;
368 glt->texels[0] = qmalloc(size);
369 for (i = 1;i < MAXMIPS && glt->texels[i];i++)
370 glt->texels[i] += (int) glt->texels[0];
374 size = width*height*4;
375 glt->texeldatasize = size;
376 glt->texels[0] = qmalloc(size);
377 for (i = 1;i < MAXMIPS;i++)
378 glt->texels[i] = NULL;
381 Sys_Error("GL_AllocTexels: out of memory\n");
384 // in can be the same as out
385 void GL_MipReduce(byte *in, byte *out, int width, int height, int destwidth, int destheight)
387 int x, y, width2, height2, nextrow;
388 if (width > destwidth)
390 if (height > destheight)
394 height2 = height >> 1;
395 nextrow = width << 2;
396 for (y = 0;y < height2;y++)
398 for (x = 0;x < width2;x++)
400 out[0] = (byte) ((in[0] + in[4] + in[nextrow ] + in[nextrow+4]) >> 2);
401 out[1] = (byte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
402 out[2] = (byte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
403 out[3] = (byte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
407 in += nextrow; // skip a line
414 for (y = 0;y < height;y++)
416 for (x = 0;x < width2;x++)
418 out[0] = (byte) ((in[0] + in[4]) >> 1);
419 out[1] = (byte) ((in[1] + in[5]) >> 1);
420 out[2] = (byte) ((in[2] + in[6]) >> 1);
421 out[3] = (byte) ((in[3] + in[7]) >> 1);
430 if (height > destheight)
433 height2 = height >> 1;
434 nextrow = width << 2;
435 for (y = 0;y < height2;y++)
437 for (x = 0;x < width;x++)
439 out[0] = (byte) ((in[0] + in[nextrow ]) >> 1);
440 out[1] = (byte) ((in[1] + in[nextrow+1]) >> 1);
441 out[2] = (byte) ((in[2] + in[nextrow+2]) >> 1);
442 out[3] = (byte) ((in[3] + in[nextrow+3]) >> 1);
446 in += nextrow; // skip a line
450 Sys_Error("GL_MipReduce: desired size already achieved\n");
454 void GL_UploadTexture (gltexture_t *glt)
456 int mip, width, height;
459 glBindTexture(GL_TEXTURE_2D, glt->texnum);
461 height = glt->height;
462 for (mip = 0;mip < MAXMIPS && glt->texels[mip];mip++)
463 glTexImage2D(GL_TEXTURE_2D, mip, glt->alpha ? 4 : 3, glt->texelsize[mip][0], glt->texelsize[mip][1], 0, GL_RGBA, GL_UNSIGNED_BYTE, glt->texels[mip]);
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);
482 int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel)
485 int i, width2, height2, width3, height3, w, h, mip;
486 gltexture_t *glt, *freeglt;
487 // LordHavoc: texture caching, turned out to be a waste of time (and immense waste of diskspace)
488 //char cachefilename[1024], *cachefile;
495 // LordHavoc: do a CRC to confirm the data really is the same as previous occurances.
496 crc = CRC_Block(data, width*height*bytesperpixel);
497 // see if the texture is already present
500 for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
504 if (!strcmp (identifier, glt->identifier))
506 // LordHavoc: everyone hates cache mismatchs, so I fixed it
507 if (crc != glt->crc || width != glt->width || height != glt->height)
509 Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n");
510 goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
512 if ((gl_lerpimages.value != 0) != glt->lerped)
513 goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
523 // LordHavoc: although this could be an else condition as it was in the original id code,
524 // it is more clear this way
528 strcpy (glt->identifier, identifier);
532 // LordHavoc: check if there are still slots available
533 if (numgltextures >= MAX_GLTEXTURES)
534 Sys_Error ("GL_LoadTexture: ran out of texture slots (%d)\n", MAX_GLTEXTURES);
535 glt = &gltextures[numgltextures++];
536 glt->texnum = texture_extension_number;
537 texture_extension_number++;
538 strcpy (glt->identifier, identifier);
541 // LordHavoc: label to drop out of the loop into the setup code
542 GL_LoadTexture_setup:
543 // calculate power of 2 size
544 width2 = 1;while (width2 < width) width2 <<= 1;
545 height2 = 1;while (height2 < height) height2 <<= 1;
546 // calculate final size (mipmapped downward to this)
547 width3 = width2 >> (int) gl_picmip.value;
548 height3 = height2 >> (int) gl_picmip.value;
549 while (width3 > (int) gl_max_size.value) width3 >>= 1;
550 while (height3 > (int) gl_max_size.value) height3 >>= 1;
551 if (width3 < 1) width3 = 1;
552 if (height3 < 1) height3 = 1;
555 GL_AllocTexels(glt, width3, height3, mipmap);
556 glt->crc = crc; // LordHavoc: used to verify textures are identical
558 glt->height = height;
559 glt->mipmap = mipmap;
560 glt->bytesperpixel = bytesperpixel;
561 glt->lerped = gl_lerpimages.value != 0;
562 glt->alpha = false; // updated later
565 // LordHavoc: texture caching, turned out to be a waste of time (and immense waste of diskspace)
566 sprintf(cachefilename, "%s%x%x%x.texels", identifier, width3, height3, crc);
567 for (i = 0;cachefilename[i];i++)
569 if (cachefilename[i] <= ' ' || cachefilename[i] >= 127 || cachefilename[i] == '/' || cachefilename[i] == '\\' || cachefilename[i] == ':' || cachefilename[i] == '*' || cachefilename[i] == '?')
570 cachefilename[i] = '@';
571 if (cachefilename[i] >= 'A' && cachefilename[i] <= 'Z')
572 cachefilename[i] += 'a' - 'A';
574 cachefile = COM_LoadMallocFile(cachefilename, true);
577 if (cachefile[0] == 'D' && cachefile[1] == 'P' && cachefile[2] == 'C' && cachefile[3] == 'T')
579 memcpy(glt->texels[0], cachefile + 4, width3*height3*4);
581 // Con_Printf("loaded cache texture %s\n", cachefilename);
588 if (width == width3 && height == height3) // perfect match
590 if (bytesperpixel == 1) // 8bit
591 Image_Copy8bitRGBA(data, glt->texels[0], width*height, d_8to24table);
593 Image_CopyRGBAGamma(data, glt->texels[0], width*height);
595 else if (width == width2 && height == height2) // perfect match for top level, but needs to be reduced
598 temptexels2 = qmalloc(width2*height2*4); // scaleup buffer
599 if (bytesperpixel == 1) // 8bit
600 Image_Copy8bitRGBA(data, temptexels2, width*height, d_8to24table);
602 Image_CopyRGBAGamma(data, temptexels2, width*height);
603 while (width2 > width3 || height2 > height3)
605 w = width2;h = height2;
606 if (width2 > width3) width2 >>= 1;
607 if (height2 > height3) height2 >>= 1;
608 if (width2 <= width3 && height2 <= height3) // size achieved
609 GL_MipReduce(temptexels2, glt->texels[0], w, h, width3, height3);
611 GL_MipReduce(temptexels2, temptexels2, w, h, width3, height3);
618 // pre-scaleup buffer
619 temptexels = qmalloc(width*height*4);
620 if (bytesperpixel == 1) // 8bit
621 Image_Copy8bitRGBA(data, temptexels, width*height, d_8to24table);
623 Image_CopyRGBAGamma(data, temptexels, width*height);
624 if (width2 != width3 || height2 != height3) // reduced by gl_pic_mip or gl_max_size
627 temptexels2 = qmalloc(width2*height2*4); // scaleup buffer
628 GL_ResampleTexture(temptexels, width, height, temptexels2, width2, height2);
629 while (width2 > width3 || height2 > height3)
631 w = width2;h = height2;
632 if (width2 > width3) width2 >>= 1;
633 if (height2 > height3) height2 >>= 1;
634 if (width2 <= width3 && height2 <= height3) // size achieved
635 GL_MipReduce(temptexels2, glt->texels[0], w, h, width3, height3);
637 GL_MipReduce(temptexels2, temptexels2, w, h, width3, height3);
641 else // copy directly
642 GL_ResampleTexture(temptexels, width, height, glt->texels[0], width2, height2);
646 // LordHavoc: texture caching, turned out to be a waste of time (and immense waste of diskspace)
647 Con_Printf("writing cache texture %s\n", cachefilename);
648 cachefile = qmalloc(width3*height3*4 + 4);
653 memcpy(cachefile + 4, glt->texels[0], width3*height3*4);
654 COM_WriteFile(cachefilename, cachefile, width3*height3*4 + 4);
660 byte *in = glt->texels[0] + 3;
661 for (i = 0;i < width*height;i++, in += 4)
668 // this loop is skipped if there are no mipmaps to generate
669 for (mip = 1;mip < MAXMIPS && glt->texels[mip];mip++)
670 GL_MipReduce(glt->texels[mip-1], glt->texels[mip], glt->texelsize[mip-1][0], glt->texelsize[mip-1][1], 1, 1);
671 GL_UploadTexture(glt);
674 // if (bytesperpixel == 1) // 8bit
675 // GL_Upload8 (data, width, height, mipmap, alpha);
677 // GL_Upload32 (data, width, height, mipmap, true);