6 #include "intoverflow.h"
8 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)"};
9 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)"};
10 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%"};
11 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)"};
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", "1", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"};
25 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
26 int gl_filter_mag = GL_LINEAR;
29 static mempool_t *texturemempool;
30 static memexpandablearray_t texturearray;
32 // note: this must not conflict with TEXF_ flags in r_textures.h
33 // bitmask for mismatch checking
34 #define GLTEXF_IMPORTANTBITS (0)
35 // dynamic texture (treat texnum == 0 differently)
36 #define GLTEXF_DYNAMIC 0x00080000
38 typedef struct textypeinfo_s
41 int inputbytesperpixel;
42 int internalbytesperpixel;
43 float glinternalbytesperpixel;
51 static textypeinfo_t textype_palette = {TEXTYPE_PALETTE , 1, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
52 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE , 1, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
53 static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, 3 , GL_RGBA , GL_UNSIGNED_BYTE };
54 static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, 4 , GL_RGBA , GL_UNSIGNED_BYTE };
55 static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
56 static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA , GL_UNSIGNED_BYTE };
57 static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
58 static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
59 static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
60 static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_BGRA , GL_UNSIGNED_BYTE };
61 static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
62 static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
63 static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , GL_ALPHA , GL_UNSIGNED_BYTE };
64 static textypeinfo_t textype_dxt1 = {TEXTYPE_DXT1 , 4, 0, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , 0 , 0 };
65 static textypeinfo_t textype_dxt1a = {TEXTYPE_DXT1A , 4, 0, 0.5f, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0 , 0 };
66 static textypeinfo_t textype_dxt3 = {TEXTYPE_DXT3 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0 , 0 };
67 static textypeinfo_t textype_dxt5 = {TEXTYPE_DXT5 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0 , 0 };
68 static textypeinfo_t textype_colorbuffer = {TEXTYPE_COLORBUFFER, 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
71 typedef enum gltexturetype_e
75 GLTEXTURETYPE_CUBEMAP,
76 GLTEXTURETYPE_RECTANGLE,
81 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
82 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
83 static int cubemapside[6] =
85 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
86 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
87 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
88 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
89 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
90 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
93 typedef struct gltexture_s
95 // this portion of the struct is exposed to the R_GetTexture macro for
96 // speed reasons, must be identical in rtexture_t!
97 int texnum; // GL texture slot number
98 qboolean dirty; // indicates that R_RealGetTexture should be called
99 int gltexturetypeenum; // used by R_Mesh_TexBind
101 // dynamic texture stuff [11/22/2007 Black]
102 updatecallback_t updatecallback;
103 void *updatacallback_data;
104 // --- [11/22/2007 Black]
106 // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
107 unsigned char *bufferpixels;
108 qboolean buffermodified;
110 // pointer to texturepool (check this to see if the texture is allocated)
111 struct gltexturepool_s *pool;
112 // pointer to next texture in texturepool chain
113 struct gltexture_s *chain;
114 // name of the texture (this might be removed someday), no duplicates
115 char identifier[MAX_QPATH + 32];
116 // original data size in *inputtexels
117 int inputwidth, inputheight, inputdepth;
118 // copy of the original texture(s) supplied to the upload function, for
119 // delayed uploads (non-precached)
120 unsigned char *inputtexels;
121 // original data size in *inputtexels
123 // flags supplied to the LoadTexture function
124 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
126 // pointer to one of the textype_ structs
127 textypeinfo_t *textype;
128 // one of the GLTEXTURETYPE_ values
130 // palette if the texture is TEXTYPE_PALETTE
131 const unsigned int *palette;
132 // actual stored texture size after gl_picmip and gl_max_size are applied
133 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
134 int tilewidth, tileheight, tiledepth;
135 // 1 or 6 depending on texturetype
139 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
142 int glinternalformat;
143 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
148 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
150 typedef struct gltexturepool_s
152 unsigned int sentinel;
153 struct gltexture_s *gltchain;
154 struct gltexturepool_s *next;
158 static gltexturepool_t *gltexturepoolchain = NULL;
160 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
161 static int resizebuffersize = 0;
162 static const unsigned char *texturebuffer;
163 static int texturebuffersize = 0;
165 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
170 return &textype_dxt1;
172 return &textype_dxt1a;
174 return &textype_dxt3;
176 return &textype_dxt5;
177 case TEXTYPE_PALETTE:
178 return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
180 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
181 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
183 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
186 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
187 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
189 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
192 return &textype_alpha;
193 case TEXTYPE_SHADOWMAP:
194 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
195 case TEXTYPE_COLORBUFFER:
196 return &textype_colorbuffer;
198 Host_Error("R_GetTexTypeInfo: unknown texture format");
201 return NULL; // this line only to hush compiler warnings
204 // dynamic texture code [11/22/2007 Black]
205 void R_MarkDirtyTexture(rtexture_t *rt) {
206 gltexture_t *glt = (gltexture_t*) rt;
211 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
212 if (glt->flags & GLTEXF_DYNAMIC)
214 // mark it as dirty, so R_RealGetTexture gets called
219 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
220 gltexture_t *glt = (gltexture_t*) rt;
225 glt->flags |= GLTEXF_DYNAMIC;
226 glt->updatecallback = updatecallback;
227 glt->updatacallback_data = data;
230 static void R_UpdateDynamicTexture(gltexture_t *glt) {
232 if( glt->updatecallback ) {
233 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
237 void R_PurgeTexture(rtexture_t *rt)
239 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
244 void R_FreeTexture(rtexture_t *rt)
246 gltexture_t *glt, **gltpointer;
248 glt = (gltexture_t *)rt;
250 Host_Error("R_FreeTexture: texture == NULL");
252 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
253 if (*gltpointer == glt)
254 *gltpointer = glt->chain;
256 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
261 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
264 if (glt->inputtexels)
265 Mem_Free(glt->inputtexels);
266 Mem_ExpandableArray_FreeRecord(&texturearray, glt);
269 rtexturepool_t *R_AllocTexturePool(void)
271 gltexturepool_t *pool;
272 if (texturemempool == NULL)
274 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
277 pool->next = gltexturepoolchain;
278 gltexturepoolchain = pool;
279 pool->sentinel = TEXTUREPOOL_SENTINEL;
280 return (rtexturepool_t *)pool;
283 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
285 gltexturepool_t *pool, **poolpointer;
286 if (rtexturepool == NULL)
288 if (*rtexturepool == NULL)
290 pool = (gltexturepool_t *)(*rtexturepool);
291 *rtexturepool = NULL;
292 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
293 Host_Error("R_FreeTexturePool: pool already freed");
294 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
295 if (*poolpointer == pool)
296 *poolpointer = pool->next;
298 Host_Error("R_FreeTexturePool: pool not linked");
299 while (pool->gltchain)
300 R_FreeTexture((rtexture_t *)pool->gltchain);
305 typedef struct glmode_s
308 int minification, magnification;
312 static glmode_t modes[6] =
314 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
315 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
316 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
317 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
318 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
319 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
322 static void GL_TextureMode_f (void)
327 gltexturepool_t *pool;
331 for (i = 0;i < 6;i++)
333 if (gl_filter_min == modes[i].minification)
335 Con_Printf("%s\n", modes[i].name);
339 Con_Print("current filter is unknown???\n");
343 for (i = 0;i < 6;i++)
344 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
348 Con_Print("bad filter name\n");
352 gl_filter_min = modes[i].minification;
353 gl_filter_mag = modes[i].magnification;
355 // change all the existing mipmap texture objects
356 // FIXME: force renderer(/client/something?) restart instead?
359 for (pool = gltexturepoolchain;pool;pool = pool->next)
361 for (glt = pool->gltchain;glt;glt = glt->chain)
363 // only update already uploaded images
364 if (glt->texnum && !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
366 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
367 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
368 if (glt->flags & TEXF_MIPMAP)
370 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
374 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
376 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
377 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
383 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
385 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
390 case GLTEXTURETYPE_2D:
391 maxsize = vid.maxtexturesize_2d;
392 if (flags & TEXF_PICMIP)
394 maxsize = bound(1, gl_max_size.integer, maxsize);
395 picmip = gl_picmip.integer;
398 case GLTEXTURETYPE_3D:
399 maxsize = vid.maxtexturesize_3d;
401 case GLTEXTURETYPE_CUBEMAP:
402 maxsize = vid.maxtexturesize_cubemap;
408 if (vid.support.arb_texture_non_power_of_two)
409 width2 = min(inwidth >> picmip, maxsize);
412 for (width2 = 1;width2 < inwidth;width2 <<= 1);
413 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
415 *outwidth = max(1, width2);
419 if (vid.support.arb_texture_non_power_of_two)
420 height2 = min(inheight >> picmip, maxsize);
423 for (height2 = 1;height2 < inheight;height2 <<= 1);
424 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
426 *outheight = max(1, height2);
430 if (vid.support.arb_texture_non_power_of_two)
431 depth2 = min(indepth >> picmip, maxsize);
434 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
435 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
437 *outdepth = max(1, depth2);
442 static int R_CalcTexelDataSize (gltexture_t *glt)
444 int width2, height2, depth2, size;
446 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
448 size = width2 * height2 * depth2;
450 if (glt->flags & TEXF_MIPMAP)
452 while (width2 > 1 || height2 > 1 || depth2 > 1)
460 size += width2 * height2 * depth2;
464 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
467 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
471 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
472 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
474 gltexturepool_t *pool;
476 Con_Print("glsize input loaded mip alpha name\n");
477 for (pool = gltexturepoolchain;pool;pool = pool->next)
485 for (glt = pool->gltchain;glt;glt = glt->chain)
487 glsize = R_CalcTexelDataSize(glt);
488 isloaded = glt->texnum != 0;
490 pooltotalt += glsize;
491 pooltotalp += glt->inputdatasize;
495 poolloadedt += glsize;
496 poolloadedp += glt->inputdatasize;
499 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);
502 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);
503 sumtotal += pooltotal;
504 sumtotalt += pooltotalt;
505 sumtotalp += pooltotalp;
506 sumloaded += poolloaded;
507 sumloadedt += poolloadedt;
508 sumloadedp += poolloadedp;
511 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);
514 static void R_TextureStats_f(void)
516 R_TextureStats_Print(true, true, true);
519 static void r_textures_start(void)
521 // LordHavoc: allow any alignment
523 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
524 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
526 texturemempool = Mem_AllocPool("texture management", 0, NULL);
527 Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
529 // Disable JPEG screenshots if the DLL isn't loaded
530 if (! JPEG_OpenLibrary ())
531 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
532 // TODO: support png screenshots?
536 static void r_textures_shutdown(void)
538 rtexturepool_t *temp;
540 JPEG_CloseLibrary ();
542 while(gltexturepoolchain)
544 temp = (rtexturepool_t *) gltexturepoolchain;
545 R_FreeTexturePool(&temp);
548 resizebuffersize = 0;
549 texturebuffersize = 0;
551 colorconvertbuffer = NULL;
552 texturebuffer = NULL;
553 Mem_ExpandableArray_FreeArray(&texturearray);
554 Mem_FreePool(&texturemempool);
557 static void r_textures_newmap(void)
561 void R_Textures_Init (void)
563 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
564 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
565 Cvar_RegisterVariable (&gl_max_size);
566 Cvar_RegisterVariable (&gl_picmip);
567 Cvar_RegisterVariable (&gl_max_lightmapsize);
568 Cvar_RegisterVariable (&r_lerpimages);
569 Cvar_RegisterVariable (&gl_texture_anisotropy);
570 Cvar_RegisterVariable (&gl_texturecompression);
571 Cvar_RegisterVariable (&gl_texturecompression_color);
572 Cvar_RegisterVariable (&gl_texturecompression_normal);
573 Cvar_RegisterVariable (&gl_texturecompression_gloss);
574 Cvar_RegisterVariable (&gl_texturecompression_glow);
575 Cvar_RegisterVariable (&gl_texturecompression_2d);
576 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
577 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
578 Cvar_RegisterVariable (&gl_texturecompression_sky);
579 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
580 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
582 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
585 void R_Textures_Frame (void)
587 static int old_aniso = 0;
589 // could do procedural texture animation here, if we keep track of which
590 // textures were accessed this frame...
592 // free the resize buffers
593 resizebuffersize = 0;
596 Mem_Free(resizebuffer);
599 if (colorconvertbuffer)
601 Mem_Free(colorconvertbuffer);
602 colorconvertbuffer = NULL;
605 if (old_aniso != gl_texture_anisotropy.integer)
608 gltexturepool_t *pool;
611 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
613 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
617 for (pool = gltexturepoolchain;pool;pool = pool->next)
619 for (glt = pool->gltchain;glt;glt = glt->chain)
621 // only update already uploaded images
622 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
624 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
626 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
627 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
629 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
636 void R_MakeResizeBufferBigger(int size)
638 if (resizebuffersize < size)
640 resizebuffersize = size;
642 Mem_Free(resizebuffer);
643 if (colorconvertbuffer)
644 Mem_Free(colorconvertbuffer);
645 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
646 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
647 if (!resizebuffer || !colorconvertbuffer)
648 Host_Error("R_Upload: out of memory");
652 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
654 int textureenum = gltexturetypeenums[texturetype];
655 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
659 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
661 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
662 if (gl_texture_anisotropy.integer != aniso)
663 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
664 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
666 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
667 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
668 if (gltexturetypedimensions[texturetype] >= 3)
670 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
674 if (flags & TEXF_FORCENEAREST)
676 if (flags & TEXF_MIPMAP)
678 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
682 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
684 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
686 else if (flags & TEXF_FORCELINEAR)
688 if (flags & TEXF_MIPMAP)
690 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
692 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
696 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
701 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
703 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
707 if (flags & TEXF_MIPMAP)
709 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
713 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
715 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
718 if (textype == TEXTYPE_SHADOWMAP)
720 if (vid.support.arb_shadow)
722 if (flags & TEXF_COMPARE)
724 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
728 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
730 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
732 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
738 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
740 int i, mip, width, height, depth;
742 const unsigned char *prevbuffer;
747 // we need to restore the texture binding after finishing the upload
749 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
750 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
752 // these are rounded up versions of the size to do better resampling
753 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
755 width = glt->inputwidth;
756 height = glt->inputheight;
757 depth = glt->inputdepth;
761 for (width = 1;width < glt->inputwidth ;width <<= 1);
762 for (height = 1;height < glt->inputheight;height <<= 1);
763 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
766 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
767 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
769 if (prevbuffer == NULL)
771 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
772 prevbuffer = resizebuffer;
774 else if (glt->textype->textype == TEXTYPE_PALETTE)
776 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
777 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
778 prevbuffer = colorconvertbuffer;
781 // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
783 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))
785 // update a portion of the image
786 switch(glt->texturetype)
788 case GLTEXTURETYPE_2D:
789 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
791 case GLTEXTURETYPE_3D:
792 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
795 Host_Error("R_Upload: partial update of type other than 2D");
801 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
802 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
804 // cubemaps contain multiple images and thus get processed a bit differently
805 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
807 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
809 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
810 prevbuffer = resizebuffer;
813 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
815 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
816 prevbuffer = resizebuffer;
820 if (qglGetCompressedTexImageARB)
822 if (gl_texturecompression.integer >= 2)
823 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
825 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
828 switch(glt->texturetype)
830 case GLTEXTURETYPE_2D:
831 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
832 if (glt->flags & TEXF_MIPMAP)
834 while (width > 1 || height > 1 || depth > 1)
836 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
837 prevbuffer = resizebuffer;
838 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
842 case GLTEXTURETYPE_3D:
843 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
844 if (glt->flags & TEXF_MIPMAP)
846 while (width > 1 || height > 1 || depth > 1)
848 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
849 prevbuffer = resizebuffer;
850 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
854 case GLTEXTURETYPE_CUBEMAP:
855 // convert and upload each side in turn,
856 // from a continuous block of input texels
857 texturebuffer = (unsigned char *)prevbuffer;
858 for (i = 0;i < 6;i++)
860 prevbuffer = texturebuffer;
861 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
862 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
864 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
865 prevbuffer = resizebuffer;
868 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
870 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
871 prevbuffer = resizebuffer;
874 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
875 if (glt->flags & TEXF_MIPMAP)
877 while (width > 1 || height > 1 || depth > 1)
879 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
880 prevbuffer = resizebuffer;
881 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
886 case GLTEXTURETYPE_RECTANGLE:
887 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
890 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
892 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
895 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)
899 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
900 textypeinfo_t *texinfo, *texinfo2;
902 if (cls.state == ca_dedicated)
905 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
907 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
910 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
912 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
915 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
917 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
921 texinfo = R_GetTexTypeInfo(textype, flags);
922 size = width * height * depth * sides * texinfo->inputbytesperpixel;
925 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
929 // clear the alpha flag if the texture has no transparent pixels
932 case TEXTYPE_PALETTE:
933 if (flags & TEXF_ALPHA)
935 flags &= ~TEXF_ALPHA;
938 for (i = 0;i < size;i++)
940 if (((unsigned char *)&palette[data[i]])[3] < 255)
951 if (flags & TEXF_ALPHA)
953 flags &= ~TEXF_ALPHA;
956 for (i = 3;i < size;i += 4)
967 case TEXTYPE_SHADOWMAP:
979 case TEXTYPE_COLORBUFFER:
983 Host_Error("R_LoadTexture: unknown texture type");
986 texinfo2 = R_GetTexTypeInfo(textype, flags);
987 if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
990 Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
992 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
994 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
996 glt->chain = pool->gltchain;
997 pool->gltchain = glt;
998 glt->inputwidth = width;
999 glt->inputheight = height;
1000 glt->inputdepth = depth;
1002 glt->textype = texinfo;
1003 glt->texturetype = texturetype;
1004 glt->inputdatasize = size;
1005 glt->palette = palette;
1006 glt->glinternalformat = texinfo->glinternalformat;
1007 glt->glformat = texinfo->glformat;
1008 glt->gltype = texinfo->gltype;
1009 glt->bytesperpixel = texinfo->internalbytesperpixel;
1010 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1013 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1014 // init the dynamic texture attributes, too [11/22/2007 Black]
1015 glt->updatecallback = NULL;
1016 glt->updatacallback_data = NULL;
1018 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1020 // upload the texture
1021 // data may be NULL (blank texture for dynamic rendering)
1023 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1024 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1025 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1026 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1028 // texture converting and uploading can take a while, so make sure we're sending keepalives
1029 CL_KeepaliveMessage(false);
1031 return (rtexture_t *)glt;
1034 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)
1036 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1039 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)
1041 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1044 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)
1046 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1049 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)
1051 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1054 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1056 int flags = TEXF_CLAMP;
1058 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1060 flags |= TEXF_FORCENEAREST;
1061 if (precision <= 16)
1062 flags |= TEXF_LOWPRECISION;
1066 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1068 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1071 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1073 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1076 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1078 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1081 int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
1083 gltexture_t *glt = (gltexture_t *)rt;
1086 int bytesperpixel = 0;
1087 int bytesperblock = 0;
1089 int dds_format_flags;
1097 GLint internalformat;
1098 const char *ddsfourcc;
1100 return -1; // NULL pointer
1101 if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
1102 return -2; // broken driver - crashes on reading internal format
1103 if (!qglGetTexLevelParameteriv)
1105 GL_ActiveTexture(0);
1106 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1107 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1108 qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
1109 switch(internalformat)
1111 default: ddsfourcc = NULL;bytesperpixel = 4;break;
1112 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1113 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
1114 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
1115 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
1117 if (!bytesperblock && skipuncompressed)
1118 return -3; // skipped
1119 memset(mipinfo, 0, sizeof(mipinfo));
1120 mipinfo[0][0] = glt->tilewidth;
1121 mipinfo[0][1] = glt->tileheight;
1123 if (glt->flags & TEXF_MIPMAP)
1125 for (mip = 1;mip < 16;mip++)
1127 mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
1128 mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
1129 if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
1137 for (mip = 0;mip < mipmaps;mip++)
1139 mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
1140 mipinfo[mip][3] = ddssize;
1141 ddssize += mipinfo[mip][2];
1143 dds = Mem_Alloc(tempmempool, ddssize);
1146 dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
1150 dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
1151 dds_format_flags = 0x4; // DDPF_FOURCC
1155 dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
1156 dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
1160 dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
1161 dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
1163 memcpy(dds, "DDS ", 4);
1164 StoreLittleLong(dds+4, ddssize);
1165 StoreLittleLong(dds+8, dds_flags);
1166 StoreLittleLong(dds+12, mipinfo[0][1]); // height
1167 StoreLittleLong(dds+16, mipinfo[0][0]); // width
1168 StoreLittleLong(dds+24, 1); // depth
1169 StoreLittleLong(dds+28, mipmaps); // mipmaps
1170 StoreLittleLong(dds+76, 32); // format size
1171 StoreLittleLong(dds+80, dds_format_flags);
1172 StoreLittleLong(dds+108, dds_caps1);
1173 StoreLittleLong(dds+112, dds_caps2);
1176 StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
1177 memcpy(dds+84, ddsfourcc, 4);
1178 for (mip = 0;mip < mipmaps;mip++)
1180 qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
1185 StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
1186 StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
1187 dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
1188 for (mip = 0;mip < mipmaps;mip++)
1190 qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
1193 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1194 ret = FS_WriteFile(filename, dds, ddssize);
1196 return ret ? ddssize : -5;
1199 rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
1201 int i, size, dds_flags, dds_format_flags, dds_miplevels, dds_width, dds_height, textype;
1202 int bytesperblock, bytesperpixel;
1205 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
1206 textypeinfo_t *texinfo;
1207 int mip, mipwidth, mipheight, mipsize;
1209 GLint oldbindtexnum;
1210 const unsigned char *mippixels, *ddspixels;
1212 fs_offset_t ddsfilesize;
1213 unsigned int ddssize;
1215 if (cls.state == ca_dedicated)
1218 dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
1219 ddssize = ddsfilesize;
1223 Log_Printf("ddstexturefailures.log", "%s\n", filename);
1224 return NULL; // not found
1227 if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
1230 Con_Printf("^1%s: not a DDS image\n", filename);
1234 dds_flags = BuffLittleLong(dds+8);
1235 dds_format_flags = BuffLittleLong(dds+80);
1236 dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
1237 dds_width = BuffLittleLong(dds+16);
1238 dds_height = BuffLittleLong(dds+12);
1239 ddspixels = dds + 128;
1241 flags &= ~TEXF_ALPHA;
1242 if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
1244 // very sloppy BGRA 32bit identification
1245 textype = TEXTYPE_BGRA;
1248 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
1249 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1252 Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
1256 for (i = 3;i < size;i += 4)
1257 if (ddspixels[i] < 255)
1260 flags &= ~TEXF_ALPHA;
1262 else if (!memcmp(dds+84, "DXT1", 4))
1264 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1265 // LordHavoc: it is my belief that this does not infringe on the
1266 // patent because it is not decoding pixels...
1267 textype = TEXTYPE_DXT1;
1270 //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1271 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1272 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1275 Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
1278 for (i = 0;i < size;i += bytesperblock)
1279 if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1282 textype = TEXTYPE_DXT1A;
1284 flags &= ~TEXF_ALPHA;
1286 else if (!memcmp(dds+84, "DXT3", 4))
1288 textype = TEXTYPE_DXT3;
1291 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1292 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1295 Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
1299 else if (!memcmp(dds+84, "DXT5", 4))
1301 textype = TEXTYPE_DXT5;
1304 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1305 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1308 Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
1315 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1319 // return whether this texture is transparent
1321 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1323 // calculate average color if requested
1327 Vector4Clear(avgcolor);
1330 for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1332 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1333 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1334 avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
1335 avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
1337 f = (float)bytesperblock / size;
1338 avgcolor[0] *= (0.5f / 31.0f) * f;
1339 avgcolor[1] *= (0.5f / 63.0f) * f;
1340 avgcolor[2] *= (0.5f / 31.0f) * f;
1341 avgcolor[3] = 1; // too hard to calculate
1345 for (i = 0;i < size;i += 4)
1347 avgcolor[0] += ddspixels[i+2];
1348 avgcolor[1] += ddspixels[i+1];
1349 avgcolor[2] += ddspixels[i];
1350 avgcolor[3] += ddspixels[i+3];
1352 f = (1.0f / 255.0f) * bytesperpixel / size;
1360 if (dds_miplevels > 1)
1361 flags |= TEXF_MIPMAP;
1363 flags &= ~TEXF_MIPMAP;
1365 // if S3TC is not supported, there's very little we can do about it
1366 if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1369 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1373 texinfo = R_GetTexTypeInfo(textype, flags);
1375 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
1376 strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1378 glt->chain = pool->gltchain;
1379 pool->gltchain = glt;
1380 glt->inputwidth = dds_width;
1381 glt->inputheight = dds_height;
1382 glt->inputdepth = 1;
1384 glt->textype = texinfo;
1385 glt->texturetype = GLTEXTURETYPE_2D;
1386 glt->inputdatasize = ddssize;
1387 glt->glinternalformat = texinfo->glinternalformat;
1388 glt->glformat = texinfo->glformat;
1389 glt->gltype = texinfo->gltype;
1390 glt->bytesperpixel = texinfo->internalbytesperpixel;
1392 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1393 glt->tilewidth = dds_width;
1394 glt->tileheight = dds_height;
1397 // texture uploading can take a while, so make sure we're sending keepalives
1398 CL_KeepaliveMessage(false);
1400 // upload the texture
1401 // we need to restore the texture binding after finishing the upload
1403 GL_ActiveTexture(0);
1404 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1405 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1406 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1407 mippixels = ddspixels;
1408 mipwidth = dds_width;
1409 mipheight = dds_height;
1410 mipcomplete = false;
1411 for (mip = 0;mip < dds_miplevels+1;mip++)
1413 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1414 if (mippixels + mipsize > dds + ddssize)
1418 qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1422 qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1424 mippixels += mipsize;
1425 if (mipwidth <= 1 && mipheight <= 1)
1435 if (dds_miplevels > 1 && !mipcomplete)
1437 // need to set GL_TEXTURE_MAX_LEVEL
1438 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1440 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1441 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1444 return (rtexture_t *)glt;
1447 int R_TextureWidth(rtexture_t *rt)
1449 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1452 int R_TextureHeight(rtexture_t *rt)
1454 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1457 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1459 gltexture_t *glt = (gltexture_t *)rt;
1461 Host_Error("R_UpdateTexture: no data supplied");
1463 Host_Error("R_UpdateTexture: no texture supplied");
1465 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1466 // update part of the texture
1467 if (glt->bufferpixels)
1470 int bpp = glt->bytesperpixel;
1471 int inputskip = width*bpp;
1472 int outputskip = glt->tilewidth*bpp;
1473 const unsigned char *input = data;
1474 unsigned char *output = glt->bufferpixels;
1484 input -= y*inputskip;
1487 if (width > glt->tilewidth - x)
1488 width = glt->tilewidth - x;
1489 if (height > glt->tileheight - y)
1490 height = glt->tileheight - y;
1491 if (width < 1 || height < 1)
1494 glt->buffermodified = true;
1495 output += y*outputskip + x*bpp;
1496 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1497 memcpy(output, input, width*bpp);
1500 R_Upload(glt, data, x, y, 0, width, height, 1);
1503 int R_RealGetTexture(rtexture_t *rt)
1508 glt = (gltexture_t *)rt;
1509 if (glt->flags & GLTEXF_DYNAMIC)
1510 R_UpdateDynamicTexture(glt);
1511 if (glt->buffermodified && glt->bufferpixels)
1513 glt->buffermodified = false;
1514 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1523 void R_ClearTexture (rtexture_t *rt)
1525 gltexture_t *glt = (gltexture_t *)rt;
1527 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );