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 gl_picmip_world = {CVAR_SAVE, "gl_picmip_world", "0", "extra picmip level for world textures (may be negative, which will then reduce gl_picmip for these)"};
12 cvar_t r_picmipworld = {CVAR_SAVE, "r_picmipworld", "1", "whether gl_picmip shall apply to world textures too (setting this to 0 is a shorthand for gl_picmip_world -9999999)"};
13 cvar_t gl_picmip_sprites = {CVAR_SAVE, "gl_picmip_sprites", "0", "extra picmip level for sprite textures (may be negative, which will then reduce gl_picmip for these)"};
14 cvar_t r_picmipsprites = {CVAR_SAVE, "r_picmipsprites", "1", "make gl_picmip affect sprites too (saves some graphics memory in sprite heavy games) (setting this to 0 is a shorthand for gl_picmip_sprites -9999999)"};
15 cvar_t gl_picmip_other = {CVAR_SAVE, "gl_picmip_other", "0", "extra picmip level for other textures (may be negative, which will then reduce gl_picmip for these)"};
16 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)"};
17 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"};
18 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"};
19 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
20 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
21 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
22 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
23 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
24 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
25 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)"};
26 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
27 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
28 cvar_t gl_texturecompression_reflectmask = {CVAR_SAVE, "gl_texturecompression_reflectmask", "1", "whether to compress reflection cubemap masks (mask of which areas of the texture should reflect the generic shiny cubemap)"};
29 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"};
30 cvar_t r_texture_dds_load_dxt1_noalpha = {0, "r_texture_dds_load_dxt1_noalpha", "0", "if set, alpha detection on DXT1 is turned off, and DXT1 textures are assumed to never have alpha"};
32 qboolean gl_filter_force = false;
33 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
34 int gl_filter_mag = GL_LINEAR;
37 static mempool_t *texturemempool;
38 static memexpandablearray_t texturearray;
40 // note: this must not conflict with TEXF_ flags in r_textures.h
41 // bitmask for mismatch checking
42 #define GLTEXF_IMPORTANTBITS (0)
43 // dynamic texture (treat texnum == 0 differently)
44 #define GLTEXF_DYNAMIC 0x00080000
46 typedef struct textypeinfo_s
49 int inputbytesperpixel;
50 int internalbytesperpixel;
51 float glinternalbytesperpixel;
59 static textypeinfo_t textype_palette = {TEXTYPE_PALETTE , 1, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
60 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE , 1, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
61 static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, 3 , GL_RGBA , GL_UNSIGNED_BYTE };
62 static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, 4 , GL_RGBA , GL_UNSIGNED_BYTE };
63 static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
64 static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA , GL_UNSIGNED_BYTE };
65 static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
66 static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
67 static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
68 static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_BGRA , GL_UNSIGNED_BYTE };
69 static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
70 static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
71 static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , GL_ALPHA , GL_UNSIGNED_BYTE };
72 static textypeinfo_t textype_dxt1 = {TEXTYPE_DXT1 , 4, 0, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , 0 , 0 };
73 static textypeinfo_t textype_dxt1a = {TEXTYPE_DXT1A , 4, 0, 0.5f, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0 , 0 };
74 static textypeinfo_t textype_dxt3 = {TEXTYPE_DXT3 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0 , 0 };
75 static textypeinfo_t textype_dxt5 = {TEXTYPE_DXT5 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0 , 0 };
76 static textypeinfo_t textype_colorbuffer = {TEXTYPE_COLORBUFFER, 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
79 typedef enum gltexturetype_e
83 GLTEXTURETYPE_CUBEMAP,
84 GLTEXTURETYPE_RECTANGLE,
89 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
90 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
91 static int cubemapside[6] =
93 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
94 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
95 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
96 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
97 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
98 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
101 typedef struct gltexture_s
103 // this portion of the struct is exposed to the R_GetTexture macro for
104 // speed reasons, must be identical in rtexture_t!
105 int texnum; // GL texture slot number
106 qboolean dirty; // indicates that R_RealGetTexture should be called
107 int gltexturetypeenum; // used by R_Mesh_TexBind
109 // dynamic texture stuff [11/22/2007 Black]
110 updatecallback_t updatecallback;
111 void *updatacallback_data;
112 // --- [11/22/2007 Black]
114 // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
115 unsigned char *bufferpixels;
116 qboolean buffermodified;
118 // pointer to texturepool (check this to see if the texture is allocated)
119 struct gltexturepool_s *pool;
120 // pointer to next texture in texturepool chain
121 struct gltexture_s *chain;
122 // name of the texture (this might be removed someday), no duplicates
123 char identifier[MAX_QPATH + 32];
124 // original data size in *inputtexels
125 int inputwidth, inputheight, inputdepth;
126 // copy of the original texture(s) supplied to the upload function, for
127 // delayed uploads (non-precached)
128 unsigned char *inputtexels;
129 // original data size in *inputtexels
131 // flags supplied to the LoadTexture function
132 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
136 // pointer to one of the textype_ structs
137 textypeinfo_t *textype;
138 // one of the GLTEXTURETYPE_ values
140 // palette if the texture is TEXTYPE_PALETTE
141 const unsigned int *palette;
142 // actual stored texture size after gl_picmip and gl_max_size are applied
143 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
144 int tilewidth, tileheight, tiledepth;
145 // 1 or 6 depending on texturetype
149 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
152 int glinternalformat;
153 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
158 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
160 typedef struct gltexturepool_s
162 unsigned int sentinel;
163 struct gltexture_s *gltchain;
164 struct gltexturepool_s *next;
168 static gltexturepool_t *gltexturepoolchain = NULL;
170 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
171 static int resizebuffersize = 0;
172 static const unsigned char *texturebuffer;
174 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
179 return &textype_dxt1;
181 return &textype_dxt1a;
183 return &textype_dxt3;
185 return &textype_dxt5;
186 case TEXTYPE_PALETTE:
187 return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
189 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
190 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
191 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
193 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
194 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
195 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
197 return &textype_alpha;
198 case TEXTYPE_SHADOWMAP:
199 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
200 case TEXTYPE_COLORBUFFER:
201 return &textype_colorbuffer;
203 Host_Error("R_GetTexTypeInfo: unknown texture format");
209 // dynamic texture code [11/22/2007 Black]
210 void R_MarkDirtyTexture(rtexture_t *rt) {
211 gltexture_t *glt = (gltexture_t*) rt;
216 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
217 if (glt->flags & GLTEXF_DYNAMIC)
219 // mark it as dirty, so R_RealGetTexture gets called
224 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
225 gltexture_t *glt = (gltexture_t*) rt;
230 glt->flags |= GLTEXF_DYNAMIC;
231 glt->updatecallback = updatecallback;
232 glt->updatacallback_data = data;
235 static void R_UpdateDynamicTexture(gltexture_t *glt) {
237 if( glt->updatecallback ) {
238 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
242 void R_PurgeTexture(rtexture_t *rt)
244 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
249 void R_FreeTexture(rtexture_t *rt)
251 gltexture_t *glt, **gltpointer;
253 glt = (gltexture_t *)rt;
255 Host_Error("R_FreeTexture: texture == NULL");
257 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
258 if (*gltpointer == glt)
259 *gltpointer = glt->chain;
261 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
266 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
269 if (glt->inputtexels)
270 Mem_Free(glt->inputtexels);
271 Mem_ExpandableArray_FreeRecord(&texturearray, glt);
274 rtexturepool_t *R_AllocTexturePool(void)
276 gltexturepool_t *pool;
277 if (texturemempool == NULL)
279 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
282 pool->next = gltexturepoolchain;
283 gltexturepoolchain = pool;
284 pool->sentinel = TEXTUREPOOL_SENTINEL;
285 return (rtexturepool_t *)pool;
288 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
290 gltexturepool_t *pool, **poolpointer;
291 if (rtexturepool == NULL)
293 if (*rtexturepool == NULL)
295 pool = (gltexturepool_t *)(*rtexturepool);
296 *rtexturepool = NULL;
297 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
298 Host_Error("R_FreeTexturePool: pool already freed");
299 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
300 if (*poolpointer == pool)
301 *poolpointer = pool->next;
303 Host_Error("R_FreeTexturePool: pool not linked");
304 while (pool->gltchain)
305 R_FreeTexture((rtexture_t *)pool->gltchain);
310 typedef struct glmode_s
313 int minification, magnification;
317 static glmode_t modes[6] =
319 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
320 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
321 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
322 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
323 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
324 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
327 static void GL_TextureMode_f (void)
332 gltexturepool_t *pool;
336 Con_Printf("Texture mode is %sforced\n", gl_filter_force ? "" : "not ");
337 for (i = 0;i < 6;i++)
339 if (gl_filter_min == modes[i].minification)
341 Con_Printf("%s\n", modes[i].name);
345 Con_Print("current filter is unknown???\n");
349 for (i = 0;i < (int)(sizeof(modes)/sizeof(*modes));i++)
350 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
354 Con_Print("bad filter name\n");
358 gl_filter_min = modes[i].minification;
359 gl_filter_mag = modes[i].magnification;
360 gl_filter_force = ((Cmd_Argc() > 2) && !strcasecmp(Cmd_Argv(2), "force"));
362 // change all the existing mipmap texture objects
363 // FIXME: force renderer(/client/something?) restart instead?
366 for (pool = gltexturepoolchain;pool;pool = pool->next)
368 for (glt = pool->gltchain;glt;glt = glt->chain)
370 // only update already uploaded images
371 if (glt->texnum && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
373 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
374 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
375 if (glt->flags & TEXF_MIPMAP)
377 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
381 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
383 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
384 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
390 static void GL_Texture_CalcImageSize(int texturetype, int flags, int miplevel, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
392 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
397 case GLTEXTURETYPE_2D:
398 maxsize = vid.maxtexturesize_2d;
399 if (flags & TEXF_PICMIP)
401 maxsize = bound(1, gl_max_size.integer, maxsize);
405 case GLTEXTURETYPE_3D:
406 maxsize = vid.maxtexturesize_3d;
408 case GLTEXTURETYPE_CUBEMAP:
409 maxsize = vid.maxtexturesize_cubemap;
415 if (vid.support.arb_texture_non_power_of_two)
416 width2 = min(inwidth >> picmip, maxsize);
419 for (width2 = 1;width2 < inwidth;width2 <<= 1);
420 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
422 *outwidth = max(1, width2);
426 if (vid.support.arb_texture_non_power_of_two)
427 height2 = min(inheight >> picmip, maxsize);
430 for (height2 = 1;height2 < inheight;height2 <<= 1);
431 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
433 *outheight = max(1, height2);
437 if (vid.support.arb_texture_non_power_of_two)
438 depth2 = min(indepth >> picmip, maxsize);
441 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
442 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
444 *outdepth = max(1, depth2);
449 static int R_CalcTexelDataSize (gltexture_t *glt)
451 int width2, height2, depth2, size;
453 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
455 size = width2 * height2 * depth2;
457 if (glt->flags & TEXF_MIPMAP)
459 while (width2 > 1 || height2 > 1 || depth2 > 1)
467 size += width2 * height2 * depth2;
471 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
474 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
478 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
479 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
481 gltexturepool_t *pool;
483 Con_Print("glsize input loaded mip alpha name\n");
484 for (pool = gltexturepoolchain;pool;pool = pool->next)
492 for (glt = pool->gltchain;glt;glt = glt->chain)
494 glsize = R_CalcTexelDataSize(glt);
495 isloaded = glt->texnum != 0;
497 pooltotalt += glsize;
498 pooltotalp += glt->inputdatasize;
502 poolloadedt += glsize;
503 poolloadedp += glt->inputdatasize;
506 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);
509 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);
510 sumtotal += pooltotal;
511 sumtotalt += pooltotalt;
512 sumtotalp += pooltotalp;
513 sumloaded += poolloaded;
514 sumloadedt += poolloadedt;
515 sumloadedp += poolloadedp;
518 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);
521 static void R_TextureStats_f(void)
523 R_TextureStats_Print(true, true, true);
526 static void r_textures_start(void)
528 // LordHavoc: allow any alignment
530 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
531 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
533 texturemempool = Mem_AllocPool("texture management", 0, NULL);
534 Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
536 // Disable JPEG screenshots if the DLL isn't loaded
537 if (! JPEG_OpenLibrary ())
538 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
539 if (! PNG_OpenLibrary ())
540 Cvar_SetValueQuick (&scr_screenshot_png, 0);
543 static void r_textures_shutdown(void)
545 rtexturepool_t *temp;
547 JPEG_CloseLibrary ();
549 while(gltexturepoolchain)
551 temp = (rtexturepool_t *) gltexturepoolchain;
552 R_FreeTexturePool(&temp);
555 resizebuffersize = 0;
557 colorconvertbuffer = NULL;
558 texturebuffer = NULL;
559 Mem_ExpandableArray_FreeArray(&texturearray);
560 Mem_FreePool(&texturemempool);
563 static void r_textures_newmap(void)
567 void R_Textures_Init (void)
569 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc); an additional argument 'force' forces the texture mode even in cases where it may not be appropriate");
570 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
571 Cvar_RegisterVariable (&gl_max_size);
572 Cvar_RegisterVariable (&gl_picmip);
573 Cvar_RegisterVariable (&gl_picmip_world);
574 Cvar_RegisterVariable (&r_picmipworld);
575 Cvar_RegisterVariable (&gl_picmip_sprites);
576 Cvar_RegisterVariable (&r_picmipsprites);
577 Cvar_RegisterVariable (&gl_picmip_other);
578 Cvar_RegisterVariable (&gl_max_lightmapsize);
579 Cvar_RegisterVariable (&r_lerpimages);
580 Cvar_RegisterVariable (&gl_texture_anisotropy);
581 Cvar_RegisterVariable (&gl_texturecompression);
582 Cvar_RegisterVariable (&gl_texturecompression_color);
583 Cvar_RegisterVariable (&gl_texturecompression_normal);
584 Cvar_RegisterVariable (&gl_texturecompression_gloss);
585 Cvar_RegisterVariable (&gl_texturecompression_glow);
586 Cvar_RegisterVariable (&gl_texturecompression_2d);
587 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
588 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
589 Cvar_RegisterVariable (&gl_texturecompression_sky);
590 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
591 Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
592 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
593 Cvar_RegisterVariable (&r_texture_dds_load_dxt1_noalpha);
595 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap, NULL, NULL);
598 void R_Textures_Frame (void)
600 static int old_aniso = 0;
602 // could do procedural texture animation here, if we keep track of which
603 // textures were accessed this frame...
605 // free the resize buffers
606 resizebuffersize = 0;
609 Mem_Free(resizebuffer);
612 if (colorconvertbuffer)
614 Mem_Free(colorconvertbuffer);
615 colorconvertbuffer = NULL;
618 if (old_aniso != gl_texture_anisotropy.integer)
621 gltexturepool_t *pool;
624 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
626 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
630 for (pool = gltexturepoolchain;pool;pool = pool->next)
632 for (glt = pool->gltchain;glt;glt = glt->chain)
634 // only update already uploaded images
635 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
637 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
639 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
640 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
642 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
649 void R_MakeResizeBufferBigger(int size)
651 if (resizebuffersize < size)
653 resizebuffersize = size;
655 Mem_Free(resizebuffer);
656 if (colorconvertbuffer)
657 Mem_Free(colorconvertbuffer);
658 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
659 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
660 if (!resizebuffer || !colorconvertbuffer)
661 Host_Error("R_Upload: out of memory");
665 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
667 int textureenum = gltexturetypeenums[texturetype];
668 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
672 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
674 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
675 if (gl_texture_anisotropy.integer != aniso)
676 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
677 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
679 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
680 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
681 if (gltexturetypedimensions[texturetype] >= 3)
683 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
687 if (!gl_filter_force && flags & TEXF_FORCENEAREST)
689 if (flags & TEXF_MIPMAP)
691 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
695 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
697 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
699 else if (!gl_filter_force && flags & TEXF_FORCELINEAR)
701 if (flags & TEXF_MIPMAP)
703 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
705 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
709 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
714 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
716 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
720 if (flags & TEXF_MIPMAP)
722 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
726 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
728 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
731 if (textype == TEXTYPE_SHADOWMAP)
733 if (vid.support.arb_shadow)
735 if (flags & TEXF_COMPARE)
737 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
741 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
743 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
745 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
751 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
753 int i, mip, width, height, depth;
755 const unsigned char *prevbuffer;
760 // we need to restore the texture binding after finishing the upload
762 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
763 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
765 // these are rounded up versions of the size to do better resampling
766 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
768 width = glt->inputwidth;
769 height = glt->inputheight;
770 depth = glt->inputdepth;
774 for (width = 1;width < glt->inputwidth ;width <<= 1);
775 for (height = 1;height < glt->inputheight;height <<= 1);
776 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
779 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
780 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
782 if (prevbuffer == NULL)
784 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
785 prevbuffer = resizebuffer;
787 else if (glt->textype->textype == TEXTYPE_PALETTE)
789 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
790 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
791 prevbuffer = colorconvertbuffer;
794 // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
796 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))
798 // update a portion of the image
799 switch(glt->texturetype)
801 case GLTEXTURETYPE_2D:
802 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
804 case GLTEXTURETYPE_3D:
805 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
808 Host_Error("R_Upload: partial update of type other than 2D");
814 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
815 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
817 // cubemaps contain multiple images and thus get processed a bit differently
818 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
820 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
822 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
823 prevbuffer = resizebuffer;
826 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
828 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
829 prevbuffer = resizebuffer;
833 if (qglGetCompressedTexImageARB)
835 if (gl_texturecompression.integer >= 2)
836 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
838 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
841 switch(glt->texturetype)
843 case GLTEXTURETYPE_2D:
844 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
845 if (glt->flags & TEXF_MIPMAP)
847 while (width > 1 || height > 1 || depth > 1)
849 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
850 prevbuffer = resizebuffer;
851 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
855 case GLTEXTURETYPE_3D:
856 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
857 if (glt->flags & TEXF_MIPMAP)
859 while (width > 1 || height > 1 || depth > 1)
861 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
862 prevbuffer = resizebuffer;
863 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
867 case GLTEXTURETYPE_CUBEMAP:
868 // convert and upload each side in turn,
869 // from a continuous block of input texels
870 texturebuffer = (unsigned char *)prevbuffer;
871 for (i = 0;i < 6;i++)
873 prevbuffer = texturebuffer;
874 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
875 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
877 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
878 prevbuffer = resizebuffer;
881 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
883 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
884 prevbuffer = resizebuffer;
887 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 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 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
899 case GLTEXTURETYPE_RECTANGLE:
900 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
903 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
905 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
908 static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, int sides, int flags, int miplevel, textype_t textype, int texturetype, const unsigned char *data, const unsigned int *palette)
912 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
913 textypeinfo_t *texinfo, *texinfo2;
915 if (cls.state == ca_dedicated)
918 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
920 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
923 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
925 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
928 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
930 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
934 texinfo = R_GetTexTypeInfo(textype, flags);
935 size = width * height * depth * sides * texinfo->inputbytesperpixel;
938 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
942 // clear the alpha flag if the texture has no transparent pixels
945 case TEXTYPE_PALETTE:
946 if (flags & TEXF_ALPHA)
948 flags &= ~TEXF_ALPHA;
951 for (i = 0;i < size;i++)
953 if (((unsigned char *)&palette[data[i]])[3] < 255)
964 if (flags & TEXF_ALPHA)
966 flags &= ~TEXF_ALPHA;
969 for (i = 3;i < size;i += 4)
980 case TEXTYPE_SHADOWMAP:
992 case TEXTYPE_COLORBUFFER:
996 Host_Error("R_LoadTexture: unknown texture type");
999 texinfo2 = R_GetTexTypeInfo(textype, flags);
1000 if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
1003 Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
1005 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
1007 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1009 glt->chain = pool->gltchain;
1010 pool->gltchain = glt;
1011 glt->inputwidth = width;
1012 glt->inputheight = height;
1013 glt->inputdepth = depth;
1015 glt->miplevel = (miplevel < 0) ? R_PicmipForFlags(flags) : miplevel; // note: if miplevel is -1, we know the texture is in original size and we can picmip it normally
1016 glt->textype = texinfo;
1017 glt->texturetype = texturetype;
1018 glt->inputdatasize = size;
1019 glt->palette = palette;
1020 glt->glinternalformat = texinfo->glinternalformat;
1021 glt->glformat = texinfo->glformat;
1022 glt->gltype = texinfo->gltype;
1023 glt->bytesperpixel = texinfo->internalbytesperpixel;
1024 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1027 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1028 // init the dynamic texture attributes, too [11/22/2007 Black]
1029 glt->updatecallback = NULL;
1030 glt->updatacallback_data = NULL;
1032 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1034 // upload the texture
1035 // data may be NULL (blank texture for dynamic rendering)
1037 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1038 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1039 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1040 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1042 // texture converting and uploading can take a while, so make sure we're sending keepalives
1043 CL_KeepaliveMessage(false);
1045 return (rtexture_t *)glt;
1048 rtexture_t *R_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
1050 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, miplevel, textype, GLTEXTURETYPE_2D, data, palette);
1053 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, int miplevel, const unsigned int *palette)
1055 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, miplevel, textype, GLTEXTURETYPE_3D, data, palette);
1058 rtexture_t *R_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
1060 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, miplevel, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1063 rtexture_t *R_LoadTextureRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
1065 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, miplevel, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1068 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1070 int flags = TEXF_CLAMP;
1072 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1074 flags |= TEXF_FORCENEAREST;
1075 if (precision <= 16)
1076 flags |= TEXF_LOWPRECISION;
1080 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1082 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), -1, TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1085 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1087 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), -1, TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1090 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1092 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), -1, TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1095 int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
1097 gltexture_t *glt = (gltexture_t *)rt;
1100 int bytesperpixel = 0;
1101 int bytesperblock = 0;
1103 int dds_format_flags;
1111 GLint internalformat;
1112 const char *ddsfourcc;
1114 return -1; // NULL pointer
1115 if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
1116 return -2; // broken driver - crashes on reading internal format
1117 if (!qglGetTexLevelParameteriv)
1119 GL_ActiveTexture(0);
1120 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1121 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1122 qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
1123 switch(internalformat)
1125 default: ddsfourcc = NULL;bytesperpixel = 4;break;
1126 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1127 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
1128 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
1129 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
1131 if (!bytesperblock && skipuncompressed)
1132 return -3; // skipped
1133 memset(mipinfo, 0, sizeof(mipinfo));
1134 mipinfo[0][0] = glt->tilewidth;
1135 mipinfo[0][1] = glt->tileheight;
1137 if (glt->flags & TEXF_MIPMAP)
1139 for (mip = 1;mip < 16;mip++)
1141 mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
1142 mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
1143 if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
1151 for (mip = 0;mip < mipmaps;mip++)
1153 mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
1154 mipinfo[mip][3] = ddssize;
1155 ddssize += mipinfo[mip][2];
1157 dds = Mem_Alloc(tempmempool, ddssize);
1160 dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
1164 dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
1165 dds_format_flags = 0x4; // DDPF_FOURCC
1169 dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
1170 dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
1174 dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
1175 dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
1177 memcpy(dds, "DDS ", 4);
1178 StoreLittleLong(dds+4, ddssize);
1179 StoreLittleLong(dds+8, dds_flags);
1180 StoreLittleLong(dds+12, mipinfo[0][1]); // height
1181 StoreLittleLong(dds+16, mipinfo[0][0]); // width
1182 StoreLittleLong(dds+24, 1); // depth
1183 StoreLittleLong(dds+28, mipmaps); // mipmaps
1184 StoreLittleLong(dds+76, 32); // format size
1185 StoreLittleLong(dds+80, dds_format_flags);
1186 StoreLittleLong(dds+108, dds_caps1);
1187 StoreLittleLong(dds+112, dds_caps2);
1190 StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
1191 memcpy(dds+84, ddsfourcc, 4);
1192 for (mip = 0;mip < mipmaps;mip++)
1194 qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
1199 StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
1200 StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
1201 dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
1202 for (mip = 0;mip < mipmaps;mip++)
1204 qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
1207 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1208 ret = FS_WriteFile(filename, dds, ddssize);
1210 return ret ? ddssize : -5;
1213 rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor, int miplevel) // DDS textures are opaque, so miplevel isn't a pointer but just seen as a hint
1215 int i, size, dds_format_flags, dds_miplevels, dds_width, dds_height;
1218 int bytesperblock, bytesperpixel;
1221 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
1222 textypeinfo_t *texinfo;
1223 int mip, mipwidth, mipheight, mipsize;
1225 GLint oldbindtexnum;
1226 const unsigned char *mippixels, *ddspixels;
1228 fs_offset_t ddsfilesize;
1229 unsigned int ddssize;
1231 if (cls.state == ca_dedicated)
1234 dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
1235 ddssize = ddsfilesize;
1239 Log_Printf("ddstexturefailures.log", "%s\n", filename);
1240 return NULL; // not found
1243 if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
1246 Con_Printf("^1%s: not a DDS image\n", filename);
1250 //dds_flags = BuffLittleLong(dds+8);
1251 dds_format_flags = BuffLittleLong(dds+80);
1252 dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
1253 dds_width = BuffLittleLong(dds+16);
1254 dds_height = BuffLittleLong(dds+12);
1255 ddspixels = dds + 128;
1257 //flags &= ~TEXF_ALPHA; // disabled, as we DISABLE TEXF_ALPHA in the alpha detection, not enable it!
1258 if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
1260 // very sloppy BGRA 32bit identification
1261 textype = TEXTYPE_BGRA;
1264 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
1265 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1268 Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
1272 for (i = 3;i < size;i += 4)
1273 if (ddspixels[i] < 255)
1276 flags &= ~TEXF_ALPHA;
1278 else if (!memcmp(dds+84, "DXT1", 4))
1280 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1281 // LordHavoc: it is my belief that this does not infringe on the
1282 // patent because it is not decoding pixels...
1283 textype = TEXTYPE_DXT1;
1286 //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1287 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1288 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1291 Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
1294 if(r_texture_dds_load_dxt1_noalpha.integer)
1296 flags &= ~TEXF_ALPHA;
1300 for (i = 0;i < size;i += bytesperblock)
1301 if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1304 textype = TEXTYPE_DXT1A;
1306 flags &= ~TEXF_ALPHA;
1309 else if (!memcmp(dds+84, "DXT3", 4))
1311 textype = TEXTYPE_DXT3;
1314 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1315 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1318 Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
1322 else if (!memcmp(dds+84, "DXT5", 4))
1324 textype = TEXTYPE_DXT5;
1327 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1328 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1331 Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
1338 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1342 // return whether this texture is transparent
1344 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1346 // calculate average color if requested
1350 Vector4Clear(avgcolor);
1353 for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1355 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1356 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1357 avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
1358 avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
1360 f = (float)bytesperblock / size;
1361 avgcolor[0] *= (0.5f / 31.0f) * f;
1362 avgcolor[1] *= (0.5f / 63.0f) * f;
1363 avgcolor[2] *= (0.5f / 31.0f) * f;
1364 avgcolor[3] = 1; // too hard to calculate
1368 for (i = 0;i < size;i += 4)
1370 avgcolor[0] += ddspixels[i+2];
1371 avgcolor[1] += ddspixels[i+1];
1372 avgcolor[2] += ddspixels[i];
1373 avgcolor[3] += ddspixels[i+3];
1375 f = (1.0f / 255.0f) * bytesperpixel / size;
1383 // this is where we apply gl_picmip
1384 mippixels = ddspixels;
1385 mipwidth = dds_width;
1386 mipheight = dds_height;
1387 while(miplevel >= 1 && dds_miplevels >= 1)
1389 if (mipwidth <= 1 && mipheight <= 1)
1391 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1392 mippixels += mipsize; // just skip
1401 // when not requesting mipmaps, do not load them
1402 if(!(flags & TEXF_MIPMAP))
1405 if (dds_miplevels >= 1)
1406 flags |= TEXF_MIPMAP;
1408 flags &= ~TEXF_MIPMAP;
1410 // if S3TC is not supported, there's very little we can do about it
1411 if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1414 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1418 texinfo = R_GetTexTypeInfo(textype, flags);
1420 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
1421 strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1423 glt->chain = pool->gltchain;
1424 pool->gltchain = glt;
1425 glt->inputwidth = mipwidth;
1426 glt->inputheight = mipheight;
1427 glt->inputdepth = 1;
1429 glt->textype = texinfo;
1430 glt->texturetype = GLTEXTURETYPE_2D;
1431 glt->inputdatasize = ddssize;
1432 glt->glinternalformat = texinfo->glinternalformat;
1433 glt->glformat = texinfo->glformat;
1434 glt->gltype = texinfo->gltype;
1435 glt->bytesperpixel = texinfo->internalbytesperpixel;
1437 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1438 glt->tilewidth = mipwidth;
1439 glt->tileheight = mipheight;
1442 // texture uploading can take a while, so make sure we're sending keepalives
1443 CL_KeepaliveMessage(false);
1445 // upload the texture
1446 // we need to restore the texture binding after finishing the upload
1448 GL_ActiveTexture(0);
1449 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1450 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1451 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1452 mipcomplete = false;
1454 for (mip = 0;mip <= dds_miplevels;mip++) // <= to include the not-counted "largest" miplevel
1456 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1457 if (mippixels + mipsize > dds + ddssize)
1461 qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1465 qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1467 mippixels += mipsize;
1468 if (mipwidth <= 1 && mipheight <= 1)
1478 if (dds_miplevels >= 1 && !mipcomplete)
1480 // need to set GL_TEXTURE_MAX_LEVEL
1481 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1483 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1484 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1487 return (rtexture_t *)glt;
1490 int R_TextureWidth(rtexture_t *rt)
1492 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1495 int R_TextureHeight(rtexture_t *rt)
1497 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1500 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1502 gltexture_t *glt = (gltexture_t *)rt;
1504 Host_Error("R_UpdateTexture: no data supplied");
1506 Host_Error("R_UpdateTexture: no texture supplied");
1508 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1509 // update part of the texture
1510 if (glt->bufferpixels)
1513 int bpp = glt->bytesperpixel;
1514 int inputskip = width*bpp;
1515 int outputskip = glt->tilewidth*bpp;
1516 const unsigned char *input = data;
1517 unsigned char *output = glt->bufferpixels;
1527 input -= y*inputskip;
1530 if (width > glt->tilewidth - x)
1531 width = glt->tilewidth - x;
1532 if (height > glt->tileheight - y)
1533 height = glt->tileheight - y;
1534 if (width < 1 || height < 1)
1537 glt->buffermodified = true;
1538 output += y*outputskip + x*bpp;
1539 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1540 memcpy(output, input, width*bpp);
1543 R_Upload(glt, data, x, y, 0, width, height, 1);
1546 int R_RealGetTexture(rtexture_t *rt)
1551 glt = (gltexture_t *)rt;
1552 if (glt->flags & GLTEXF_DYNAMIC)
1553 R_UpdateDynamicTexture(glt);
1554 if (glt->buffermodified && glt->bufferpixels)
1556 glt->buffermodified = false;
1557 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1566 void R_ClearTexture (rtexture_t *rt)
1568 gltexture_t *glt = (gltexture_t *)rt;
1570 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );
1573 int R_PicmipForFlags(int flags)
1576 if(flags & TEXF_PICMIP)
1578 miplevel += gl_picmip.integer;
1579 if (flags & TEXF_ISWORLD)
1581 if (r_picmipworld.integer)
1582 miplevel += gl_picmip_world.integer;
1586 else if (flags & TEXF_ISSPRITE)
1588 if (r_picmipsprites.integer)
1589 miplevel += gl_picmip_sprites.integer;
1594 miplevel += gl_picmip_other.integer;
1596 return max(0, miplevel);