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 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"};
12 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"};
13 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
14 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
15 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
16 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
17 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
18 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
19 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)"};
20 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
21 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
22 cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "0", "use alternate path for dynamic lightmap updates"};
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 // stores backup copy of texture for deferred texture updates (r_nopartialtextureupdates cvar)
104 unsigned char *bufferpixels;
105 qboolean buffermodified;
107 // pointer to texturepool (check this to see if the texture is allocated)
108 struct gltexturepool_s *pool;
109 // pointer to next texture in texturepool chain
110 struct gltexture_s *chain;
111 // name of the texture (this might be removed someday), no duplicates
112 char identifier[MAX_QPATH + 32];
113 // original data size in *inputtexels
114 int inputwidth, inputheight, inputdepth;
115 // copy of the original texture(s) supplied to the upload function, for
116 // delayed uploads (non-precached)
117 unsigned char *inputtexels;
118 // original data size in *inputtexels
120 // flags supplied to the LoadTexture function
121 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
123 // pointer to one of the textype_ structs
124 textypeinfo_t *textype;
125 // one of the GLTEXTURETYPE_ values
127 // palette if the texture is TEXTYPE_PALETTE
128 const unsigned int *palette;
129 // actual stored texture size after gl_picmip and gl_max_size are applied
130 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
131 int tilewidth, tileheight, tiledepth;
132 // 1 or 6 depending on texturetype
136 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
139 int glinternalformat;
140 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
145 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
147 typedef struct gltexturepool_s
149 unsigned int sentinel;
150 struct gltexture_s *gltchain;
151 struct gltexturepool_s *next;
155 static gltexturepool_t *gltexturepoolchain = NULL;
157 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
158 static int resizebuffersize = 0;
159 static const unsigned char *texturebuffer;
160 static int texturebuffersize = 0;
162 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
164 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
166 if (flags & TEXF_ALPHA)
170 case TEXTYPE_PALETTE:
171 return &textype_palette_alpha_compress;
173 return &textype_rgba_alpha_compress;
175 return &textype_bgra_alpha_compress;
177 Host_Error("R_GetTexTypeInfo: unknown texture format");
185 case TEXTYPE_PALETTE:
186 return &textype_palette_compress;
188 return &textype_rgba_compress;
190 return &textype_bgra_compress;
192 Host_Error("R_GetTexTypeInfo: unknown texture format");
199 if (flags & TEXF_ALPHA)
203 case TEXTYPE_PALETTE:
204 return &textype_palette_alpha;
206 return &textype_rgba_alpha;
208 return &textype_bgra_alpha;
210 Host_Error("R_GetTexTypeInfo: unknown texture format");
218 case TEXTYPE_PALETTE:
219 return &textype_palette;
221 return &textype_rgba;
223 return &textype_bgra;
224 case TEXTYPE_SHADOWMAP:
225 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
227 Host_Error("R_GetTexTypeInfo: unknown texture format");
232 return NULL; // this line only to hush compiler warnings
235 // dynamic texture code [11/22/2007 Black]
236 void R_MarkDirtyTexture(rtexture_t *rt) {
237 gltexture_t *glt = (gltexture_t*) rt;
242 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
243 if( !glt->dirtytexnum && glt->flags & GLTEXF_DYNAMIC ) {
244 glt->dirtytexnum = glt->texnum;
245 // mark it as dirty, so R_RealGetTexture gets called
250 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
251 gltexture_t *glt = (gltexture_t*) rt;
256 glt->flags |= GLTEXF_DYNAMIC;
257 glt->updatecallback = updatecallback;
258 glt->updatacallback_data = data;
259 glt->dirtytexnum = 0;
262 static void R_UpdateDynamicTexture(gltexture_t *glt) {
263 glt->texnum = glt->dirtytexnum;
264 // reset dirtytexnum again (not dirty anymore)
265 glt->dirtytexnum = 0;
266 // TODO: now assert that t->texnum != 0 ?
267 if( glt->updatecallback ) {
268 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
272 void R_PurgeTexture(rtexture_t *rt)
274 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
279 void R_FreeTexture(rtexture_t *rt)
281 gltexture_t *glt, **gltpointer;
283 glt = (gltexture_t *)rt;
285 Host_Error("R_FreeTexture: texture == NULL");
287 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
288 if (*gltpointer == glt)
289 *gltpointer = glt->chain;
291 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
293 if (!(glt->flags & GLTEXF_UPLOAD))
296 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
299 if (glt->inputtexels)
300 Mem_Free(glt->inputtexels);
304 rtexturepool_t *R_AllocTexturePool(void)
306 gltexturepool_t *pool;
307 if (texturemempool == NULL)
309 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
312 pool->next = gltexturepoolchain;
313 gltexturepoolchain = pool;
314 pool->sentinel = TEXTUREPOOL_SENTINEL;
315 return (rtexturepool_t *)pool;
318 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
320 gltexturepool_t *pool, **poolpointer;
321 if (rtexturepool == NULL)
323 if (*rtexturepool == NULL)
325 pool = (gltexturepool_t *)(*rtexturepool);
326 *rtexturepool = NULL;
327 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
328 Host_Error("R_FreeTexturePool: pool already freed");
329 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
330 if (*poolpointer == pool)
331 *poolpointer = pool->next;
333 Host_Error("R_FreeTexturePool: pool not linked");
334 while (pool->gltchain)
335 R_FreeTexture((rtexture_t *)pool->gltchain);
340 typedef struct glmode_s
343 int minification, magnification;
347 static glmode_t modes[6] =
349 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
350 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
351 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
352 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
353 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
354 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
357 static void GL_TextureMode_f (void)
362 gltexturepool_t *pool;
366 for (i = 0;i < 6;i++)
368 if (gl_filter_min == modes[i].minification)
370 Con_Printf("%s\n", modes[i].name);
374 Con_Print("current filter is unknown???\n");
378 for (i = 0;i < 6;i++)
379 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
383 Con_Print("bad filter name\n");
387 gl_filter_min = modes[i].minification;
388 gl_filter_mag = modes[i].magnification;
390 // change all the existing mipmap texture objects
391 // FIXME: force renderer(/client/something?) restart instead?
394 for (pool = gltexturepoolchain;pool;pool = pool->next)
396 for (glt = pool->gltchain;glt;glt = glt->chain)
398 // only update already uploaded images
399 if (!(glt->flags & (GLTEXF_UPLOAD | TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
401 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
402 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
403 if (glt->flags & TEXF_MIPMAP)
405 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
409 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
411 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
412 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
418 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
420 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
425 case GLTEXTURETYPE_2D:
426 maxsize = vid.maxtexturesize_2d;
427 if (flags & TEXF_PICMIP)
429 maxsize = bound(1, gl_max_size.integer, maxsize);
430 picmip = gl_picmip.integer;
433 case GLTEXTURETYPE_3D:
434 maxsize = vid.maxtexturesize_3d;
436 case GLTEXTURETYPE_CUBEMAP:
437 maxsize = vid.maxtexturesize_cubemap;
443 if (vid.support.arb_texture_non_power_of_two)
444 width2 = min(inwidth >> picmip, maxsize);
447 for (width2 = 1;width2 < inwidth;width2 <<= 1);
448 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
450 *outwidth = max(1, width2);
454 if (vid.support.arb_texture_non_power_of_two)
455 height2 = min(inheight >> picmip, maxsize);
458 for (height2 = 1;height2 < inheight;height2 <<= 1);
459 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
461 *outheight = max(1, height2);
465 if (vid.support.arb_texture_non_power_of_two)
466 depth2 = min(indepth >> picmip, maxsize);
469 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
470 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
472 *outdepth = max(1, depth2);
477 static int R_CalcTexelDataSize (gltexture_t *glt)
479 int width2, height2, depth2, size;
481 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
483 size = width2 * height2 * depth2;
485 if (glt->flags & TEXF_MIPMAP)
487 while (width2 > 1 || height2 > 1 || depth2 > 1)
495 size += width2 * height2 * depth2;
499 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
502 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
506 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
507 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
509 gltexturepool_t *pool;
511 Con_Print("glsize input loaded mip alpha name\n");
512 for (pool = gltexturepoolchain;pool;pool = pool->next)
520 for (glt = pool->gltchain;glt;glt = glt->chain)
522 glsize = R_CalcTexelDataSize(glt);
523 isloaded = !(glt->flags & GLTEXF_UPLOAD);
525 pooltotalt += glsize;
526 pooltotalp += glt->inputdatasize;
530 poolloadedt += glsize;
531 poolloadedp += glt->inputdatasize;
534 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);
537 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);
538 sumtotal += pooltotal;
539 sumtotalt += pooltotalt;
540 sumtotalp += pooltotalp;
541 sumloaded += poolloaded;
542 sumloadedt += poolloadedt;
543 sumloadedp += poolloadedp;
546 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);
549 static void R_TextureStats_f(void)
551 R_TextureStats_Print(true, true, true);
554 static void r_textures_start(void)
556 // LordHavoc: allow any alignment
558 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
559 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
561 texturemempool = Mem_AllocPool("texture management", 0, NULL);
563 // Disable JPEG screenshots if the DLL isn't loaded
564 if (! JPEG_OpenLibrary ())
565 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
566 // TODO: support png screenshots?
570 static void r_textures_shutdown(void)
572 rtexturepool_t *temp;
574 JPEG_CloseLibrary ();
576 while(gltexturepoolchain)
578 temp = (rtexturepool_t *) gltexturepoolchain;
579 R_FreeTexturePool(&temp);
582 resizebuffersize = 0;
583 texturebuffersize = 0;
585 colorconvertbuffer = NULL;
586 texturebuffer = NULL;
587 Mem_FreePool(&texturemempool);
590 static void r_textures_newmap(void)
594 void R_Textures_Init (void)
596 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
597 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
598 Cvar_RegisterVariable (&gl_max_size);
599 Cvar_RegisterVariable (&gl_picmip);
600 Cvar_RegisterVariable (&gl_max_lightmapsize);
601 Cvar_RegisterVariable (&r_lerpimages);
602 Cvar_RegisterVariable (&gl_texture_anisotropy);
603 Cvar_RegisterVariable (&gl_texturecompression);
604 Cvar_RegisterVariable (&gl_texturecompression_color);
605 Cvar_RegisterVariable (&gl_texturecompression_normal);
606 Cvar_RegisterVariable (&gl_texturecompression_gloss);
607 Cvar_RegisterVariable (&gl_texturecompression_glow);
608 Cvar_RegisterVariable (&gl_texturecompression_2d);
609 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
610 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
611 Cvar_RegisterVariable (&gl_texturecompression_sky);
612 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
613 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
615 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
618 void R_Textures_Frame (void)
620 static int old_aniso = 0;
622 // could do procedural texture animation here, if we keep track of which
623 // textures were accessed this frame...
625 // free the resize buffers
626 resizebuffersize = 0;
629 Mem_Free(resizebuffer);
632 if (colorconvertbuffer)
634 Mem_Free(colorconvertbuffer);
635 colorconvertbuffer = NULL;
638 if (old_aniso != gl_texture_anisotropy.integer)
641 gltexturepool_t *pool;
644 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
646 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
650 for (pool = gltexturepoolchain;pool;pool = pool->next)
652 for (glt = pool->gltchain;glt;glt = glt->chain)
654 // only update already uploaded images
655 if ((glt->flags & (GLTEXF_UPLOAD | TEXF_MIPMAP)) == TEXF_MIPMAP)
657 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
659 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
660 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
662 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
669 void R_MakeResizeBufferBigger(int size)
671 if (resizebuffersize < size)
673 resizebuffersize = size;
675 Mem_Free(resizebuffer);
676 if (colorconvertbuffer)
677 Mem_Free(colorconvertbuffer);
678 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
679 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
680 if (!resizebuffer || !colorconvertbuffer)
681 Host_Error("R_Upload: out of memory");
685 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
687 int textureenum = gltexturetypeenums[texturetype];
688 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
692 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
694 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
695 if (gl_texture_anisotropy.integer != aniso)
696 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
697 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
699 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
700 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
701 if (gltexturetypedimensions[texturetype] >= 3)
703 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
707 if (flags & TEXF_FORCENEAREST)
709 if (flags & TEXF_MIPMAP)
711 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
715 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
717 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
719 else if (flags & TEXF_FORCELINEAR)
721 if (flags & TEXF_MIPMAP)
723 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
725 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
729 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
734 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
736 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
740 if (flags & TEXF_MIPMAP)
742 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
746 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
748 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
751 if (textype == TEXTYPE_SHADOWMAP)
753 if (vid.support.arb_shadow)
755 if (flags & TEXF_COMPARE)
757 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
761 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
763 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
765 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
771 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
773 int i, mip, width, height, depth;
775 const unsigned char *prevbuffer;
780 // we need to restore the texture binding after finishing the upload
782 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
783 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
785 // these are rounded up versions of the size to do better resampling
786 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
788 width = glt->inputwidth;
789 height = glt->inputheight;
790 depth = glt->inputdepth;
794 for (width = 1;width < glt->inputwidth ;width <<= 1);
795 for (height = 1;height < glt->inputheight;height <<= 1);
796 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
799 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
800 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
802 if (prevbuffer == NULL)
804 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
805 prevbuffer = resizebuffer;
807 else if (glt->textype->textype == TEXTYPE_PALETTE)
809 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
810 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
811 prevbuffer = colorconvertbuffer;
814 if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP | GLTEXF_UPLOAD)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth && (fragx != 0 || fragy != 0 || fragwidth != glt->tilewidth || fragheight != glt->tileheight))
816 // update a portion of the image
817 switch(glt->texturetype)
819 case GLTEXTURETYPE_2D:
820 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
822 case GLTEXTURETYPE_3D:
823 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
826 Host_Error("R_Upload: partial update of type other than 2D");
832 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
833 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
835 // upload the image for the first time
836 glt->flags &= ~GLTEXF_UPLOAD;
838 // cubemaps contain multiple images and thus get processed a bit differently
839 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
841 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
843 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
844 prevbuffer = resizebuffer;
847 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
849 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
850 prevbuffer = resizebuffer;
854 if (qglGetCompressedTexImageARB)
856 if (gl_texturecompression.integer >= 2)
857 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
859 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
862 switch(glt->texturetype)
864 case GLTEXTURETYPE_2D:
865 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
866 if (glt->flags & TEXF_MIPMAP)
868 while (width > 1 || height > 1 || depth > 1)
870 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
871 prevbuffer = resizebuffer;
872 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
876 case GLTEXTURETYPE_3D:
877 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
878 if (glt->flags & TEXF_MIPMAP)
880 while (width > 1 || height > 1 || depth > 1)
882 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
883 prevbuffer = resizebuffer;
884 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
888 case GLTEXTURETYPE_CUBEMAP:
889 // convert and upload each side in turn,
890 // from a continuous block of input texels
891 texturebuffer = (unsigned char *)prevbuffer;
892 for (i = 0;i < 6;i++)
894 prevbuffer = texturebuffer;
895 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
896 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
898 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
899 prevbuffer = resizebuffer;
902 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
904 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
905 prevbuffer = resizebuffer;
908 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
909 if (glt->flags & TEXF_MIPMAP)
911 while (width > 1 || height > 1 || depth > 1)
913 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
914 prevbuffer = resizebuffer;
915 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
920 case GLTEXTURETYPE_RECTANGLE:
921 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
924 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
926 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
929 int R_RealGetTexture(rtexture_t *rt)
934 glt = (gltexture_t *)rt;
935 if (glt->flags & GLTEXF_DYNAMIC)
936 R_UpdateDynamicTexture(glt);
937 if (glt->flags & GLTEXF_UPLOAD)
940 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
941 R_Upload(glt, glt->inputtexels, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
942 if (glt->inputtexels)
944 Mem_Free(glt->inputtexels);
945 glt->inputtexels = NULL;
946 glt->flags |= GLTEXF_DESTROYED;
948 else if (glt->flags & GLTEXF_DESTROYED)
949 Con_Printf("R_GetTexture: Texture %s already uploaded and destroyed. Can not upload original image again. Uploaded blank texture.\n", glt->identifier);
958 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)
962 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
963 textypeinfo_t *texinfo;
965 if (cls.state == ca_dedicated)
968 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
970 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
973 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
975 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
978 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
980 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
984 texinfo = R_GetTexTypeInfo(textype, flags);
985 size = width * height * depth * sides * texinfo->inputbytesperpixel;
988 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
992 // clear the alpha flag if the texture has no transparent pixels
995 case TEXTYPE_PALETTE:
996 if (flags & TEXF_ALPHA)
998 flags &= ~TEXF_ALPHA;
1001 for (i = 0;i < size;i++)
1003 if (((unsigned char *)&palette[data[i]])[3] < 255)
1005 flags |= TEXF_ALPHA;
1014 if (flags & TEXF_ALPHA)
1016 flags &= ~TEXF_ALPHA;
1019 for (i = 3;i < size;i += 4)
1023 flags |= TEXF_ALPHA;
1030 case TEXTYPE_SHADOWMAP:
1033 Host_Error("R_LoadTexture: unknown texture type");
1036 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1038 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1040 glt->chain = pool->gltchain;
1041 pool->gltchain = glt;
1042 glt->inputwidth = width;
1043 glt->inputheight = height;
1044 glt->inputdepth = depth;
1045 glt->flags = flags | GLTEXF_UPLOAD;
1046 glt->textype = texinfo;
1047 glt->texturetype = texturetype;
1048 glt->inputdatasize = size;
1049 glt->palette = palette;
1050 glt->glinternalformat = texinfo->glinternalformat;
1051 glt->glformat = texinfo->glformat;
1052 glt->gltype = texinfo->gltype;
1053 glt->bytesperpixel = texinfo->internalbytesperpixel;
1054 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1056 // init the dynamic texture attributes, too [11/22/2007 Black]
1057 glt->dirtytexnum = 0;
1058 glt->updatecallback = NULL;
1059 glt->updatacallback_data = NULL;
1061 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1063 // upload the texture
1064 // data may be NULL (blank texture for dynamic rendering)
1066 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1067 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1068 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1069 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1071 // texture converting and uploading can take a while, so make sure we're sending keepalives
1072 CL_KeepaliveMessage(false);
1074 return (rtexture_t *)glt;
1077 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)
1079 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1082 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)
1084 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1087 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)
1089 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1092 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)
1094 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1097 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1099 int flags = TEXF_CLAMP;
1101 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1103 flags |= TEXF_FORCENEAREST;
1104 if (precision <= 16)
1105 flags |= TEXF_LOWPRECISION;
1109 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1111 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1114 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1116 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1119 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1121 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1124 int R_TextureWidth(rtexture_t *rt)
1126 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1129 int R_TextureHeight(rtexture_t *rt)
1131 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1134 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1136 gltexture_t *glt = (gltexture_t *)rt;
1138 Host_Error("R_UpdateTexture: no data supplied");
1140 Host_Error("R_UpdateTexture: no texture supplied");
1142 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1143 // update part of the texture
1144 if (glt->bufferpixels)
1147 int bpp = glt->bytesperpixel;
1148 int inputskip = width*bpp;
1149 int outputskip = glt->tilewidth*bpp;
1150 const unsigned char *input = data;
1151 unsigned char *output = glt->bufferpixels;
1161 input -= y*inputskip;
1164 if (width > glt->tilewidth - x)
1165 width = glt->tilewidth - x;
1166 if (height > glt->tileheight - y)
1167 height = glt->tileheight - y;
1168 if (width < 1 || height < 1)
1170 glt->buffermodified = true;
1171 output += y*outputskip + x*bpp;
1172 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1173 memcpy(output, input, width*bpp);
1174 if (!(glt->flags & TEXF_MANUALFLUSHUPDATES))
1178 R_Upload(glt, data, x, y, 0, width, height, 1);
1181 void R_FlushTexture(rtexture_t *rt)
1185 Host_Error("R_FlushTexture: no texture supplied");
1187 // update part of the texture
1188 glt = (gltexture_t *)rt;
1190 if (!glt->buffermodified || !glt->bufferpixels)
1192 glt->buffermodified = false;
1193 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1196 void R_ClearTexture (rtexture_t *rt)
1198 gltexture_t *glt = (gltexture_t *)rt;
1200 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );