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};
66 static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , 4, GL_UNSIGNED_BYTE};
67 static textypeinfo_t textype_alpha_compress = {TEXTYPE_ALPHA , 1, 4, 1.0f, GL_ALPHA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
69 typedef enum gltexturetype_e
73 GLTEXTURETYPE_CUBEMAP,
74 GLTEXTURETYPE_RECTANGLE,
79 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
80 static int gltexturetypebindingenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB, GL_TEXTURE_BINDING_RECTANGLE_ARB};
81 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
82 static int cubemapside[6] =
84 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
85 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
86 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
87 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
88 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
89 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
92 typedef struct gltexture_s
94 // this field is exposed to the R_GetTexture macro, for speed reasons
95 // (must be identical in rtexture_t)
96 int texnum; // GL texture slot number
98 // dynamic texture stuff [11/22/2007 Black]
99 // used to hold the texture number of dirty textures
101 updatecallback_t updatecallback;
102 void *updatacallback_data;
103 // --- [11/22/2007 Black]
105 // stores backup copy of texture for deferred texture updates (r_nopartialtextureupdates cvar)
106 unsigned char *bufferpixels;
107 qboolean buffermodified;
109 // pointer to texturepool (check this to see if the texture is allocated)
110 struct gltexturepool_s *pool;
111 // pointer to next texture in texturepool chain
112 struct gltexture_s *chain;
113 // name of the texture (this might be removed someday), no duplicates
114 char identifier[MAX_QPATH + 32];
115 // original data size in *inputtexels
116 int inputwidth, inputheight, inputdepth;
117 // copy of the original texture(s) supplied to the upload function, for
118 // delayed uploads (non-precached)
119 unsigned char *inputtexels;
120 // original data size in *inputtexels
122 // flags supplied to the LoadTexture function
123 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
125 // pointer to one of the textype_ structs
126 textypeinfo_t *textype;
127 // one of the GLTEXTURETYPE_ values
129 // palette if the texture is TEXTYPE_PALETTE
130 const unsigned int *palette;
131 // actual stored texture size after gl_picmip and gl_max_size are applied
132 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
133 int tilewidth, tileheight, tiledepth;
134 // 1 or 6 depending on texturetype
138 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
141 int glinternalformat;
142 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
147 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
149 typedef struct gltexturepool_s
151 unsigned int sentinel;
152 struct gltexture_s *gltchain;
153 struct gltexturepool_s *next;
157 static gltexturepool_t *gltexturepoolchain = NULL;
159 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
160 static int resizebuffersize = 0;
161 static const unsigned char *texturebuffer;
162 static int texturebuffersize = 0;
164 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
166 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
168 if (flags & TEXF_ALPHA)
172 case TEXTYPE_PALETTE:
173 return &textype_palette_alpha_compress;
175 return &textype_rgba_alpha_compress;
177 return &textype_bgra_alpha_compress;
179 return &textype_alpha_compress;
181 Host_Error("R_GetTexTypeInfo: unknown texture format");
189 case TEXTYPE_PALETTE:
190 return &textype_palette_compress;
192 return &textype_rgba_compress;
194 return &textype_bgra_compress;
196 return &textype_alpha_compress;
198 Host_Error("R_GetTexTypeInfo: unknown texture format");
205 if (flags & TEXF_ALPHA)
209 case TEXTYPE_PALETTE:
210 return &textype_palette_alpha;
212 return &textype_rgba_alpha;
214 return &textype_bgra_alpha;
216 return &textype_alpha;
218 Host_Error("R_GetTexTypeInfo: unknown texture format");
226 case TEXTYPE_PALETTE:
227 return &textype_palette;
229 return &textype_rgba;
231 return &textype_bgra;
232 case TEXTYPE_SHADOWMAP:
233 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
235 return &textype_alpha;
237 Host_Error("R_GetTexTypeInfo: unknown texture format");
242 return NULL; // this line only to hush compiler warnings
245 // dynamic texture code [11/22/2007 Black]
246 void R_MarkDirtyTexture(rtexture_t *rt) {
247 gltexture_t *glt = (gltexture_t*) rt;
252 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
253 if( !glt->dirtytexnum && glt->flags & GLTEXF_DYNAMIC ) {
254 glt->dirtytexnum = glt->texnum;
255 // mark it as dirty, so R_RealGetTexture gets called
260 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
261 gltexture_t *glt = (gltexture_t*) rt;
266 glt->flags |= GLTEXF_DYNAMIC;
267 glt->updatecallback = updatecallback;
268 glt->updatacallback_data = data;
269 glt->dirtytexnum = 0;
272 static void R_UpdateDynamicTexture(gltexture_t *glt) {
273 glt->texnum = glt->dirtytexnum;
274 // reset dirtytexnum again (not dirty anymore)
275 glt->dirtytexnum = 0;
276 // TODO: now assert that t->texnum != 0 ?
277 if( glt->updatecallback ) {
278 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
282 void R_PurgeTexture(rtexture_t *rt)
284 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
289 void R_FreeTexture(rtexture_t *rt)
291 gltexture_t *glt, **gltpointer;
293 glt = (gltexture_t *)rt;
295 Host_Error("R_FreeTexture: texture == NULL");
297 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
298 if (*gltpointer == glt)
299 *gltpointer = glt->chain;
301 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
303 if (!(glt->flags & GLTEXF_UPLOAD))
306 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
309 if (glt->inputtexels)
310 Mem_Free(glt->inputtexels);
314 rtexturepool_t *R_AllocTexturePool(void)
316 gltexturepool_t *pool;
317 if (texturemempool == NULL)
319 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
322 pool->next = gltexturepoolchain;
323 gltexturepoolchain = pool;
324 pool->sentinel = TEXTUREPOOL_SENTINEL;
325 return (rtexturepool_t *)pool;
328 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
330 gltexturepool_t *pool, **poolpointer;
331 if (rtexturepool == NULL)
333 if (*rtexturepool == NULL)
335 pool = (gltexturepool_t *)(*rtexturepool);
336 *rtexturepool = NULL;
337 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
338 Host_Error("R_FreeTexturePool: pool already freed");
339 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
340 if (*poolpointer == pool)
341 *poolpointer = pool->next;
343 Host_Error("R_FreeTexturePool: pool not linked");
344 while (pool->gltchain)
345 R_FreeTexture((rtexture_t *)pool->gltchain);
350 typedef struct glmode_s
353 int minification, magnification;
357 static glmode_t modes[6] =
359 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
360 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
361 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
362 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
363 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
364 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
367 static void GL_TextureMode_f (void)
372 gltexturepool_t *pool;
376 for (i = 0;i < 6;i++)
378 if (gl_filter_min == modes[i].minification)
380 Con_Printf("%s\n", modes[i].name);
384 Con_Print("current filter is unknown???\n");
388 for (i = 0;i < 6;i++)
389 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
393 Con_Print("bad filter name\n");
397 gl_filter_min = modes[i].minification;
398 gl_filter_mag = modes[i].magnification;
400 // change all the existing mipmap texture objects
401 // FIXME: force renderer(/client/something?) restart instead?
404 for (pool = gltexturepoolchain;pool;pool = pool->next)
406 for (glt = pool->gltchain;glt;glt = glt->chain)
408 // only update already uploaded images
409 if (!(glt->flags & (GLTEXF_UPLOAD | TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
411 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
412 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
413 if (glt->flags & TEXF_MIPMAP)
415 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
419 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
421 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
422 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
428 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
430 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
435 case GLTEXTURETYPE_2D:
436 maxsize = vid.maxtexturesize_2d;
437 if (flags & TEXF_PICMIP)
439 maxsize = bound(1, gl_max_size.integer, maxsize);
440 picmip = gl_picmip.integer;
443 case GLTEXTURETYPE_3D:
444 maxsize = vid.maxtexturesize_3d;
446 case GLTEXTURETYPE_CUBEMAP:
447 maxsize = vid.maxtexturesize_cubemap;
453 if (vid.support.arb_texture_non_power_of_two)
454 width2 = min(inwidth >> picmip, maxsize);
457 for (width2 = 1;width2 < inwidth;width2 <<= 1);
458 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
460 *outwidth = max(1, width2);
464 if (vid.support.arb_texture_non_power_of_two)
465 height2 = min(inheight >> picmip, maxsize);
468 for (height2 = 1;height2 < inheight;height2 <<= 1);
469 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
471 *outheight = max(1, height2);
475 if (vid.support.arb_texture_non_power_of_two)
476 depth2 = min(indepth >> picmip, maxsize);
479 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
480 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
482 *outdepth = max(1, depth2);
487 static int R_CalcTexelDataSize (gltexture_t *glt)
489 int width2, height2, depth2, size;
491 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
493 size = width2 * height2 * depth2;
495 if (glt->flags & TEXF_MIPMAP)
497 while (width2 > 1 || height2 > 1 || depth2 > 1)
505 size += width2 * height2 * depth2;
509 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
512 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
516 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
517 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
519 gltexturepool_t *pool;
521 Con_Print("glsize input loaded mip alpha name\n");
522 for (pool = gltexturepoolchain;pool;pool = pool->next)
530 for (glt = pool->gltchain;glt;glt = glt->chain)
532 glsize = R_CalcTexelDataSize(glt);
533 isloaded = !(glt->flags & GLTEXF_UPLOAD);
535 pooltotalt += glsize;
536 pooltotalp += glt->inputdatasize;
540 poolloadedt += glsize;
541 poolloadedp += glt->inputdatasize;
544 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);
547 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);
548 sumtotal += pooltotal;
549 sumtotalt += pooltotalt;
550 sumtotalp += pooltotalp;
551 sumloaded += poolloaded;
552 sumloadedt += poolloadedt;
553 sumloadedp += poolloadedp;
556 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);
559 static void R_TextureStats_f(void)
561 R_TextureStats_Print(true, true, true);
564 static void r_textures_start(void)
566 // LordHavoc: allow any alignment
568 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
569 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
571 texturemempool = Mem_AllocPool("texture management", 0, NULL);
573 // Disable JPEG screenshots if the DLL isn't loaded
574 if (! JPEG_OpenLibrary ())
575 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
576 // TODO: support png screenshots?
580 static void r_textures_shutdown(void)
582 rtexturepool_t *temp;
584 JPEG_CloseLibrary ();
586 while(gltexturepoolchain)
588 temp = (rtexturepool_t *) gltexturepoolchain;
589 R_FreeTexturePool(&temp);
592 resizebuffersize = 0;
593 texturebuffersize = 0;
595 colorconvertbuffer = NULL;
596 texturebuffer = NULL;
597 Mem_FreePool(&texturemempool);
600 static void r_textures_newmap(void)
604 void R_Textures_Init (void)
606 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
607 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
608 Cvar_RegisterVariable (&gl_max_size);
609 Cvar_RegisterVariable (&gl_picmip);
610 Cvar_RegisterVariable (&gl_max_lightmapsize);
611 Cvar_RegisterVariable (&r_lerpimages);
612 Cvar_RegisterVariable (&gl_texture_anisotropy);
613 Cvar_RegisterVariable (&gl_texturecompression);
614 Cvar_RegisterVariable (&gl_texturecompression_color);
615 Cvar_RegisterVariable (&gl_texturecompression_normal);
616 Cvar_RegisterVariable (&gl_texturecompression_gloss);
617 Cvar_RegisterVariable (&gl_texturecompression_glow);
618 Cvar_RegisterVariable (&gl_texturecompression_2d);
619 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
620 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
621 Cvar_RegisterVariable (&gl_texturecompression_sky);
622 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
623 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
625 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
628 void R_Textures_Frame (void)
630 static int old_aniso = 0;
632 // could do procedural texture animation here, if we keep track of which
633 // textures were accessed this frame...
635 // free the resize buffers
636 resizebuffersize = 0;
639 Mem_Free(resizebuffer);
642 if (colorconvertbuffer)
644 Mem_Free(colorconvertbuffer);
645 colorconvertbuffer = NULL;
648 if (old_aniso != gl_texture_anisotropy.integer)
651 gltexturepool_t *pool;
654 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
656 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
660 for (pool = gltexturepoolchain;pool;pool = pool->next)
662 for (glt = pool->gltchain;glt;glt = glt->chain)
664 // only update already uploaded images
665 if ((glt->flags & (GLTEXF_UPLOAD | TEXF_MIPMAP)) == TEXF_MIPMAP)
667 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
669 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
670 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
672 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
679 void R_MakeResizeBufferBigger(int size)
681 if (resizebuffersize < size)
683 resizebuffersize = size;
685 Mem_Free(resizebuffer);
686 if (colorconvertbuffer)
687 Mem_Free(colorconvertbuffer);
688 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
689 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
690 if (!resizebuffer || !colorconvertbuffer)
691 Host_Error("R_Upload: out of memory");
695 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
697 int textureenum = gltexturetypeenums[texturetype];
698 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
702 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
704 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
705 if (gl_texture_anisotropy.integer != aniso)
706 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
707 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
709 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
710 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
711 if (gltexturetypedimensions[texturetype] >= 3)
713 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
717 if (flags & TEXF_FORCENEAREST)
719 if (flags & TEXF_MIPMAP)
721 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
725 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
727 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
729 else if (flags & TEXF_FORCELINEAR)
731 if (flags & TEXF_MIPMAP)
733 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
735 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
739 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
744 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
746 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
750 if (flags & TEXF_MIPMAP)
752 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
756 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
758 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
761 if (textype == TEXTYPE_SHADOWMAP)
763 if (vid.support.arb_shadow)
765 if (flags & TEXF_COMPARE)
767 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
771 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
773 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
775 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
781 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
783 int i, mip, width, height, depth;
785 const unsigned char *prevbuffer;
790 // we need to restore the texture binding after finishing the upload
792 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
793 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
795 // these are rounded up versions of the size to do better resampling
796 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
798 width = glt->inputwidth;
799 height = glt->inputheight;
800 depth = glt->inputdepth;
804 for (width = 1;width < glt->inputwidth ;width <<= 1);
805 for (height = 1;height < glt->inputheight;height <<= 1);
806 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
809 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
810 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
812 if (prevbuffer == NULL)
814 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
815 prevbuffer = resizebuffer;
817 else if (glt->textype->textype == TEXTYPE_PALETTE)
819 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
820 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
821 prevbuffer = colorconvertbuffer;
824 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))
826 // update a portion of the image
827 switch(glt->texturetype)
829 case GLTEXTURETYPE_2D:
830 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
832 case GLTEXTURETYPE_3D:
833 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
836 Host_Error("R_Upload: partial update of type other than 2D");
842 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
843 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
845 // upload the image for the first time
846 glt->flags &= ~GLTEXF_UPLOAD;
848 // cubemaps contain multiple images and thus get processed a bit differently
849 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
851 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
853 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
854 prevbuffer = resizebuffer;
857 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
859 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
860 prevbuffer = resizebuffer;
864 if (qglGetCompressedTexImageARB)
866 if (gl_texturecompression.integer >= 2)
867 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
869 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
872 switch(glt->texturetype)
874 case GLTEXTURETYPE_2D:
875 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
876 if (glt->flags & TEXF_MIPMAP)
878 while (width > 1 || height > 1 || depth > 1)
880 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
881 prevbuffer = resizebuffer;
882 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
886 case GLTEXTURETYPE_3D:
887 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
888 if (glt->flags & TEXF_MIPMAP)
890 while (width > 1 || height > 1 || depth > 1)
892 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
893 prevbuffer = resizebuffer;
894 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
898 case GLTEXTURETYPE_CUBEMAP:
899 // convert and upload each side in turn,
900 // from a continuous block of input texels
901 texturebuffer = (unsigned char *)prevbuffer;
902 for (i = 0;i < 6;i++)
904 prevbuffer = texturebuffer;
905 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
906 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
908 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
909 prevbuffer = resizebuffer;
912 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
914 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
915 prevbuffer = resizebuffer;
918 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
919 if (glt->flags & TEXF_MIPMAP)
921 while (width > 1 || height > 1 || depth > 1)
923 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
924 prevbuffer = resizebuffer;
925 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
930 case GLTEXTURETYPE_RECTANGLE:
931 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
934 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
936 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
939 int R_RealGetTexture(rtexture_t *rt)
944 glt = (gltexture_t *)rt;
945 if (glt->flags & GLTEXF_DYNAMIC)
946 R_UpdateDynamicTexture(glt);
947 if (glt->flags & GLTEXF_UPLOAD)
950 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
951 R_Upload(glt, glt->inputtexels, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
952 if (glt->inputtexels)
954 Mem_Free(glt->inputtexels);
955 glt->inputtexels = NULL;
956 glt->flags |= GLTEXF_DESTROYED;
958 else if (glt->flags & GLTEXF_DESTROYED)
959 Con_Printf("R_GetTexture: Texture %s already uploaded and destroyed. Can not upload original image again. Uploaded blank texture.\n", glt->identifier);
968 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)
972 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
973 textypeinfo_t *texinfo;
975 if (cls.state == ca_dedicated)
978 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
980 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
983 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
985 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
988 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
990 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
994 texinfo = R_GetTexTypeInfo(textype, flags);
995 size = width * height * depth * sides * texinfo->inputbytesperpixel;
998 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
1002 // clear the alpha flag if the texture has no transparent pixels
1005 case TEXTYPE_PALETTE:
1006 if (flags & TEXF_ALPHA)
1008 flags &= ~TEXF_ALPHA;
1011 for (i = 0;i < size;i++)
1013 if (((unsigned char *)&palette[data[i]])[3] < 255)
1015 flags |= TEXF_ALPHA;
1024 if (flags & TEXF_ALPHA)
1026 flags &= ~TEXF_ALPHA;
1029 for (i = 3;i < size;i += 4)
1033 flags |= TEXF_ALPHA;
1040 case TEXTYPE_SHADOWMAP:
1043 flags |= TEXF_ALPHA;
1046 Host_Error("R_LoadTexture: unknown texture type");
1049 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1051 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1053 glt->chain = pool->gltchain;
1054 pool->gltchain = glt;
1055 glt->inputwidth = width;
1056 glt->inputheight = height;
1057 glt->inputdepth = depth;
1058 glt->flags = flags | GLTEXF_UPLOAD;
1059 glt->textype = texinfo;
1060 glt->texturetype = texturetype;
1061 glt->inputdatasize = size;
1062 glt->palette = palette;
1063 glt->glinternalformat = texinfo->glinternalformat;
1064 glt->glformat = texinfo->glformat;
1065 glt->gltype = texinfo->gltype;
1066 glt->bytesperpixel = texinfo->internalbytesperpixel;
1067 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1069 // init the dynamic texture attributes, too [11/22/2007 Black]
1070 glt->dirtytexnum = 0;
1071 glt->updatecallback = NULL;
1072 glt->updatacallback_data = NULL;
1074 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1076 // upload the texture
1077 // data may be NULL (blank texture for dynamic rendering)
1079 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1080 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1081 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1082 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1084 // texture converting and uploading can take a while, so make sure we're sending keepalives
1085 CL_KeepaliveMessage(false);
1087 return (rtexture_t *)glt;
1090 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)
1092 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1095 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)
1097 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1100 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)
1102 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1105 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)
1107 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1110 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1112 int flags = TEXF_CLAMP;
1114 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1116 flags |= TEXF_FORCENEAREST;
1117 if (precision <= 16)
1118 flags |= TEXF_LOWPRECISION;
1122 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1124 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1127 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1129 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1132 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1134 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1137 int R_TextureWidth(rtexture_t *rt)
1139 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1142 int R_TextureHeight(rtexture_t *rt)
1144 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1147 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1149 gltexture_t *glt = (gltexture_t *)rt;
1151 Host_Error("R_UpdateTexture: no data supplied");
1153 Host_Error("R_UpdateTexture: no texture supplied");
1155 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1156 // update part of the texture
1157 if (glt->bufferpixels)
1160 int bpp = glt->bytesperpixel;
1161 int inputskip = width*bpp;
1162 int outputskip = glt->tilewidth*bpp;
1163 const unsigned char *input = data;
1164 unsigned char *output = glt->bufferpixels;
1174 input -= y*inputskip;
1177 if (width > glt->tilewidth - x)
1178 width = glt->tilewidth - x;
1179 if (height > glt->tileheight - y)
1180 height = glt->tileheight - y;
1181 if (width < 1 || height < 1)
1183 glt->buffermodified = true;
1184 output += y*outputskip + x*bpp;
1185 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1186 memcpy(output, input, width*bpp);
1187 if (!(glt->flags & TEXF_MANUALFLUSHUPDATES))
1191 R_Upload(glt, data, x, y, 0, width, height, 1);
1194 void R_FlushTexture(rtexture_t *rt)
1198 Host_Error("R_FlushTexture: no texture supplied");
1200 // update part of the texture
1201 glt = (gltexture_t *)rt;
1203 if (!glt->buffermodified || !glt->bufferpixels)
1205 glt->buffermodified = false;
1206 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1209 void R_ClearTexture (rtexture_t *rt)
1211 gltexture_t *glt = (gltexture_t *)rt;
1213 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );