7 cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
8 cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
9 cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0", "reduces resolution of textures by powers of 2, for example 1 will halve width/height, reducing texture memory usage by 75%"};
10 cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1", "bilinear filters images when scaling them up to power of 2 size (mode 1), looks better than glquake (mode 0)"};
11 cvar_t r_precachetextures = {CVAR_SAVE, "r_precachetextures", "1", "0 = never upload textures until used, 1 = upload most textures before use (exceptions: rarely used skin colormap layers), 2 = upload all textures before use (can increase texture memory usage significantly)"};
12 cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
13 cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
14 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
15 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
16 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
17 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
18 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
19 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
20 cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
21 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
22 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
23 cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "0", "use alternate path for dynamic lightmap updates"};
25 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
26 int gl_filter_mag = GL_LINEAR;
29 static mempool_t *texturemempool;
31 // note: this must not conflict with TEXF_ flags in r_textures.h
32 // cleared when a texture is uploaded
33 #define GLTEXF_UPLOAD 0x00010000
34 // bitmask for mismatch checking
35 #define GLTEXF_IMPORTANTBITS (0)
36 // set when image is uploaded and freed
37 #define GLTEXF_DESTROYED 0x00040000
38 // dynamic texture (treat texnum == 0 differently)
39 #define GLTEXF_DYNAMIC 0x00080000
41 typedef struct textypeinfo_s
44 int inputbytesperpixel;
45 int internalbytesperpixel;
46 float glinternalbytesperpixel;
53 static textypeinfo_t textype_palette = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 3, GL_UNSIGNED_BYTE};
54 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 4, GL_UNSIGNED_BYTE};
55 static textypeinfo_t textype_palette_compress = {TEXTYPE_PALETTE, 1, 4, 0.5f, GL_BGRA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
56 static textypeinfo_t textype_palette_alpha_compress = {TEXTYPE_PALETTE, 1, 4, 1.0f, GL_BGRA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
57 static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 3, GL_UNSIGNED_BYTE};
58 static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 4, GL_UNSIGNED_BYTE};
59 static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_RGBA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
60 static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_RGBA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
61 static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 3, GL_UNSIGNED_BYTE};
62 static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 4, GL_UNSIGNED_BYTE};
63 static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_BGRA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
64 static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_BGRA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
65 static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP,2,2, 2.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16_ARB, GL_UNSIGNED_SHORT};
66 static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP,4,4, 4.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24_ARB, GL_UNSIGNED_INT};
68 typedef enum gltexturetype_e
72 GLTEXTURETYPE_CUBEMAP,
73 GLTEXTURETYPE_RECTANGLE,
78 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
79 static int gltexturetypebindingenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB, GL_TEXTURE_BINDING_RECTANGLE_ARB};
80 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
81 static int cubemapside[6] =
83 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
84 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
85 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
86 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
87 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
88 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
91 typedef struct gltexture_s
93 // this field is exposed to the R_GetTexture macro, for speed reasons
94 // (must be identical in rtexture_t)
95 int texnum; // GL texture slot number
97 // dynamic texture stuff [11/22/2007 Black]
98 // used to hold the texture number of dirty textures
100 updatecallback_t updatecallback;
101 void *updatacallback_data;
102 // --- [11/22/2007 Black]
104 // stores backup copy of texture for deferred texture updates (r_nopartialtextureupdates cvar)
105 unsigned char *bufferpixels;
106 qboolean buffermodified;
108 // pointer to texturepool (check this to see if the texture is allocated)
109 struct gltexturepool_s *pool;
110 // pointer to next texture in texturepool chain
111 struct gltexture_s *chain;
112 // name of the texture (this might be removed someday), no duplicates
113 char identifier[MAX_QPATH + 32];
114 // original data size in *inputtexels
115 int inputwidth, inputheight, inputdepth;
116 // copy of the original texture(s) supplied to the upload function, for
117 // delayed uploads (non-precached)
118 unsigned char *inputtexels;
119 // original data size in *inputtexels
121 // flags supplied to the LoadTexture function
122 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
124 // pointer to one of the textype_ structs
125 textypeinfo_t *textype;
126 // one of the GLTEXTURETYPE_ values
128 // palette if the texture is TEXTYPE_PALETTE
129 const unsigned int *palette;
130 // actual stored texture size after gl_picmip and gl_max_size are applied
131 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
132 int tilewidth, tileheight, tiledepth;
133 // 1 or 6 depending on texturetype
137 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
140 int glinternalformat;
141 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
146 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
148 typedef struct gltexturepool_s
150 unsigned int sentinel;
151 struct gltexture_s *gltchain;
152 struct gltexturepool_s *next;
156 static gltexturepool_t *gltexturepoolchain = NULL;
158 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
159 static int resizebuffersize = 0;
160 static const unsigned char *texturebuffer;
161 static int texturebuffersize = 0;
163 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
165 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
167 if (flags & TEXF_ALPHA)
171 case TEXTYPE_PALETTE:
172 return &textype_palette_alpha_compress;
174 return &textype_rgba_alpha_compress;
176 return &textype_bgra_alpha_compress;
178 Host_Error("R_GetTexTypeInfo: unknown texture format");
186 case TEXTYPE_PALETTE:
187 return &textype_palette_compress;
189 return &textype_rgba_compress;
191 return &textype_bgra_compress;
193 Host_Error("R_GetTexTypeInfo: unknown texture format");
200 if (flags & TEXF_ALPHA)
204 case TEXTYPE_PALETTE:
205 return &textype_palette_alpha;
207 return &textype_rgba_alpha;
209 return &textype_bgra_alpha;
211 Host_Error("R_GetTexTypeInfo: unknown texture format");
219 case TEXTYPE_PALETTE:
220 return &textype_palette;
222 return &textype_rgba;
224 return &textype_bgra;
225 case TEXTYPE_SHADOWMAP:
226 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
228 Host_Error("R_GetTexTypeInfo: unknown texture format");
233 return NULL; // this line only to hush compiler warnings
236 // dynamic texture code [11/22/2007 Black]
237 void R_MarkDirtyTexture(rtexture_t *rt) {
238 gltexture_t *glt = (gltexture_t*) rt;
243 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
244 if( !glt->dirtytexnum && glt->flags & GLTEXF_DYNAMIC ) {
245 glt->dirtytexnum = glt->texnum;
246 // mark it as dirty, so R_RealGetTexture gets called
251 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
252 gltexture_t *glt = (gltexture_t*) rt;
257 glt->flags |= GLTEXF_DYNAMIC;
258 glt->updatecallback = updatecallback;
259 glt->updatacallback_data = data;
260 glt->dirtytexnum = 0;
263 static void R_UpdateDynamicTexture(gltexture_t *glt) {
264 glt->texnum = glt->dirtytexnum;
265 // reset dirtytexnum again (not dirty anymore)
266 glt->dirtytexnum = 0;
267 // TODO: now assert that t->texnum != 0 ?
268 if( glt->updatecallback ) {
269 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
273 void R_PurgeTexture(rtexture_t *rt)
275 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
280 void R_FreeTexture(rtexture_t *rt)
282 gltexture_t *glt, **gltpointer;
284 glt = (gltexture_t *)rt;
286 Host_Error("R_FreeTexture: texture == NULL");
288 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
289 if (*gltpointer == glt)
290 *gltpointer = glt->chain;
292 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
294 if (!(glt->flags & GLTEXF_UPLOAD))
297 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
300 if (glt->inputtexels)
301 Mem_Free(glt->inputtexels);
305 rtexturepool_t *R_AllocTexturePool(void)
307 gltexturepool_t *pool;
308 if (texturemempool == NULL)
310 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
313 pool->next = gltexturepoolchain;
314 gltexturepoolchain = pool;
315 pool->sentinel = TEXTUREPOOL_SENTINEL;
316 return (rtexturepool_t *)pool;
319 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
321 gltexturepool_t *pool, **poolpointer;
322 if (rtexturepool == NULL)
324 if (*rtexturepool == NULL)
326 pool = (gltexturepool_t *)(*rtexturepool);
327 *rtexturepool = NULL;
328 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
329 Host_Error("R_FreeTexturePool: pool already freed");
330 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
331 if (*poolpointer == pool)
332 *poolpointer = pool->next;
334 Host_Error("R_FreeTexturePool: pool not linked");
335 while (pool->gltchain)
336 R_FreeTexture((rtexture_t *)pool->gltchain);
341 typedef struct glmode_s
344 int minification, magnification;
348 static glmode_t modes[6] =
350 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
351 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
352 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
353 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
354 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
355 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
358 static void GL_TextureMode_f (void)
363 gltexturepool_t *pool;
367 for (i = 0;i < 6;i++)
369 if (gl_filter_min == modes[i].minification)
371 Con_Printf("%s\n", modes[i].name);
375 Con_Print("current filter is unknown???\n");
379 for (i = 0;i < 6;i++)
380 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
384 Con_Print("bad filter name\n");
388 gl_filter_min = modes[i].minification;
389 gl_filter_mag = modes[i].magnification;
391 // change all the existing mipmap texture objects
392 // FIXME: force renderer(/client/something?) restart instead?
395 for (pool = gltexturepoolchain;pool;pool = pool->next)
397 for (glt = pool->gltchain;glt;glt = glt->chain)
399 // only update already uploaded images
400 if (!(glt->flags & (GLTEXF_UPLOAD | TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
402 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
403 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
404 if (glt->flags & TEXF_MIPMAP)
406 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
410 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
412 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
413 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
419 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
421 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
426 case GLTEXTURETYPE_2D:
427 maxsize = vid.maxtexturesize_2d;
428 if (flags & TEXF_PICMIP)
430 maxsize = bound(1, gl_max_size.integer, maxsize);
431 picmip = gl_picmip.integer;
434 case GLTEXTURETYPE_3D:
435 maxsize = vid.maxtexturesize_3d;
437 case GLTEXTURETYPE_CUBEMAP:
438 maxsize = vid.maxtexturesize_cubemap;
444 if (vid.support.arb_texture_non_power_of_two)
445 width2 = min(inwidth >> picmip, maxsize);
448 for (width2 = 1;width2 < inwidth;width2 <<= 1);
449 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
451 *outwidth = max(1, width2);
455 if (vid.support.arb_texture_non_power_of_two)
456 height2 = min(inheight >> picmip, maxsize);
459 for (height2 = 1;height2 < inheight;height2 <<= 1);
460 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
462 *outheight = max(1, height2);
466 if (vid.support.arb_texture_non_power_of_two)
467 depth2 = min(indepth >> picmip, maxsize);
470 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
471 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
473 *outdepth = max(1, depth2);
478 static int R_CalcTexelDataSize (gltexture_t *glt)
480 int width2, height2, depth2, size;
482 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
484 size = width2 * height2 * depth2;
486 if (glt->flags & TEXF_MIPMAP)
488 while (width2 > 1 || height2 > 1 || depth2 > 1)
496 size += width2 * height2 * depth2;
500 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
503 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
507 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
508 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
510 gltexturepool_t *pool;
512 Con_Print("glsize input loaded mip alpha name\n");
513 for (pool = gltexturepoolchain;pool;pool = pool->next)
521 for (glt = pool->gltchain;glt;glt = glt->chain)
523 glsize = R_CalcTexelDataSize(glt);
524 isloaded = !(glt->flags & GLTEXF_UPLOAD);
526 pooltotalt += glsize;
527 pooltotalp += glt->inputdatasize;
531 poolloadedt += glsize;
532 poolloadedp += glt->inputdatasize;
535 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);
538 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);
539 sumtotal += pooltotal;
540 sumtotalt += pooltotalt;
541 sumtotalp += pooltotalp;
542 sumloaded += poolloaded;
543 sumloadedt += poolloadedt;
544 sumloadedp += poolloadedp;
547 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);
550 static void R_TextureStats_f(void)
552 R_TextureStats_Print(true, true, true);
555 static void r_textures_start(void)
557 // LordHavoc: allow any alignment
559 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
560 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
562 texturemempool = Mem_AllocPool("texture management", 0, NULL);
564 // Disable JPEG screenshots if the DLL isn't loaded
565 if (! JPEG_OpenLibrary ())
566 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
567 // TODO: support png screenshots?
571 static void r_textures_shutdown(void)
573 rtexturepool_t *temp;
575 JPEG_CloseLibrary ();
577 while(gltexturepoolchain)
579 temp = (rtexturepool_t *) gltexturepoolchain;
580 R_FreeTexturePool(&temp);
583 resizebuffersize = 0;
584 texturebuffersize = 0;
586 colorconvertbuffer = NULL;
587 texturebuffer = NULL;
588 Mem_FreePool(&texturemempool);
591 static void r_textures_newmap(void)
595 void R_Textures_Init (void)
597 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
598 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
599 Cvar_RegisterVariable (&gl_max_size);
600 Cvar_RegisterVariable (&gl_picmip);
601 Cvar_RegisterVariable (&gl_max_lightmapsize);
602 Cvar_RegisterVariable (&r_lerpimages);
603 Cvar_RegisterVariable (&r_precachetextures);
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->flags & (GLTEXF_UPLOAD | 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 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))
818 // update a portion of the image
819 switch(glt->texturetype)
821 case GLTEXTURETYPE_2D:
822 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
824 case GLTEXTURETYPE_3D:
825 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
828 Host_Error("R_Upload: partial update of type other than 2D");
834 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
835 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
837 // upload the image for the first time
838 glt->flags &= ~GLTEXF_UPLOAD;
840 // cubemaps contain multiple images and thus get processed a bit differently
841 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
843 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
845 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
846 prevbuffer = resizebuffer;
849 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
851 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
852 prevbuffer = resizebuffer;
856 if (qglGetCompressedTexImageARB)
858 if (gl_texturecompression.integer >= 2)
859 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
861 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
864 switch(glt->texturetype)
866 case GLTEXTURETYPE_2D:
867 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
868 if (glt->flags & TEXF_MIPMAP)
870 while (width > 1 || height > 1 || depth > 1)
872 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
873 prevbuffer = resizebuffer;
874 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
878 case GLTEXTURETYPE_3D:
879 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
880 if (glt->flags & TEXF_MIPMAP)
882 while (width > 1 || height > 1 || depth > 1)
884 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
885 prevbuffer = resizebuffer;
886 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
890 case GLTEXTURETYPE_CUBEMAP:
891 // convert and upload each side in turn,
892 // from a continuous block of input texels
893 texturebuffer = (unsigned char *)prevbuffer;
894 for (i = 0;i < 6;i++)
896 prevbuffer = texturebuffer;
897 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
898 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
900 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
901 prevbuffer = resizebuffer;
904 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
906 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
907 prevbuffer = resizebuffer;
910 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
911 if (glt->flags & TEXF_MIPMAP)
913 while (width > 1 || height > 1 || depth > 1)
915 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
916 prevbuffer = resizebuffer;
917 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
922 case GLTEXTURETYPE_RECTANGLE:
923 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
926 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
928 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
931 int R_RealGetTexture(rtexture_t *rt)
936 glt = (gltexture_t *)rt;
937 if (glt->flags & GLTEXF_DYNAMIC)
938 R_UpdateDynamicTexture(glt);
939 if (glt->flags & GLTEXF_UPLOAD)
942 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
943 R_Upload(glt, glt->inputtexels, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
944 if (glt->inputtexels)
946 Mem_Free(glt->inputtexels);
947 glt->inputtexels = NULL;
948 glt->flags |= GLTEXF_DESTROYED;
950 else if (glt->flags & GLTEXF_DESTROYED)
951 Con_Printf("R_GetTexture: Texture %s already uploaded and destroyed. Can not upload original image again. Uploaded blank texture.\n", glt->identifier);
960 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)
964 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
965 textypeinfo_t *texinfo;
968 if (cls.state == ca_dedicated)
971 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
973 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
976 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
978 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
981 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
983 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
987 texinfo = R_GetTexTypeInfo(textype, flags);
988 size = width * height * depth * sides * texinfo->inputbytesperpixel;
991 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
995 // clear the alpha flag if the texture has no transparent pixels
998 case TEXTYPE_PALETTE:
999 if (flags & TEXF_ALPHA)
1001 flags &= ~TEXF_ALPHA;
1004 for (i = 0;i < size;i++)
1006 if (((unsigned char *)&palette[data[i]])[3] < 255)
1008 flags |= TEXF_ALPHA;
1017 if (flags & TEXF_ALPHA)
1019 flags &= ~TEXF_ALPHA;
1022 for (i = 3;i < size;i += 4)
1026 flags |= TEXF_ALPHA;
1033 case TEXTYPE_SHADOWMAP:
1036 Host_Error("R_LoadTexture: unknown texture type");
1039 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1041 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1043 glt->chain = pool->gltchain;
1044 pool->gltchain = glt;
1045 glt->inputwidth = width;
1046 glt->inputheight = height;
1047 glt->inputdepth = depth;
1048 glt->flags = flags | GLTEXF_UPLOAD;
1049 glt->textype = texinfo;
1050 glt->texturetype = texturetype;
1051 glt->inputdatasize = size;
1052 glt->palette = palette;
1053 glt->glinternalformat = texinfo->glinternalformat;
1054 glt->glformat = texinfo->glformat;
1055 glt->gltype = texinfo->gltype;
1056 glt->bytesperpixel = texinfo->internalbytesperpixel;
1057 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1059 // init the dynamic texture attributes, too [11/22/2007 Black]
1060 glt->dirtytexnum = 0;
1061 glt->updatecallback = NULL;
1062 glt->updatacallback_data = NULL;
1064 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1067 if (glt->flags & TEXF_ALWAYSPRECACHE)
1069 else if (r_precachetextures.integer >= 2)
1071 else if (r_precachetextures.integer >= 1)
1072 if (glt->flags & TEXF_PRECACHE)
1077 // immediate upload (most common case)
1078 // data may be NULL (blank texture for dynamic rendering)
1080 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1081 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1082 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1083 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1087 // deferred texture upload (menu graphics)
1088 // optimize first if possible
1089 if ((textype == TEXTYPE_BGRA || textype == TEXTYPE_RGBA) && glt->inputwidth * glt->inputheight * glt->inputdepth > glt->tilewidth * glt->tileheight * glt->tiledepth)
1091 glt->inputtexels = (unsigned char *)Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1092 Image_Resample32(data, glt->inputwidth, glt->inputheight, glt->inputdepth, glt->inputtexels, glt->tilewidth, glt->tileheight, glt->tiledepth, r_lerpimages.integer);
1093 // change texture size accordingly
1094 glt->inputwidth = glt->tilewidth;
1095 glt->inputheight = glt->tileheight;
1096 glt->inputdepth = glt->tiledepth;
1097 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1101 glt->inputtexels = (unsigned char *)Mem_Alloc(texturemempool, size);
1102 memcpy(glt->inputtexels, data, size);
1106 // texture converting and uploading can take a while, so make sure we're sending keepalives
1107 CL_KeepaliveMessage(false);
1109 return (rtexture_t *)glt;
1112 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)
1114 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1117 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)
1119 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1122 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)
1124 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1127 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)
1129 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1132 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1134 int flags = TEXF_ALWAYSPRECACHE | TEXF_CLAMP;
1136 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1138 flags |= TEXF_FORCENEAREST;
1139 if (precision <= 16)
1140 flags |= TEXF_LOWPRECISION;
1144 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1146 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1149 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1151 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1154 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1156 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1159 int R_TextureWidth(rtexture_t *rt)
1161 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1164 int R_TextureHeight(rtexture_t *rt)
1166 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1169 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1171 gltexture_t *glt = (gltexture_t *)rt;
1173 Host_Error("R_UpdateTexture: no data supplied");
1175 Host_Error("R_UpdateTexture: no texture supplied");
1177 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1178 // update part of the texture
1179 if (glt->bufferpixels)
1182 int bpp = glt->bytesperpixel;
1183 int inputskip = width*bpp;
1184 int outputskip = glt->tilewidth*bpp;
1185 const unsigned char *input = data;
1186 unsigned char *output = glt->bufferpixels;
1196 input -= y*inputskip;
1199 if (width > glt->tilewidth - x)
1200 width = glt->tilewidth - x;
1201 if (height > glt->tileheight - y)
1202 height = glt->tileheight - y;
1203 if (width < 1 || height < 1)
1205 glt->buffermodified = true;
1206 output += y*outputskip + x*bpp;
1207 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1208 memcpy(output, input, width*bpp);
1209 if (!(glt->flags & TEXF_MANUALFLUSHUPDATES))
1213 R_Upload(glt, data, x, y, 0, width, height, 1);
1216 void R_FlushTexture(rtexture_t *rt)
1220 Host_Error("R_FlushTexture: no texture supplied");
1222 // update part of the texture
1223 glt = (gltexture_t *)rt;
1225 if (!glt->buffermodified || !glt->bufferpixels)
1227 glt->buffermodified = false;
1228 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1231 void R_ClearTexture (rtexture_t *rt)
1233 gltexture_t *glt = (gltexture_t *)rt;
1235 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );