7 cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
8 cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
9 cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0", "reduces resolution of textures by powers of 2, for example 1 will halve width/height, reducing texture memory usage by 75%"};
10 cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1", "bilinear filters images when scaling them up to power of 2 size (mode 1), looks better than glquake (mode 0)"};
11 cvar_t r_precachetextures = {CVAR_SAVE, "r_precachetextures", "1", "0 = never upload textures until used, 1 = upload most textures before use (exceptions: rarely used skin colormap layers), 2 = upload all textures before use (can increase texture memory usage significantly)"};
12 cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
13 cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
14 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
15 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
16 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
17 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
18 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
19 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
20 cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
21 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
22 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
24 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
25 int gl_filter_mag = GL_LINEAR;
28 static mempool_t *texturemempool;
30 // note: this must not conflict with TEXF_ flags in r_textures.h
31 // cleared when a texture is uploaded
32 #define GLTEXF_UPLOAD 0x00010000
33 // bitmask for mismatch checking
34 #define GLTEXF_IMPORTANTBITS (0)
35 // set when image is uploaded and freed
36 #define GLTEXF_DESTROYED 0x00040000
37 // dynamic texture (treat texnum == 0 differently)
38 #define GLTEXF_DYNAMIC 0x00080000
40 typedef struct textypeinfo_s
43 int inputbytesperpixel;
44 int internalbytesperpixel;
45 float glinternalbytesperpixel;
52 static textypeinfo_t textype_palette = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 3, GL_UNSIGNED_BYTE};
53 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 4, GL_UNSIGNED_BYTE};
54 static textypeinfo_t textype_palette_compress = {TEXTYPE_PALETTE, 1, 4, 0.5f, GL_BGRA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
55 static textypeinfo_t textype_palette_alpha_compress = {TEXTYPE_PALETTE, 1, 4, 1.0f, GL_BGRA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
56 static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 3, GL_UNSIGNED_BYTE};
57 static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 4, GL_UNSIGNED_BYTE};
58 static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_RGBA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
59 static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_RGBA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
60 static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 3, GL_UNSIGNED_BYTE};
61 static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 4, GL_UNSIGNED_BYTE};
62 static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_BGRA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
63 static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_BGRA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
64 static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP,2,2, 2.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16_ARB, GL_UNSIGNED_SHORT};
65 static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP,4,4, 4.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24_ARB, GL_UNSIGNED_INT};
67 typedef enum gltexturetype_e
71 GLTEXTURETYPE_CUBEMAP,
72 GLTEXTURETYPE_RECTANGLE,
77 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
78 static int gltexturetypebindingenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB, GL_TEXTURE_BINDING_RECTANGLE_ARB};
79 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
80 static int cubemapside[6] =
82 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
83 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
84 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
85 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
86 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
87 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
90 typedef struct gltexture_s
92 // this field is exposed to the R_GetTexture macro, for speed reasons
93 // (must be identical in rtexture_t)
94 int texnum; // GL texture slot number
96 // dynamic texture stuff [11/22/2007 Black]
97 // used to hold the texture number of dirty textures
99 updatecallback_t updatecallback;
100 void *updatacallback_data;
101 // --- [11/22/2007 Black]
103 // pointer to texturepool (check this to see if the texture is allocated)
104 struct gltexturepool_s *pool;
105 // pointer to next texture in texturepool chain
106 struct gltexture_s *chain;
107 // name of the texture (this might be removed someday), no duplicates
108 char identifier[MAX_QPATH + 32];
109 // original data size in *inputtexels
110 int inputwidth, inputheight, inputdepth;
111 // copy of the original texture(s) supplied to the upload function, for
112 // delayed uploads (non-precached)
113 unsigned char *inputtexels;
114 // original data size in *inputtexels
116 // flags supplied to the LoadTexture function
117 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
119 // pointer to one of the textype_ structs
120 textypeinfo_t *textype;
121 // one of the GLTEXTURETYPE_ values
123 // palette if the texture is TEXTYPE_PALETTE
124 const unsigned int *palette;
125 // actual stored texture size after gl_picmip and gl_max_size are applied
126 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
127 int tilewidth, tileheight, tiledepth;
128 // 1 or 6 depending on texturetype
132 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
135 int glinternalformat;
136 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
141 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
143 typedef struct gltexturepool_s
145 unsigned int sentinel;
146 struct gltexture_s *gltchain;
147 struct gltexturepool_s *next;
151 static gltexturepool_t *gltexturepoolchain = NULL;
153 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
154 static int resizebuffersize = 0;
155 static const unsigned char *texturebuffer;
156 static int texturebuffersize = 0;
158 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
160 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
162 if (flags & TEXF_ALPHA)
166 case TEXTYPE_PALETTE:
167 return &textype_palette_alpha_compress;
169 return &textype_rgba_alpha_compress;
171 return &textype_bgra_alpha_compress;
173 Host_Error("R_GetTexTypeInfo: unknown texture format");
181 case TEXTYPE_PALETTE:
182 return &textype_palette_compress;
184 return &textype_rgba_compress;
186 return &textype_bgra_compress;
188 Host_Error("R_GetTexTypeInfo: unknown texture format");
195 if (flags & TEXF_ALPHA)
199 case TEXTYPE_PALETTE:
200 return &textype_palette_alpha;
202 return &textype_rgba_alpha;
204 return &textype_bgra_alpha;
206 Host_Error("R_GetTexTypeInfo: unknown texture format");
214 case TEXTYPE_PALETTE:
215 return &textype_palette;
217 return &textype_rgba;
219 return &textype_bgra;
220 case TEXTYPE_SHADOWMAP:
221 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
223 Host_Error("R_GetTexTypeInfo: unknown texture format");
228 return NULL; // this line only to hush compiler warnings
231 // dynamic texture code [11/22/2007 Black]
232 void R_MarkDirtyTexture(rtexture_t *rt) {
233 gltexture_t *glt = (gltexture_t*) rt;
238 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
239 if( !glt->dirtytexnum && glt->flags & GLTEXF_DYNAMIC ) {
240 glt->dirtytexnum = glt->texnum;
241 // mark it as dirty, so R_RealGetTexture gets called
246 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
247 gltexture_t *glt = (gltexture_t*) rt;
252 glt->flags |= GLTEXF_DYNAMIC;
253 glt->updatecallback = updatecallback;
254 glt->updatacallback_data = data;
255 glt->dirtytexnum = 0;
258 static void R_UpdateDynamicTexture(gltexture_t *glt) {
259 glt->texnum = glt->dirtytexnum;
260 // reset dirtytexnum again (not dirty anymore)
261 glt->dirtytexnum = 0;
262 // TODO: now assert that t->texnum != 0 ?
263 if( glt->updatecallback ) {
264 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
268 void R_PurgeTexture(rtexture_t *rt)
270 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
275 void R_FreeTexture(rtexture_t *rt)
277 gltexture_t *glt, **gltpointer;
279 glt = (gltexture_t *)rt;
281 Host_Error("R_FreeTexture: texture == NULL");
283 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
284 if (*gltpointer == glt)
285 *gltpointer = glt->chain;
287 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
289 if (!(glt->flags & GLTEXF_UPLOAD))
292 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
295 if (glt->inputtexels)
296 Mem_Free(glt->inputtexels);
300 rtexturepool_t *R_AllocTexturePool(void)
302 gltexturepool_t *pool;
303 if (texturemempool == NULL)
305 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
308 pool->next = gltexturepoolchain;
309 gltexturepoolchain = pool;
310 pool->sentinel = TEXTUREPOOL_SENTINEL;
311 return (rtexturepool_t *)pool;
314 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
316 gltexturepool_t *pool, **poolpointer;
317 if (rtexturepool == NULL)
319 if (*rtexturepool == NULL)
321 pool = (gltexturepool_t *)(*rtexturepool);
322 *rtexturepool = NULL;
323 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
324 Host_Error("R_FreeTexturePool: pool already freed");
325 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
326 if (*poolpointer == pool)
327 *poolpointer = pool->next;
329 Host_Error("R_FreeTexturePool: pool not linked");
330 while (pool->gltchain)
331 R_FreeTexture((rtexture_t *)pool->gltchain);
336 typedef struct glmode_s
339 int minification, magnification;
343 static glmode_t modes[6] =
345 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
346 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
347 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
348 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
349 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
350 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
353 static void GL_TextureMode_f (void)
358 gltexturepool_t *pool;
362 for (i = 0;i < 6;i++)
364 if (gl_filter_min == modes[i].minification)
366 Con_Printf("%s\n", modes[i].name);
370 Con_Print("current filter is unknown???\n");
374 for (i = 0;i < 6;i++)
375 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
379 Con_Print("bad filter name\n");
383 gl_filter_min = modes[i].minification;
384 gl_filter_mag = modes[i].magnification;
386 // change all the existing mipmap texture objects
387 // FIXME: force renderer(/client/something?) restart instead?
389 for (pool = gltexturepoolchain;pool;pool = pool->next)
391 for (glt = pool->gltchain;glt;glt = glt->chain)
393 // only update already uploaded images
394 if (!(glt->flags & (GLTEXF_UPLOAD | TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
396 qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
397 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
398 if (glt->flags & TEXF_MIPMAP)
400 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
404 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
406 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
407 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
413 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
415 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
420 case GLTEXTURETYPE_2D:
421 maxsize = vid.maxtexturesize_2d;
422 if (flags & TEXF_PICMIP)
424 maxsize = bound(1, gl_max_size.integer, maxsize);
425 picmip = gl_picmip.integer;
428 case GLTEXTURETYPE_3D:
429 maxsize = vid.maxtexturesize_3d;
431 case GLTEXTURETYPE_CUBEMAP:
432 maxsize = vid.maxtexturesize_cubemap;
438 if (vid.support.arb_texture_non_power_of_two)
439 width2 = min(inwidth >> picmip, maxsize);
442 for (width2 = 1;width2 < inwidth;width2 <<= 1);
443 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
445 *outwidth = max(1, width2);
449 if (vid.support.arb_texture_non_power_of_two)
450 height2 = min(inheight >> picmip, maxsize);
453 for (height2 = 1;height2 < inheight;height2 <<= 1);
454 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
456 *outheight = max(1, height2);
460 if (vid.support.arb_texture_non_power_of_two)
461 depth2 = min(indepth >> picmip, maxsize);
464 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
465 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
467 *outdepth = max(1, depth2);
472 static int R_CalcTexelDataSize (gltexture_t *glt)
474 int width2, height2, depth2, size;
476 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
478 size = width2 * height2 * depth2;
480 if (glt->flags & TEXF_MIPMAP)
482 while (width2 > 1 || height2 > 1 || depth2 > 1)
490 size += width2 * height2 * depth2;
494 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
497 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
501 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
502 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
504 gltexturepool_t *pool;
506 Con_Print("glsize input loaded mip alpha name\n");
507 for (pool = gltexturepoolchain;pool;pool = pool->next)
515 for (glt = pool->gltchain;glt;glt = glt->chain)
517 glsize = R_CalcTexelDataSize(glt);
518 isloaded = !(glt->flags & GLTEXF_UPLOAD);
520 pooltotalt += glsize;
521 pooltotalp += glt->inputdatasize;
525 poolloadedt += glsize;
526 poolloadedp += glt->inputdatasize;
529 Con_Printf("%c%4i%c%c%4i%c %s %s %s %s\n", isloaded ? '[' : ' ', (glsize + 1023) / 1024, isloaded ? ']' : ' ', glt->inputtexels ? '[' : ' ', (glt->inputdatasize + 1023) / 1024, glt->inputtexels ? ']' : ' ', isloaded ? "loaded" : " ", (glt->flags & TEXF_MIPMAP) ? "mip" : " ", (glt->flags & TEXF_ALPHA) ? "alpha" : " ", glt->identifier);
532 Con_Printf("texturepool %10p total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", (void *)pool, pooltotal, pooltotalt / 1048576.0, pooltotalp / 1048576.0, poolloaded, poolloadedt / 1048576.0, poolloadedp / 1048576.0, pooltotal - poolloaded, (pooltotalt - poolloadedt) / 1048576.0, (pooltotalp - poolloadedp) / 1048576.0);
533 sumtotal += pooltotal;
534 sumtotalt += pooltotalt;
535 sumtotalp += pooltotalp;
536 sumloaded += poolloaded;
537 sumloadedt += poolloadedt;
538 sumloadedp += poolloadedp;
541 Con_Printf("textures total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", sumtotal, sumtotalt / 1048576.0, sumtotalp / 1048576.0, sumloaded, sumloadedt / 1048576.0, sumloadedp / 1048576.0, sumtotal - sumloaded, (sumtotalt - sumloadedt) / 1048576.0, (sumtotalp - sumloadedp) / 1048576.0);
544 static void R_TextureStats_f(void)
546 R_TextureStats_Print(true, true, true);
549 static void r_textures_start(void)
551 // LordHavoc: allow any alignment
553 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
554 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
556 texturemempool = Mem_AllocPool("texture management", 0, NULL);
558 // Disable JPEG screenshots if the DLL isn't loaded
559 if (! JPEG_OpenLibrary ())
560 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
561 // TODO: support png screenshots?
565 static void r_textures_shutdown(void)
567 rtexturepool_t *temp;
569 JPEG_CloseLibrary ();
571 while(gltexturepoolchain)
573 temp = (rtexturepool_t *) gltexturepoolchain;
574 R_FreeTexturePool(&temp);
577 resizebuffersize = 0;
578 texturebuffersize = 0;
580 colorconvertbuffer = NULL;
581 texturebuffer = NULL;
582 Mem_FreePool(&texturemempool);
585 static void r_textures_newmap(void)
589 void R_Textures_Init (void)
591 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
592 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
593 Cvar_RegisterVariable (&gl_max_size);
594 Cvar_RegisterVariable (&gl_picmip);
595 Cvar_RegisterVariable (&gl_max_lightmapsize);
596 Cvar_RegisterVariable (&r_lerpimages);
597 Cvar_RegisterVariable (&r_precachetextures);
598 Cvar_RegisterVariable (&gl_texture_anisotropy);
599 Cvar_RegisterVariable (&gl_texturecompression);
600 Cvar_RegisterVariable (&gl_texturecompression_color);
601 Cvar_RegisterVariable (&gl_texturecompression_normal);
602 Cvar_RegisterVariable (&gl_texturecompression_gloss);
603 Cvar_RegisterVariable (&gl_texturecompression_glow);
604 Cvar_RegisterVariable (&gl_texturecompression_2d);
605 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
606 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
607 Cvar_RegisterVariable (&gl_texturecompression_sky);
608 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
610 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
613 void R_Textures_Frame (void)
615 static int old_aniso = 0;
617 // could do procedural texture animation here, if we keep track of which
618 // textures were accessed this frame...
620 // free the resize buffers
621 resizebuffersize = 0;
624 Mem_Free(resizebuffer);
627 if (colorconvertbuffer)
629 Mem_Free(colorconvertbuffer);
630 colorconvertbuffer = NULL;
633 if (old_aniso != gl_texture_anisotropy.integer)
636 gltexturepool_t *pool;
639 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
641 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
644 for (pool = gltexturepoolchain;pool;pool = pool->next)
646 for (glt = pool->gltchain;glt;glt = glt->chain)
648 // only update already uploaded images
649 if ((glt->flags & (GLTEXF_UPLOAD | TEXF_MIPMAP)) == TEXF_MIPMAP)
651 qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
653 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
654 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
656 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
663 void R_MakeResizeBufferBigger(int size)
665 if (resizebuffersize < size)
667 resizebuffersize = size;
669 Mem_Free(resizebuffer);
670 if (colorconvertbuffer)
671 Mem_Free(colorconvertbuffer);
672 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
673 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
674 if (!resizebuffer || !colorconvertbuffer)
675 Host_Error("R_Upload: out of memory");
679 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
681 int textureenum = gltexturetypeenums[texturetype];
682 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
686 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
688 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
689 if (gl_texture_anisotropy.integer != aniso)
690 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
691 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
693 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
694 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
695 if (gltexturetypedimensions[texturetype] >= 3)
697 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
701 if (flags & TEXF_FORCENEAREST)
703 if (flags & TEXF_MIPMAP)
705 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
709 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
711 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
713 else if (flags & TEXF_FORCELINEAR)
715 if (flags & TEXF_MIPMAP)
717 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
719 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
723 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
728 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
730 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
734 if (flags & TEXF_MIPMAP)
736 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
740 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
742 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
745 if (textype == TEXTYPE_SHADOWMAP)
747 if (vid.support.arb_shadow)
749 if (flags & TEXF_COMPARE)
751 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
755 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
757 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
759 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
765 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
767 int i, mip, width, height, depth;
769 const unsigned char *prevbuffer;
774 // we need to restore the texture binding after finishing the upload
775 qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
776 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
778 // these are rounded up versions of the size to do better resampling
779 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
781 width = glt->inputwidth;
782 height = glt->inputheight;
783 depth = glt->inputdepth;
787 for (width = 1;width < glt->inputwidth ;width <<= 1);
788 for (height = 1;height < glt->inputheight;height <<= 1);
789 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
792 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
793 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
795 if (prevbuffer == NULL)
797 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
798 prevbuffer = resizebuffer;
800 else if (glt->textype->textype == TEXTYPE_PALETTE)
802 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
803 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
804 prevbuffer = colorconvertbuffer;
807 if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP | GLTEXF_UPLOAD)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth)
809 // update a portion of the image
810 switch(glt->texturetype)
812 case GLTEXTURETYPE_2D:
813 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
815 case GLTEXTURETYPE_3D:
816 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
819 Host_Error("R_Upload: partial update of type other than 2D");
825 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
826 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
828 // upload the image for the first time
829 glt->flags &= ~GLTEXF_UPLOAD;
831 // cubemaps contain multiple images and thus get processed a bit differently
832 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
834 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
836 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
837 prevbuffer = resizebuffer;
840 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
842 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
843 prevbuffer = resizebuffer;
847 if (vid.support.arb_texture_compression)
849 if (gl_texturecompression.integer >= 2)
850 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
852 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
855 switch(glt->texturetype)
857 case GLTEXTURETYPE_2D:
858 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
859 if (glt->flags & TEXF_MIPMAP)
861 while (width > 1 || height > 1 || depth > 1)
863 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
864 prevbuffer = resizebuffer;
865 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
869 case GLTEXTURETYPE_3D:
870 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
871 if (glt->flags & TEXF_MIPMAP)
873 while (width > 1 || height > 1 || depth > 1)
875 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
876 prevbuffer = resizebuffer;
877 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
881 case GLTEXTURETYPE_CUBEMAP:
882 // convert and upload each side in turn,
883 // from a continuous block of input texels
884 texturebuffer = (unsigned char *)prevbuffer;
885 for (i = 0;i < 6;i++)
887 prevbuffer = texturebuffer;
888 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
889 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
891 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
892 prevbuffer = resizebuffer;
895 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
897 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
898 prevbuffer = resizebuffer;
901 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
902 if (glt->flags & TEXF_MIPMAP)
904 while (width > 1 || height > 1 || depth > 1)
906 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
907 prevbuffer = resizebuffer;
908 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
913 case GLTEXTURETYPE_RECTANGLE:
914 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
917 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
919 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
922 int R_RealGetTexture(rtexture_t *rt)
927 glt = (gltexture_t *)rt;
928 if (glt->flags & GLTEXF_DYNAMIC)
929 R_UpdateDynamicTexture(glt);
930 if (glt->flags & GLTEXF_UPLOAD)
933 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
934 R_Upload(glt, glt->inputtexels, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
935 if (glt->inputtexels)
937 Mem_Free(glt->inputtexels);
938 glt->inputtexels = NULL;
939 glt->flags |= GLTEXF_DESTROYED;
941 else if (glt->flags & GLTEXF_DESTROYED)
942 Con_Printf("R_GetTexture: Texture %s already uploaded and destroyed. Can not upload original image again. Uploaded blank texture.\n", glt->identifier);
951 static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, int sides, int flags, textype_t textype, int texturetype, const unsigned char *data, const unsigned int *palette)
955 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
956 textypeinfo_t *texinfo;
959 if (cls.state == ca_dedicated)
962 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
964 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
967 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
969 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
972 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
974 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
978 texinfo = R_GetTexTypeInfo(textype, flags);
979 size = width * height * depth * sides * texinfo->inputbytesperpixel;
982 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
986 // clear the alpha flag if the texture has no transparent pixels
989 case TEXTYPE_PALETTE:
990 if (flags & TEXF_ALPHA)
992 flags &= ~TEXF_ALPHA;
995 for (i = 0;i < size;i++)
997 if (((unsigned char *)&palette[data[i]])[3] < 255)
1008 if (flags & TEXF_ALPHA)
1010 flags &= ~TEXF_ALPHA;
1013 for (i = 3;i < size;i += 4)
1017 flags |= TEXF_ALPHA;
1024 case TEXTYPE_SHADOWMAP:
1027 Host_Error("R_LoadTexture: unknown texture type");
1030 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1032 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1034 glt->chain = pool->gltchain;
1035 pool->gltchain = glt;
1036 glt->inputwidth = width;
1037 glt->inputheight = height;
1038 glt->inputdepth = depth;
1039 glt->flags = flags | GLTEXF_UPLOAD;
1040 glt->textype = texinfo;
1041 glt->texturetype = texturetype;
1042 glt->inputdatasize = size;
1043 glt->palette = palette;
1044 glt->glinternalformat = texinfo->glinternalformat;
1045 glt->glformat = texinfo->glformat;
1046 glt->gltype = texinfo->gltype;
1047 glt->bytesperpixel = texinfo->internalbytesperpixel;
1048 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1050 // init the dynamic texture attributes, too [11/22/2007 Black]
1051 glt->dirtytexnum = 0;
1052 glt->updatecallback = NULL;
1053 glt->updatacallback_data = NULL;
1055 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1058 if (glt->flags & TEXF_ALWAYSPRECACHE)
1060 else if (r_precachetextures.integer >= 2)
1062 else if (r_precachetextures.integer >= 1)
1063 if (glt->flags & TEXF_PRECACHE)
1068 // immediate upload (most common case)
1069 // data may be NULL (blank texture for dynamic rendering)
1071 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1072 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1076 // deferred texture upload (menu graphics)
1077 // optimize first if possible
1078 if ((textype == TEXTYPE_BGRA || textype == TEXTYPE_RGBA) && glt->inputwidth * glt->inputheight * glt->inputdepth > glt->tilewidth * glt->tileheight * glt->tiledepth)
1080 glt->inputtexels = (unsigned char *)Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1081 Image_Resample32(data, glt->inputwidth, glt->inputheight, glt->inputdepth, glt->inputtexels, glt->tilewidth, glt->tileheight, glt->tiledepth, r_lerpimages.integer);
1082 // change texture size accordingly
1083 glt->inputwidth = glt->tilewidth;
1084 glt->inputheight = glt->tileheight;
1085 glt->inputdepth = glt->tiledepth;
1086 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1090 glt->inputtexels = (unsigned char *)Mem_Alloc(texturemempool, size);
1091 memcpy(glt->inputtexels, data, size);
1095 // texture converting and uploading can take a while, so make sure we're sending keepalives
1096 CL_KeepaliveMessage(false);
1098 return (rtexture_t *)glt;
1101 rtexture_t *R_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
1103 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1106 rtexture_t *R_LoadTexture3D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
1108 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1111 rtexture_t *R_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
1113 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1116 rtexture_t *R_LoadTextureRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
1118 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1121 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1123 int flags = TEXF_ALWAYSPRECACHE | TEXF_CLAMP;
1125 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1127 flags |= TEXF_FORCENEAREST;
1128 if (precision <= 16)
1129 flags |= TEXF_LOWPRECISION;
1133 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1135 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1138 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1140 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1143 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1145 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1148 int R_TextureWidth(rtexture_t *rt)
1150 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1153 int R_TextureHeight(rtexture_t *rt)
1155 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1158 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1160 gltexture_t *glt = (gltexture_t *)rt;
1162 Host_Error("R_UpdateTexture: no data supplied");
1164 Host_Error("R_UpdateTexture: no texture supplied");
1166 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1167 // update part of the texture
1168 R_Upload(glt, data, x, y, 0, width, height, 1);
1171 void R_ClearTexture (rtexture_t *rt)
1173 gltexture_t *glt = (gltexture_t *)rt;
1175 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );