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_newmap()
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)
257 Sys_Error("R_ResampleTexture: output width must be a multiple of 4");
259 if (r_lerpimages.value)
261 int i, j, yi, oldy, f, fstep, endy = (inheight-1);
262 byte *inrow, *out, *row1, *row2;
264 fstep = (int) (inheight*65536.0f/outheight);
266 row1 = qmalloc(outwidth*4);
267 row2 = qmalloc(outwidth*4);
270 R_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
271 R_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
272 for (i = 0, f = 0;i < outheight;i++,f += fstep)
277 int lerp = f & 0xFFFF;
280 inrow = (byte *)indata + inwidth*4*yi;
282 memcpy(row1, row2, outwidth*4);
284 R_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
285 R_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
288 for (j = 0;j < outwidth;j += 4)
290 out[ 0] = (byte) ((((row2[ 0] - row1[ 0]) * lerp) >> 16) + row1[ 0]);
291 out[ 1] = (byte) ((((row2[ 1] - row1[ 1]) * lerp) >> 16) + row1[ 1]);
292 out[ 2] = (byte) ((((row2[ 2] - row1[ 2]) * lerp) >> 16) + row1[ 2]);
293 out[ 3] = (byte) ((((row2[ 3] - row1[ 3]) * lerp) >> 16) + row1[ 3]);
294 out[ 4] = (byte) ((((row2[ 4] - row1[ 4]) * lerp) >> 16) + row1[ 4]);
295 out[ 5] = (byte) ((((row2[ 5] - row1[ 5]) * lerp) >> 16) + row1[ 5]);
296 out[ 6] = (byte) ((((row2[ 6] - row1[ 6]) * lerp) >> 16) + row1[ 6]);
297 out[ 7] = (byte) ((((row2[ 7] - row1[ 7]) * lerp) >> 16) + row1[ 7]);
298 out[ 8] = (byte) ((((row2[ 8] - row1[ 8]) * lerp) >> 16) + row1[ 8]);
299 out[ 9] = (byte) ((((row2[ 9] - row1[ 9]) * lerp) >> 16) + row1[ 9]);
300 out[10] = (byte) ((((row2[10] - row1[10]) * lerp) >> 16) + row1[10]);
301 out[11] = (byte) ((((row2[11] - row1[11]) * lerp) >> 16) + row1[11]);
302 out[12] = (byte) ((((row2[12] - row1[12]) * lerp) >> 16) + row1[12]);
303 out[13] = (byte) ((((row2[13] - row1[13]) * lerp) >> 16) + row1[13]);
304 out[14] = (byte) ((((row2[14] - row1[14]) * lerp) >> 16) + row1[14]);
305 out[15] = (byte) ((((row2[15] - row1[15]) * lerp) >> 16) + row1[15]);
317 inrow = (byte *)indata + inwidth*4*yi;
319 memcpy(row1, row2, outwidth*4);
321 R_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
324 memcpy(out, row1, outwidth * 4);
333 unsigned frac, fracstep;
334 // relies on int being 4 bytes
338 fracstep = inwidth*0x10000/outwidth;
339 for (i = 0;i < outheight;i++)
341 inrow = (int *)indata + inwidth*(i*inheight/outheight);
342 frac = fracstep >> 1;
343 for (j = 0;j < outwidth;j += 4)
345 out[0] = inrow[frac >> 16];frac += fracstep;
346 out[1] = inrow[frac >> 16];frac += fracstep;
347 out[2] = inrow[frac >> 16];frac += fracstep;
348 out[3] = inrow[frac >> 16];frac += fracstep;
355 // in can be the same as out
356 void GL_MipReduce(byte *in, byte *out, int width, int height, int destwidth, int destheight)
358 int x, y, width2, height2, nextrow;
359 if (width > destwidth)
361 if (height > destheight)
365 height2 = height >> 1;
366 nextrow = width << 2;
367 for (y = 0;y < height2;y++)
369 for (x = 0;x < width2;x++)
371 out[0] = (byte) ((in[0] + in[4] + in[nextrow ] + in[nextrow+4]) >> 2);
372 out[1] = (byte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
373 out[2] = (byte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
374 out[3] = (byte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
378 in += nextrow; // skip a line
385 for (y = 0;y < height;y++)
387 for (x = 0;x < width2;x++)
389 out[0] = (byte) ((in[0] + in[4]) >> 1);
390 out[1] = (byte) ((in[1] + in[5]) >> 1);
391 out[2] = (byte) ((in[2] + in[6]) >> 1);
392 out[3] = (byte) ((in[3] + in[7]) >> 1);
401 if (height > destheight)
404 height2 = height >> 1;
405 nextrow = width << 2;
406 for (y = 0;y < height2;y++)
408 for (x = 0;x < width;x++)
410 out[0] = (byte) ((in[0] + in[nextrow ]) >> 1);
411 out[1] = (byte) ((in[1] + in[nextrow+1]) >> 1);
412 out[2] = (byte) ((in[2] + in[nextrow+2]) >> 1);
413 out[3] = (byte) ((in[3] + in[nextrow+3]) >> 1);
417 in += nextrow; // skip a line
421 Sys_Error("GL_MipReduce: desired size already achieved\n");
425 void GL_Upload32(int glslot, byte *data, int width, int height, int flags)
427 int mip, width2, height2, width3, height3, internalformat;
428 byte *gammadata, *buffer;
433 // 3 and 4 are converted by the driver to it's preferred format for the current display mode
435 if (flags & TEXF_ALPHA)
438 // calculate power of 2 size
439 width2 = 1;while (width2 < width) width2 <<= 1;
440 height2 = 1;while (height2 < height) height2 <<= 1;
441 // calculate final size (mipmapped downward to this)
442 width3 = width2 >> (int) r_picmip.value;
443 height3 = height2 >> (int) r_picmip.value;
444 while (width3 > (int) r_max_size.value) width3 >>= 1;
445 while (height3 > (int) r_max_size.value) height3 >>= 1;
446 if (width3 < 1) width3 = 1;
447 if (height3 < 1) height3 = 1;
449 gammadata = qmalloc(width*height*4);
450 buffer = qmalloc(width2*height2*4);
451 if (!gammadata || !buffer)
452 Host_Error("GL_Upload32: out of memory\n");
454 Image_CopyRGBAGamma(data, gammadata, width*height);
456 R_ResampleTexture(gammadata, width, height, buffer, width2, height2);
460 while (width2 > width3 || height2 > height3)
462 GL_MipReduce(buffer, buffer, width2, height2, width3, height3);
466 if (height2 > height3)
470 glBindTexture(GL_TEXTURE_2D, glslot);
472 glTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
473 if (flags & TEXF_MIPMAP)
475 while (width2 > 1 || height2 > 1)
477 GL_MipReduce(buffer, buffer, width2, height2, 1, 1);
484 glTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
487 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
488 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
492 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
493 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
495 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
500 void GL_Upload8 (int glslot, byte *data, int width, int height, int flags)
503 data32 = qmalloc(width*height*4);
504 Image_Copy8bitRGBA(data, data32, width*height, d_8to24table);
505 GL_Upload32(glslot, data32, width, height, flags);
509 void GL_UploadTexture (gltexture_t *glt)
511 if (glt->inputtexels == NULL)
513 if (glt->flags & TEXF_RGBA)
514 GL_Upload32(glt->texnum, glt->inputtexels, glt->width, glt->height, glt->flags);
516 GL_Upload8(glt->texnum, glt->inputtexels, glt->width, glt->height, glt->flags);
517 glt->internalflags |= GLTEXF_UPLOADED;
518 qfree(glt->inputtexels);
519 glt->inputtexels = NULL;
522 int R_CalcTexelDataSize (int width, int height, int mipmapped)
524 int width2, height2, size;
525 width2 = 1;while (width2 < width) width2 <<= 1;
526 height2 = 1;while (height2 < height) height2 <<= 1;
527 // calculate final size (mipmapped downward to this)
528 width2 >>= (int) r_picmip.value;
529 height2 >>= (int) r_picmip.value;
530 while (width2 > (int) r_max_size.value) width2 >>= 1;
531 while (height2 > (int) r_max_size.value) height2 >>= 1;
532 if (width2 < 1) width2 = 1;
533 if (height2 < 1) height2 = 1;
538 while (width2 > 1 || height2 > 1)
540 size += width2 * height2;
546 size++; // count the last 1x1 mipmap
549 size = width2*height2;
561 rtexture_t *R_LoadTexture (char *identifier, int width, int height, byte *data, int flags)
563 int i, bytesperpixel, internalflags, precache;
571 Host_Error("R_LoadTexture: no identifier\n");
573 // clear the alpha flag if the texture has no transparent pixels
574 if (flags & TEXF_ALPHA)
577 if (flags & TEXF_RGBA)
579 for (i = 0;i < width * height;i++)
581 if (data[i * 4 + 3] < 255)
590 for (i = 0;i < width * height;i++)
600 flags &= ~TEXF_ALPHA;
603 if (flags & TEXF_RGBA)
609 if (r_lerpimages.value != 0)
610 internalflags |= GLTEXF_LERPED;
612 // LordHavoc: do a CRC to confirm the data really is the same as previous occurances.
613 crc = CRC_Block(data, width*height*bytesperpixel);
614 // see if the texture is already present
615 for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
617 if (!strcmp (identifier, glt->identifier))
619 // LordHavoc: everyone hates cache mismatchs, so I fixed it
620 if (crc != glt->crc || width != glt->width || height != glt->height || flags != glt->flags)
622 Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n");
623 goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
625 if (internalflags != glt->internalflags)
626 goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
627 return (rtexture_t *)glt;
635 strcpy (glt->identifier, identifier);
640 // LordHavoc: check if there are still slots available
641 if (numgltextures >= MAX_GLTEXTURES)
642 Sys_Error ("GL_LoadTexture: ran out of texture slots (%d)\n", MAX_GLTEXTURES);
643 glt = &gltextures[numgltextures++];
644 glt->texnum = gl_texture_number++;
645 strcpy (glt->identifier, identifier);
648 // LordHavoc: label to drop out of the loop into the setup code
649 GL_LoadTexture_setup:
650 glt->crc = crc; // LordHavoc: used to verify textures are identical
652 glt->height = height;
654 glt->internalflags = internalflags;
656 if (glt->inputtexels)
657 qfree(glt->inputtexels);
658 glt->inputtexeldatasize = width*height*bytesperpixel;
659 glt->inputtexels = qmalloc(glt->inputtexeldatasize);
661 memcpy(glt->inputtexels, data, glt->inputtexeldatasize);
663 glt->texeldatasize = R_CalcTexelDataSize(width, height, flags & TEXF_MIPMAP);
666 if (r_precachetextures.value >= 1)
668 if (flags & TEXF_PRECACHE)
670 if (r_precachetextures.value >= 2)
675 GL_UploadTexture(glt);
677 return (rtexture_t *)glt;
680 // only used for lightmaps
681 int R_GetTextureSlots(int count)
684 i = gl_texture_number;
685 gl_texture_number += count;