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 // 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 (r_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 int R_RealGetTexture(rtexture_t *rt)
935 glt = (gltexture_t *)rt;
936 if (glt->flags & GLTEXF_DYNAMIC)
937 R_UpdateDynamicTexture(glt);
944 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)
948 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
949 textypeinfo_t *texinfo;
951 if (cls.state == ca_dedicated)
954 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
956 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
959 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
961 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
964 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
966 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
970 texinfo = R_GetTexTypeInfo(textype, flags);
971 size = width * height * depth * sides * texinfo->inputbytesperpixel;
974 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
978 // clear the alpha flag if the texture has no transparent pixels
981 case TEXTYPE_PALETTE:
982 if (flags & TEXF_ALPHA)
984 flags &= ~TEXF_ALPHA;
987 for (i = 0;i < size;i++)
989 if (((unsigned char *)&palette[data[i]])[3] < 255)
1000 if (flags & TEXF_ALPHA)
1002 flags &= ~TEXF_ALPHA;
1005 for (i = 3;i < size;i += 4)
1009 flags |= TEXF_ALPHA;
1016 case TEXTYPE_SHADOWMAP:
1019 flags |= TEXF_ALPHA;
1022 Host_Error("R_LoadTexture: unknown texture type");
1025 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1027 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1029 glt->chain = pool->gltchain;
1030 pool->gltchain = glt;
1031 glt->inputwidth = width;
1032 glt->inputheight = height;
1033 glt->inputdepth = depth;
1035 glt->textype = texinfo;
1036 glt->texturetype = texturetype;
1037 glt->inputdatasize = size;
1038 glt->palette = palette;
1039 glt->glinternalformat = texinfo->glinternalformat;
1040 glt->glformat = texinfo->glformat;
1041 glt->gltype = texinfo->gltype;
1042 glt->bytesperpixel = texinfo->internalbytesperpixel;
1043 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1046 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1047 // init the dynamic texture attributes, too [11/22/2007 Black]
1048 glt->updatecallback = NULL;
1049 glt->updatacallback_data = NULL;
1051 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1053 // upload the texture
1054 // data may be NULL (blank texture for dynamic rendering)
1056 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1057 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1058 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1059 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1061 // texture converting and uploading can take a while, so make sure we're sending keepalives
1062 CL_KeepaliveMessage(false);
1064 return (rtexture_t *)glt;
1067 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)
1069 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1072 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)
1074 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1077 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)
1079 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1082 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)
1084 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1087 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1089 int flags = TEXF_CLAMP;
1091 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1093 flags |= TEXF_FORCENEAREST;
1094 if (precision <= 16)
1095 flags |= TEXF_LOWPRECISION;
1099 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1101 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1104 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1106 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1109 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1111 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1114 int R_TextureWidth(rtexture_t *rt)
1116 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1119 int R_TextureHeight(rtexture_t *rt)
1121 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1124 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1126 gltexture_t *glt = (gltexture_t *)rt;
1128 Host_Error("R_UpdateTexture: no data supplied");
1130 Host_Error("R_UpdateTexture: no texture supplied");
1132 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1133 // update part of the texture
1134 if (glt->bufferpixels)
1137 int bpp = glt->bytesperpixel;
1138 int inputskip = width*bpp;
1139 int outputskip = glt->tilewidth*bpp;
1140 const unsigned char *input = data;
1141 unsigned char *output = glt->bufferpixels;
1151 input -= y*inputskip;
1154 if (width > glt->tilewidth - x)
1155 width = glt->tilewidth - x;
1156 if (height > glt->tileheight - y)
1157 height = glt->tileheight - y;
1158 if (width < 1 || height < 1)
1160 glt->buffermodified = true;
1161 output += y*outputskip + x*bpp;
1162 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1163 memcpy(output, input, width*bpp);
1164 if (!(glt->flags & TEXF_MANUALFLUSHUPDATES))
1168 R_Upload(glt, data, x, y, 0, width, height, 1);
1171 void R_FlushTexture(rtexture_t *rt)
1175 Host_Error("R_FlushTexture: no texture supplied");
1177 // update part of the texture
1178 glt = (gltexture_t *)rt;
1180 if (!glt->buffermodified || !glt->bufferpixels)
1182 glt->buffermodified = false;
1183 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1186 void R_ClearTexture (rtexture_t *rt)
1188 gltexture_t *glt = (gltexture_t *)rt;
1190 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );