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 (&r_lerpimages);
596 Cvar_RegisterVariable (&r_precachetextures);
597 Cvar_RegisterVariable (&gl_texture_anisotropy);
598 Cvar_RegisterVariable (&gl_texturecompression);
599 Cvar_RegisterVariable (&gl_texturecompression_color);
600 Cvar_RegisterVariable (&gl_texturecompression_normal);
601 Cvar_RegisterVariable (&gl_texturecompression_gloss);
602 Cvar_RegisterVariable (&gl_texturecompression_glow);
603 Cvar_RegisterVariable (&gl_texturecompression_2d);
604 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
605 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
606 Cvar_RegisterVariable (&gl_texturecompression_sky);
607 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
609 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
612 void R_Textures_Frame (void)
614 static int old_aniso = 0;
616 // could do procedural texture animation here, if we keep track of which
617 // textures were accessed this frame...
619 // free the resize buffers
620 resizebuffersize = 0;
623 Mem_Free(resizebuffer);
626 if (colorconvertbuffer)
628 Mem_Free(colorconvertbuffer);
629 colorconvertbuffer = NULL;
632 if (old_aniso != gl_texture_anisotropy.integer)
635 gltexturepool_t *pool;
638 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
640 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
643 for (pool = gltexturepoolchain;pool;pool = pool->next)
645 for (glt = pool->gltchain;glt;glt = glt->chain)
647 // only update already uploaded images
648 if ((glt->flags & (GLTEXF_UPLOAD | TEXF_MIPMAP)) == TEXF_MIPMAP)
650 qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
652 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
653 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
655 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
662 void R_MakeResizeBufferBigger(int size)
664 if (resizebuffersize < size)
666 resizebuffersize = size;
668 Mem_Free(resizebuffer);
669 if (colorconvertbuffer)
670 Mem_Free(colorconvertbuffer);
671 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
672 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
673 if (!resizebuffer || !colorconvertbuffer)
674 Host_Error("R_Upload: out of memory");
678 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
680 int textureenum = gltexturetypeenums[texturetype];
681 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
685 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
687 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
688 if (gl_texture_anisotropy.integer != aniso)
689 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
690 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
692 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
693 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
694 if (gltexturetypedimensions[texturetype] >= 3)
696 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
700 if (flags & TEXF_FORCENEAREST)
702 if (flags & TEXF_MIPMAP)
704 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
708 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
710 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
712 else if (flags & TEXF_FORCELINEAR)
714 if (flags & TEXF_MIPMAP)
716 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
718 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
722 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
727 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
729 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
733 if (flags & TEXF_MIPMAP)
735 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
739 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
741 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
744 if (textype == TEXTYPE_SHADOWMAP)
746 if (vid.support.arb_shadow)
748 if (flags & TEXF_COMPARE)
750 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
754 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
756 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
758 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
764 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
766 int i, mip, width, height, depth;
768 const unsigned char *prevbuffer;
773 // we need to restore the texture binding after finishing the upload
774 qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
775 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
777 // these are rounded up versions of the size to do better resampling
778 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
780 width = glt->inputwidth;
781 height = glt->inputheight;
782 depth = glt->inputdepth;
786 for (width = 1;width < glt->inputwidth ;width <<= 1);
787 for (height = 1;height < glt->inputheight;height <<= 1);
788 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
791 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
792 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
794 if (prevbuffer == NULL)
796 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
797 prevbuffer = resizebuffer;
799 else if (glt->textype->textype == TEXTYPE_PALETTE)
801 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
802 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
803 prevbuffer = colorconvertbuffer;
806 if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP | GLTEXF_UPLOAD)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth)
808 // update a portion of the image
809 switch(glt->texturetype)
811 case GLTEXTURETYPE_2D:
812 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
814 case GLTEXTURETYPE_3D:
815 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
818 Host_Error("R_Upload: partial update of type other than 2D");
824 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
825 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
827 // upload the image for the first time
828 glt->flags &= ~GLTEXF_UPLOAD;
830 // cubemaps contain multiple images and thus get processed a bit differently
831 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
833 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
835 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
836 prevbuffer = resizebuffer;
839 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
841 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
842 prevbuffer = resizebuffer;
846 if (vid.support.arb_texture_compression)
848 if (gl_texturecompression.integer >= 2)
849 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
851 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
854 switch(glt->texturetype)
856 case GLTEXTURETYPE_2D:
857 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
858 if (glt->flags & TEXF_MIPMAP)
860 while (width > 1 || height > 1 || depth > 1)
862 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
863 prevbuffer = resizebuffer;
864 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
868 case GLTEXTURETYPE_3D:
869 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
870 if (glt->flags & TEXF_MIPMAP)
872 while (width > 1 || height > 1 || depth > 1)
874 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
875 prevbuffer = resizebuffer;
876 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
880 case GLTEXTURETYPE_CUBEMAP:
881 // convert and upload each side in turn,
882 // from a continuous block of input texels
883 texturebuffer = (unsigned char *)prevbuffer;
884 for (i = 0;i < 6;i++)
886 prevbuffer = texturebuffer;
887 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
888 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
890 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
891 prevbuffer = resizebuffer;
894 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
896 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
897 prevbuffer = resizebuffer;
900 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
901 if (glt->flags & TEXF_MIPMAP)
903 while (width > 1 || height > 1 || depth > 1)
905 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
906 prevbuffer = resizebuffer;
907 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
912 case GLTEXTURETYPE_RECTANGLE:
913 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
916 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
918 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
921 int R_RealGetTexture(rtexture_t *rt)
926 glt = (gltexture_t *)rt;
927 if (glt->flags & GLTEXF_DYNAMIC)
928 R_UpdateDynamicTexture(glt);
929 if (glt->flags & GLTEXF_UPLOAD)
932 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
933 R_Upload(glt, glt->inputtexels, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
934 if (glt->inputtexels)
936 Mem_Free(glt->inputtexels);
937 glt->inputtexels = NULL;
938 glt->flags |= GLTEXF_DESTROYED;
940 else if (glt->flags & GLTEXF_DESTROYED)
941 Con_Printf("R_GetTexture: Texture %s already uploaded and destroyed. Can not upload original image again. Uploaded blank texture.\n", glt->identifier);
950 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)
954 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
955 textypeinfo_t *texinfo;
958 if (cls.state == ca_dedicated)
961 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
963 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
966 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
968 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
971 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
973 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
977 texinfo = R_GetTexTypeInfo(textype, flags);
978 size = width * height * depth * sides * texinfo->inputbytesperpixel;
981 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
985 // clear the alpha flag if the texture has no transparent pixels
988 case TEXTYPE_PALETTE:
989 if (flags & TEXF_ALPHA)
991 flags &= ~TEXF_ALPHA;
994 for (i = 0;i < size;i++)
996 if (((unsigned char *)&palette[data[i]])[3] < 255)
1007 if (flags & TEXF_ALPHA)
1009 flags &= ~TEXF_ALPHA;
1012 for (i = 3;i < size;i += 4)
1016 flags |= TEXF_ALPHA;
1023 case TEXTYPE_SHADOWMAP:
1026 Host_Error("R_LoadTexture: unknown texture type");
1029 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1031 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1033 glt->chain = pool->gltchain;
1034 pool->gltchain = glt;
1035 glt->inputwidth = width;
1036 glt->inputheight = height;
1037 glt->inputdepth = depth;
1038 glt->flags = flags | GLTEXF_UPLOAD;
1039 glt->textype = texinfo;
1040 glt->texturetype = texturetype;
1041 glt->inputdatasize = size;
1042 glt->palette = palette;
1043 glt->glinternalformat = texinfo->glinternalformat;
1044 glt->glformat = texinfo->glformat;
1045 glt->gltype = texinfo->gltype;
1046 glt->bytesperpixel = texinfo->internalbytesperpixel;
1047 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1049 // init the dynamic texture attributes, too [11/22/2007 Black]
1050 glt->dirtytexnum = 0;
1051 glt->updatecallback = NULL;
1052 glt->updatacallback_data = NULL;
1054 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1057 if (glt->flags & TEXF_ALWAYSPRECACHE)
1059 else if (r_precachetextures.integer >= 2)
1061 else if (r_precachetextures.integer >= 1)
1062 if (glt->flags & TEXF_PRECACHE)
1067 // immediate upload (most common case)
1068 // data may be NULL (blank texture for dynamic rendering)
1070 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1071 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1075 // deferred texture upload (menu graphics)
1076 // optimize first if possible
1077 if ((textype == TEXTYPE_BGRA || textype == TEXTYPE_RGBA) && glt->inputwidth * glt->inputheight * glt->inputdepth > glt->tilewidth * glt->tileheight * glt->tiledepth)
1079 glt->inputtexels = (unsigned char *)Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1080 Image_Resample32(data, glt->inputwidth, glt->inputheight, glt->inputdepth, glt->inputtexels, glt->tilewidth, glt->tileheight, glt->tiledepth, r_lerpimages.integer);
1081 // change texture size accordingly
1082 glt->inputwidth = glt->tilewidth;
1083 glt->inputheight = glt->tileheight;
1084 glt->inputdepth = glt->tiledepth;
1085 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1089 glt->inputtexels = (unsigned char *)Mem_Alloc(texturemempool, size);
1090 memcpy(glt->inputtexels, data, size);
1094 // texture converting and uploading can take a while, so make sure we're sending keepalives
1095 CL_KeepaliveMessage(false);
1097 return (rtexture_t *)glt;
1100 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)
1102 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1105 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)
1107 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1110 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)
1112 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1115 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1117 int flags = TEXF_ALWAYSPRECACHE | TEXF_CLAMP;
1119 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1121 flags |= TEXF_FORCENEAREST;
1122 if (precision <= 16)
1123 flags |= TEXF_LOWPRECISION;
1127 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1129 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1132 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1134 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1137 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1139 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1142 int R_TextureWidth(rtexture_t *rt)
1144 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1147 int R_TextureHeight(rtexture_t *rt)
1149 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1152 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1154 gltexture_t *glt = (gltexture_t *)rt;
1156 Host_Error("R_UpdateTexture: no data supplied");
1158 Host_Error("R_UpdateTexture: no texture supplied");
1160 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1161 // update part of the texture
1162 R_Upload(glt, data, x, y, 0, width, height, 1);
1165 void R_ClearTexture (rtexture_t *rt)
1167 gltexture_t *glt = (gltexture_t *)rt;
1169 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );