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", "1", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"};
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 // bitmask for mismatch checking
32 #define GLTEXF_IMPORTANTBITS (0)
33 // dynamic texture (treat texnum == 0 differently)
34 #define GLTEXF_DYNAMIC 0x00080000
36 typedef struct textypeinfo_s
39 int inputbytesperpixel;
40 int internalbytesperpixel;
41 float glinternalbytesperpixel;
48 static textypeinfo_t textype_palette = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 3, GL_UNSIGNED_BYTE};
49 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 4, GL_UNSIGNED_BYTE};
50 static textypeinfo_t textype_palette_compress = {TEXTYPE_PALETTE, 1, 4, 0.5f, GL_BGRA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
51 static textypeinfo_t textype_palette_alpha_compress = {TEXTYPE_PALETTE, 1, 4, 1.0f, GL_BGRA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
52 static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 3, GL_UNSIGNED_BYTE};
53 static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 4, GL_UNSIGNED_BYTE};
54 static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_RGBA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
55 static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_RGBA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
56 static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 3, GL_UNSIGNED_BYTE};
57 static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 4, GL_UNSIGNED_BYTE};
58 static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_BGRA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
59 static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_BGRA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
60 static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP,2,2, 2.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16_ARB, GL_UNSIGNED_SHORT};
61 static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP,4,4, 4.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24_ARB, GL_UNSIGNED_INT};
62 static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , 4, GL_UNSIGNED_BYTE};
63 static textypeinfo_t textype_alpha_compress = {TEXTYPE_ALPHA , 1, 4, 1.0f, GL_ALPHA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
65 typedef enum gltexturetype_e
69 GLTEXTURETYPE_CUBEMAP,
70 GLTEXTURETYPE_RECTANGLE,
75 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
76 static int gltexturetypebindingenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB, GL_TEXTURE_BINDING_RECTANGLE_ARB};
77 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
78 static int cubemapside[6] =
80 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
81 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
82 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
83 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
84 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
85 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
88 typedef struct gltexture_s
90 // this portion of the struct is exposed to the R_GetTexture macro for
91 // speed reasons, must be identical in rtexture_t!
92 int texnum; // GL texture slot number
93 qboolean dirty; // indicates that R_RealGetTexture should be called
94 int gltexturetypeenum; // used by R_Mesh_TexBind
96 // dynamic texture stuff [11/22/2007 Black]
97 updatecallback_t updatecallback;
98 void *updatacallback_data;
99 // --- [11/22/2007 Black]
101 // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
102 unsigned char *bufferpixels;
103 qboolean buffermodified;
105 // pointer to texturepool (check this to see if the texture is allocated)
106 struct gltexturepool_s *pool;
107 // pointer to next texture in texturepool chain
108 struct gltexture_s *chain;
109 // name of the texture (this might be removed someday), no duplicates
110 char identifier[MAX_QPATH + 32];
111 // original data size in *inputtexels
112 int inputwidth, inputheight, inputdepth;
113 // copy of the original texture(s) supplied to the upload function, for
114 // delayed uploads (non-precached)
115 unsigned char *inputtexels;
116 // original data size in *inputtexels
118 // flags supplied to the LoadTexture function
119 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
121 // pointer to one of the textype_ structs
122 textypeinfo_t *textype;
123 // one of the GLTEXTURETYPE_ values
125 // palette if the texture is TEXTYPE_PALETTE
126 const unsigned int *palette;
127 // actual stored texture size after gl_picmip and gl_max_size are applied
128 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
129 int tilewidth, tileheight, tiledepth;
130 // 1 or 6 depending on texturetype
134 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
137 int glinternalformat;
138 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
143 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
145 typedef struct gltexturepool_s
147 unsigned int sentinel;
148 struct gltexture_s *gltchain;
149 struct gltexturepool_s *next;
153 static gltexturepool_t *gltexturepoolchain = NULL;
155 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
156 static int resizebuffersize = 0;
157 static const unsigned char *texturebuffer;
158 static int texturebuffersize = 0;
160 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
162 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
164 if (flags & TEXF_ALPHA)
168 case TEXTYPE_PALETTE:
169 return &textype_palette_alpha_compress;
171 return &textype_rgba_alpha_compress;
173 return &textype_bgra_alpha_compress;
175 return &textype_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 return &textype_alpha_compress;
194 Host_Error("R_GetTexTypeInfo: unknown texture format");
201 if (flags & TEXF_ALPHA)
205 case TEXTYPE_PALETTE:
206 return &textype_palette_alpha;
208 return &textype_rgba_alpha;
210 return &textype_bgra_alpha;
212 return &textype_alpha;
214 Host_Error("R_GetTexTypeInfo: unknown texture format");
222 case TEXTYPE_PALETTE:
223 return &textype_palette;
225 return &textype_rgba;
227 return &textype_bgra;
228 case TEXTYPE_SHADOWMAP:
229 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
231 return &textype_alpha;
233 Host_Error("R_GetTexTypeInfo: unknown texture format");
238 return NULL; // this line only to hush compiler warnings
241 // dynamic texture code [11/22/2007 Black]
242 void R_MarkDirtyTexture(rtexture_t *rt) {
243 gltexture_t *glt = (gltexture_t*) rt;
248 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
249 if (glt->flags & GLTEXF_DYNAMIC)
251 // mark it as dirty, so R_RealGetTexture gets called
256 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
257 gltexture_t *glt = (gltexture_t*) rt;
262 glt->flags |= GLTEXF_DYNAMIC;
263 glt->updatecallback = updatecallback;
264 glt->updatacallback_data = data;
267 static void R_UpdateDynamicTexture(gltexture_t *glt) {
269 if( glt->updatecallback ) {
270 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
274 void R_PurgeTexture(rtexture_t *rt)
276 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
281 void R_FreeTexture(rtexture_t *rt)
283 gltexture_t *glt, **gltpointer;
285 glt = (gltexture_t *)rt;
287 Host_Error("R_FreeTexture: texture == NULL");
289 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
290 if (*gltpointer == glt)
291 *gltpointer = glt->chain;
293 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
298 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
301 if (glt->inputtexels)
302 Mem_Free(glt->inputtexels);
306 rtexturepool_t *R_AllocTexturePool(void)
308 gltexturepool_t *pool;
309 if (texturemempool == NULL)
311 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
314 pool->next = gltexturepoolchain;
315 gltexturepoolchain = pool;
316 pool->sentinel = TEXTUREPOOL_SENTINEL;
317 return (rtexturepool_t *)pool;
320 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
322 gltexturepool_t *pool, **poolpointer;
323 if (rtexturepool == NULL)
325 if (*rtexturepool == NULL)
327 pool = (gltexturepool_t *)(*rtexturepool);
328 *rtexturepool = NULL;
329 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
330 Host_Error("R_FreeTexturePool: pool already freed");
331 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
332 if (*poolpointer == pool)
333 *poolpointer = pool->next;
335 Host_Error("R_FreeTexturePool: pool not linked");
336 while (pool->gltchain)
337 R_FreeTexture((rtexture_t *)pool->gltchain);
342 typedef struct glmode_s
345 int minification, magnification;
349 static glmode_t modes[6] =
351 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
352 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
353 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
354 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
355 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
356 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
359 static void GL_TextureMode_f (void)
364 gltexturepool_t *pool;
368 for (i = 0;i < 6;i++)
370 if (gl_filter_min == modes[i].minification)
372 Con_Printf("%s\n", modes[i].name);
376 Con_Print("current filter is unknown???\n");
380 for (i = 0;i < 6;i++)
381 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
385 Con_Print("bad filter name\n");
389 gl_filter_min = modes[i].minification;
390 gl_filter_mag = modes[i].magnification;
392 // change all the existing mipmap texture objects
393 // FIXME: force renderer(/client/something?) restart instead?
396 for (pool = gltexturepoolchain;pool;pool = pool->next)
398 for (glt = pool->gltchain;glt;glt = glt->chain)
400 // only update already uploaded images
401 if (glt->texnum && !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
403 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
404 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
405 if (glt->flags & TEXF_MIPMAP)
407 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
411 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
413 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
414 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
420 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
422 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
427 case GLTEXTURETYPE_2D:
428 maxsize = vid.maxtexturesize_2d;
429 if (flags & TEXF_PICMIP)
431 maxsize = bound(1, gl_max_size.integer, maxsize);
432 picmip = gl_picmip.integer;
435 case GLTEXTURETYPE_3D:
436 maxsize = vid.maxtexturesize_3d;
438 case GLTEXTURETYPE_CUBEMAP:
439 maxsize = vid.maxtexturesize_cubemap;
445 if (vid.support.arb_texture_non_power_of_two)
446 width2 = min(inwidth >> picmip, maxsize);
449 for (width2 = 1;width2 < inwidth;width2 <<= 1);
450 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
452 *outwidth = max(1, width2);
456 if (vid.support.arb_texture_non_power_of_two)
457 height2 = min(inheight >> picmip, maxsize);
460 for (height2 = 1;height2 < inheight;height2 <<= 1);
461 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
463 *outheight = max(1, height2);
467 if (vid.support.arb_texture_non_power_of_two)
468 depth2 = min(indepth >> picmip, maxsize);
471 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
472 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
474 *outdepth = max(1, depth2);
479 static int R_CalcTexelDataSize (gltexture_t *glt)
481 int width2, height2, depth2, size;
483 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
485 size = width2 * height2 * depth2;
487 if (glt->flags & TEXF_MIPMAP)
489 while (width2 > 1 || height2 > 1 || depth2 > 1)
497 size += width2 * height2 * depth2;
501 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
504 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
508 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
509 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
511 gltexturepool_t *pool;
513 Con_Print("glsize input loaded mip alpha name\n");
514 for (pool = gltexturepoolchain;pool;pool = pool->next)
522 for (glt = pool->gltchain;glt;glt = glt->chain)
524 glsize = R_CalcTexelDataSize(glt);
525 isloaded = glt->texnum != 0;
527 pooltotalt += glsize;
528 pooltotalp += glt->inputdatasize;
532 poolloadedt += glsize;
533 poolloadedp += glt->inputdatasize;
536 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);
539 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);
540 sumtotal += pooltotal;
541 sumtotalt += pooltotalt;
542 sumtotalp += pooltotalp;
543 sumloaded += poolloaded;
544 sumloadedt += poolloadedt;
545 sumloadedp += poolloadedp;
548 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);
551 static void R_TextureStats_f(void)
553 R_TextureStats_Print(true, true, true);
556 static void r_textures_start(void)
558 // LordHavoc: allow any alignment
560 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
561 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
563 texturemempool = Mem_AllocPool("texture management", 0, NULL);
565 // Disable JPEG screenshots if the DLL isn't loaded
566 if (! JPEG_OpenLibrary ())
567 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
568 // TODO: support png screenshots?
572 static void r_textures_shutdown(void)
574 rtexturepool_t *temp;
576 JPEG_CloseLibrary ();
578 while(gltexturepoolchain)
580 temp = (rtexturepool_t *) gltexturepoolchain;
581 R_FreeTexturePool(&temp);
584 resizebuffersize = 0;
585 texturebuffersize = 0;
587 colorconvertbuffer = NULL;
588 texturebuffer = NULL;
589 Mem_FreePool(&texturemempool);
592 static void r_textures_newmap(void)
596 void R_Textures_Init (void)
598 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
599 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
600 Cvar_RegisterVariable (&gl_max_size);
601 Cvar_RegisterVariable (&gl_picmip);
602 Cvar_RegisterVariable (&gl_max_lightmapsize);
603 Cvar_RegisterVariable (&r_lerpimages);
604 Cvar_RegisterVariable (&gl_texture_anisotropy);
605 Cvar_RegisterVariable (&gl_texturecompression);
606 Cvar_RegisterVariable (&gl_texturecompression_color);
607 Cvar_RegisterVariable (&gl_texturecompression_normal);
608 Cvar_RegisterVariable (&gl_texturecompression_gloss);
609 Cvar_RegisterVariable (&gl_texturecompression_glow);
610 Cvar_RegisterVariable (&gl_texturecompression_2d);
611 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
612 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
613 Cvar_RegisterVariable (&gl_texturecompression_sky);
614 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
615 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
617 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
620 void R_Textures_Frame (void)
622 static int old_aniso = 0;
624 // could do procedural texture animation here, if we keep track of which
625 // textures were accessed this frame...
627 // free the resize buffers
628 resizebuffersize = 0;
631 Mem_Free(resizebuffer);
634 if (colorconvertbuffer)
636 Mem_Free(colorconvertbuffer);
637 colorconvertbuffer = NULL;
640 if (old_aniso != gl_texture_anisotropy.integer)
643 gltexturepool_t *pool;
646 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
648 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
652 for (pool = gltexturepoolchain;pool;pool = pool->next)
654 for (glt = pool->gltchain;glt;glt = glt->chain)
656 // only update already uploaded images
657 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
659 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
661 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
662 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
664 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
671 void R_MakeResizeBufferBigger(int size)
673 if (resizebuffersize < size)
675 resizebuffersize = size;
677 Mem_Free(resizebuffer);
678 if (colorconvertbuffer)
679 Mem_Free(colorconvertbuffer);
680 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
681 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
682 if (!resizebuffer || !colorconvertbuffer)
683 Host_Error("R_Upload: out of memory");
687 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
689 int textureenum = gltexturetypeenums[texturetype];
690 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
694 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
696 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
697 if (gl_texture_anisotropy.integer != aniso)
698 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
699 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
701 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
702 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
703 if (gltexturetypedimensions[texturetype] >= 3)
705 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
709 if (flags & TEXF_FORCENEAREST)
711 if (flags & TEXF_MIPMAP)
713 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
717 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
719 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
721 else if (flags & TEXF_FORCELINEAR)
723 if (flags & TEXF_MIPMAP)
725 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
727 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
731 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
736 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
738 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
742 if (flags & TEXF_MIPMAP)
744 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
748 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
750 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
753 if (textype == TEXTYPE_SHADOWMAP)
755 if (vid.support.arb_shadow)
757 if (flags & TEXF_COMPARE)
759 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
763 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
765 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
767 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
773 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
775 int i, mip, width, height, depth;
777 const unsigned char *prevbuffer;
782 // we need to restore the texture binding after finishing the upload
784 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
785 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
787 // these are rounded up versions of the size to do better resampling
788 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
790 width = glt->inputwidth;
791 height = glt->inputheight;
792 depth = glt->inputdepth;
796 for (width = 1;width < glt->inputwidth ;width <<= 1);
797 for (height = 1;height < glt->inputheight;height <<= 1);
798 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
801 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
802 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
804 if (prevbuffer == NULL)
806 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
807 prevbuffer = resizebuffer;
809 else if (glt->textype->textype == TEXTYPE_PALETTE)
811 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
812 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
813 prevbuffer = colorconvertbuffer;
816 // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
818 if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth && (fragx != 0 || fragy != 0 || fragwidth != glt->tilewidth || fragheight != glt->tileheight))
820 // update a portion of the image
821 switch(glt->texturetype)
823 case GLTEXTURETYPE_2D:
824 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
826 case GLTEXTURETYPE_3D:
827 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
830 Host_Error("R_Upload: partial update of type other than 2D");
836 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
837 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
839 // cubemaps contain multiple images and thus get processed a bit differently
840 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
842 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
844 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
845 prevbuffer = resizebuffer;
848 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
850 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
851 prevbuffer = resizebuffer;
855 if (qglGetCompressedTexImageARB)
857 if (gl_texturecompression.integer >= 2)
858 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
860 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
863 switch(glt->texturetype)
865 case GLTEXTURETYPE_2D:
866 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
867 if (glt->flags & TEXF_MIPMAP)
869 while (width > 1 || height > 1 || depth > 1)
871 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
872 prevbuffer = resizebuffer;
873 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
877 case GLTEXTURETYPE_3D:
878 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
879 if (glt->flags & TEXF_MIPMAP)
881 while (width > 1 || height > 1 || depth > 1)
883 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
884 prevbuffer = resizebuffer;
885 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
889 case GLTEXTURETYPE_CUBEMAP:
890 // convert and upload each side in turn,
891 // from a continuous block of input texels
892 texturebuffer = (unsigned char *)prevbuffer;
893 for (i = 0;i < 6;i++)
895 prevbuffer = texturebuffer;
896 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
897 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
899 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
900 prevbuffer = resizebuffer;
903 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
905 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
906 prevbuffer = resizebuffer;
909 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
910 if (glt->flags & TEXF_MIPMAP)
912 while (width > 1 || height > 1 || depth > 1)
914 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
915 prevbuffer = resizebuffer;
916 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
921 case GLTEXTURETYPE_RECTANGLE:
922 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
925 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
927 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
930 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)
934 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
935 textypeinfo_t *texinfo;
937 if (cls.state == ca_dedicated)
940 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
942 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
945 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
947 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
950 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
952 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
956 texinfo = R_GetTexTypeInfo(textype, flags);
957 size = width * height * depth * sides * texinfo->inputbytesperpixel;
960 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
964 // clear the alpha flag if the texture has no transparent pixels
967 case TEXTYPE_PALETTE:
968 if (flags & TEXF_ALPHA)
970 flags &= ~TEXF_ALPHA;
973 for (i = 0;i < size;i++)
975 if (((unsigned char *)&palette[data[i]])[3] < 255)
986 if (flags & TEXF_ALPHA)
988 flags &= ~TEXF_ALPHA;
991 for (i = 3;i < size;i += 4)
1002 case TEXTYPE_SHADOWMAP:
1005 flags |= TEXF_ALPHA;
1008 Host_Error("R_LoadTexture: unknown texture type");
1011 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1013 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1015 glt->chain = pool->gltchain;
1016 pool->gltchain = glt;
1017 glt->inputwidth = width;
1018 glt->inputheight = height;
1019 glt->inputdepth = depth;
1021 glt->textype = texinfo;
1022 glt->texturetype = texturetype;
1023 glt->inputdatasize = size;
1024 glt->palette = palette;
1025 glt->glinternalformat = texinfo->glinternalformat;
1026 glt->glformat = texinfo->glformat;
1027 glt->gltype = texinfo->gltype;
1028 glt->bytesperpixel = texinfo->internalbytesperpixel;
1029 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1032 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1033 // init the dynamic texture attributes, too [11/22/2007 Black]
1034 glt->updatecallback = NULL;
1035 glt->updatacallback_data = NULL;
1037 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1039 // upload the texture
1040 // data may be NULL (blank texture for dynamic rendering)
1042 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1043 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1044 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1045 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1047 // texture converting and uploading can take a while, so make sure we're sending keepalives
1048 CL_KeepaliveMessage(false);
1050 return (rtexture_t *)glt;
1053 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)
1055 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1058 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)
1060 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1063 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)
1065 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1068 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)
1070 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1073 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1075 int flags = TEXF_CLAMP;
1077 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1079 flags |= TEXF_FORCENEAREST;
1080 if (precision <= 16)
1081 flags |= TEXF_LOWPRECISION;
1085 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1087 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1090 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1092 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1095 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1097 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1100 int R_TextureWidth(rtexture_t *rt)
1102 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1105 int R_TextureHeight(rtexture_t *rt)
1107 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1110 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1112 gltexture_t *glt = (gltexture_t *)rt;
1114 Host_Error("R_UpdateTexture: no data supplied");
1116 Host_Error("R_UpdateTexture: no texture supplied");
1118 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1119 // update part of the texture
1120 if (glt->bufferpixels)
1123 int bpp = glt->bytesperpixel;
1124 int inputskip = width*bpp;
1125 int outputskip = glt->tilewidth*bpp;
1126 const unsigned char *input = data;
1127 unsigned char *output = glt->bufferpixels;
1137 input -= y*inputskip;
1140 if (width > glt->tilewidth - x)
1141 width = glt->tilewidth - x;
1142 if (height > glt->tileheight - y)
1143 height = glt->tileheight - y;
1144 if (width < 1 || height < 1)
1147 glt->buffermodified = true;
1148 output += y*outputskip + x*bpp;
1149 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1150 memcpy(output, input, width*bpp);
1153 R_Upload(glt, data, x, y, 0, width, height, 1);
1156 int R_RealGetTexture(rtexture_t *rt)
1161 glt = (gltexture_t *)rt;
1162 if (glt->flags & GLTEXF_DYNAMIC)
1163 R_UpdateDynamicTexture(glt);
1164 if (glt->buffermodified && glt->bufferpixels)
1166 glt->buffermodified = false;
1167 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1176 void R_ClearTexture (rtexture_t *rt)
1178 gltexture_t *glt = (gltexture_t *)rt;
1180 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );