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"};
31 qboolean gl_filter_force = false;
32 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
33 int gl_filter_mag = GL_LINEAR;
36 static mempool_t *texturemempool;
37 static memexpandablearray_t texturearray;
39 // note: this must not conflict with TEXF_ flags in r_textures.h
40 // bitmask for mismatch checking
41 #define GLTEXF_IMPORTANTBITS (0)
42 // dynamic texture (treat texnum == 0 differently)
43 #define GLTEXF_DYNAMIC 0x00080000
45 typedef struct textypeinfo_s
48 int inputbytesperpixel;
49 int internalbytesperpixel;
50 float glinternalbytesperpixel;
58 static textypeinfo_t textype_palette = {TEXTYPE_PALETTE , 1, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
59 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE , 1, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
60 static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, 3 , GL_RGBA , GL_UNSIGNED_BYTE };
61 static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, 4 , GL_RGBA , GL_UNSIGNED_BYTE };
62 static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
63 static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA , GL_UNSIGNED_BYTE };
64 static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
65 static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
66 static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
67 static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_BGRA , GL_UNSIGNED_BYTE };
68 static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
69 static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
70 static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , GL_ALPHA , GL_UNSIGNED_BYTE };
71 static textypeinfo_t textype_dxt1 = {TEXTYPE_DXT1 , 4, 0, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , 0 , 0 };
72 static textypeinfo_t textype_dxt1a = {TEXTYPE_DXT1A , 4, 0, 0.5f, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0 , 0 };
73 static textypeinfo_t textype_dxt3 = {TEXTYPE_DXT3 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0 , 0 };
74 static textypeinfo_t textype_dxt5 = {TEXTYPE_DXT5 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0 , 0 };
75 static textypeinfo_t textype_colorbuffer = {TEXTYPE_COLORBUFFER, 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
78 typedef enum gltexturetype_e
82 GLTEXTURETYPE_CUBEMAP,
83 GLTEXTURETYPE_RECTANGLE,
88 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
89 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
90 static int cubemapside[6] =
92 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
93 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
94 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
95 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
96 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
97 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
100 typedef struct gltexture_s
102 // this portion of the struct is exposed to the R_GetTexture macro for
103 // speed reasons, must be identical in rtexture_t!
104 int texnum; // GL texture slot number
105 qboolean dirty; // indicates that R_RealGetTexture should be called
106 int gltexturetypeenum; // used by R_Mesh_TexBind
108 // dynamic texture stuff [11/22/2007 Black]
109 updatecallback_t updatecallback;
110 void *updatacallback_data;
111 // --- [11/22/2007 Black]
113 // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
114 unsigned char *bufferpixels;
115 qboolean buffermodified;
117 // pointer to texturepool (check this to see if the texture is allocated)
118 struct gltexturepool_s *pool;
119 // pointer to next texture in texturepool chain
120 struct gltexture_s *chain;
121 // name of the texture (this might be removed someday), no duplicates
122 char identifier[MAX_QPATH + 32];
123 // original data size in *inputtexels
124 int inputwidth, inputheight, inputdepth;
125 // copy of the original texture(s) supplied to the upload function, for
126 // delayed uploads (non-precached)
127 unsigned char *inputtexels;
128 // original data size in *inputtexels
130 // flags supplied to the LoadTexture function
131 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
133 // pointer to one of the textype_ structs
134 textypeinfo_t *textype;
135 // one of the GLTEXTURETYPE_ values
137 // palette if the texture is TEXTYPE_PALETTE
138 const unsigned int *palette;
139 // actual stored texture size after gl_picmip and gl_max_size are applied
140 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
141 int tilewidth, tileheight, tiledepth;
142 // 1 or 6 depending on texturetype
146 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
149 int glinternalformat;
150 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
155 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
157 typedef struct gltexturepool_s
159 unsigned int sentinel;
160 struct gltexture_s *gltchain;
161 struct gltexturepool_s *next;
165 static gltexturepool_t *gltexturepoolchain = NULL;
167 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
168 static int resizebuffersize = 0;
169 static const unsigned char *texturebuffer;
171 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
176 return &textype_dxt1;
178 return &textype_dxt1a;
180 return &textype_dxt3;
182 return &textype_dxt5;
183 case TEXTYPE_PALETTE:
184 return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
186 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
187 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
188 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
190 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
191 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
192 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
194 return &textype_alpha;
195 case TEXTYPE_SHADOWMAP:
196 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
197 case TEXTYPE_COLORBUFFER:
198 return &textype_colorbuffer;
200 Host_Error("R_GetTexTypeInfo: unknown texture format");
206 // dynamic texture code [11/22/2007 Black]
207 void R_MarkDirtyTexture(rtexture_t *rt) {
208 gltexture_t *glt = (gltexture_t*) rt;
213 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
214 if (glt->flags & GLTEXF_DYNAMIC)
216 // mark it as dirty, so R_RealGetTexture gets called
221 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
222 gltexture_t *glt = (gltexture_t*) rt;
227 glt->flags |= GLTEXF_DYNAMIC;
228 glt->updatecallback = updatecallback;
229 glt->updatacallback_data = data;
232 static void R_UpdateDynamicTexture(gltexture_t *glt) {
234 if( glt->updatecallback ) {
235 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
239 void R_PurgeTexture(rtexture_t *rt)
241 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
246 void R_FreeTexture(rtexture_t *rt)
248 gltexture_t *glt, **gltpointer;
250 glt = (gltexture_t *)rt;
252 Host_Error("R_FreeTexture: texture == NULL");
254 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
255 if (*gltpointer == glt)
256 *gltpointer = glt->chain;
258 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
263 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
266 if (glt->inputtexels)
267 Mem_Free(glt->inputtexels);
268 Mem_ExpandableArray_FreeRecord(&texturearray, glt);
271 rtexturepool_t *R_AllocTexturePool(void)
273 gltexturepool_t *pool;
274 if (texturemempool == NULL)
276 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
279 pool->next = gltexturepoolchain;
280 gltexturepoolchain = pool;
281 pool->sentinel = TEXTUREPOOL_SENTINEL;
282 return (rtexturepool_t *)pool;
285 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
287 gltexturepool_t *pool, **poolpointer;
288 if (rtexturepool == NULL)
290 if (*rtexturepool == NULL)
292 pool = (gltexturepool_t *)(*rtexturepool);
293 *rtexturepool = NULL;
294 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
295 Host_Error("R_FreeTexturePool: pool already freed");
296 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
297 if (*poolpointer == pool)
298 *poolpointer = pool->next;
300 Host_Error("R_FreeTexturePool: pool not linked");
301 while (pool->gltchain)
302 R_FreeTexture((rtexture_t *)pool->gltchain);
307 typedef struct glmode_s
310 int minification, magnification;
314 static glmode_t modes[6] =
316 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
317 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
318 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
319 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
320 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
321 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
324 static void GL_TextureMode_f (void)
329 gltexturepool_t *pool;
333 Con_Printf("Texture mode is %sforced\n", gl_filter_force ? "" : "not ");
334 for (i = 0;i < 6;i++)
336 if (gl_filter_min == modes[i].minification)
338 Con_Printf("%s\n", modes[i].name);
342 Con_Print("current filter is unknown???\n");
346 for (i = 0;i < (int)(sizeof(modes)/sizeof(*modes));i++)
347 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
351 Con_Print("bad filter name\n");
355 gl_filter_min = modes[i].minification;
356 gl_filter_mag = modes[i].magnification;
357 gl_filter_force = ((Cmd_Argc() > 2) && !strcasecmp(Cmd_Argv(2), "force"));
359 // change all the existing mipmap texture objects
360 // FIXME: force renderer(/client/something?) restart instead?
363 for (pool = gltexturepoolchain;pool;pool = pool->next)
365 for (glt = pool->gltchain;glt;glt = glt->chain)
367 // only update already uploaded images
368 if (glt->texnum && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
370 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
371 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
372 if (glt->flags & TEXF_MIPMAP)
374 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
378 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
380 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
381 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
387 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
389 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
394 case GLTEXTURETYPE_2D:
395 maxsize = vid.maxtexturesize_2d;
396 if (flags & TEXF_PICMIP)
398 maxsize = bound(1, gl_max_size.integer, maxsize);
399 picmip = gl_picmip.integer;
400 if (flags & TEXF_ISWORLD)
402 if (r_picmipworld.integer)
403 picmip += gl_picmip_world.integer;
408 picmip += gl_picmip_other.integer;
409 picmip = bound(0, picmip, 31); // can't do more than 31 or >> operator gets funny
412 case GLTEXTURETYPE_3D:
413 maxsize = vid.maxtexturesize_3d;
415 case GLTEXTURETYPE_CUBEMAP:
416 maxsize = vid.maxtexturesize_cubemap;
422 if (vid.support.arb_texture_non_power_of_two)
423 width2 = min(inwidth >> picmip, maxsize);
426 for (width2 = 1;width2 < inwidth;width2 <<= 1);
427 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
429 *outwidth = max(1, width2);
433 if (vid.support.arb_texture_non_power_of_two)
434 height2 = min(inheight >> picmip, maxsize);
437 for (height2 = 1;height2 < inheight;height2 <<= 1);
438 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
440 *outheight = max(1, height2);
444 if (vid.support.arb_texture_non_power_of_two)
445 depth2 = min(indepth >> picmip, maxsize);
448 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
449 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
451 *outdepth = max(1, depth2);
456 static int R_CalcTexelDataSize (gltexture_t *glt)
458 int width2, height2, depth2, size;
460 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
462 size = width2 * height2 * depth2;
464 if (glt->flags & TEXF_MIPMAP)
466 while (width2 > 1 || height2 > 1 || depth2 > 1)
474 size += width2 * height2 * depth2;
478 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
481 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
485 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
486 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
488 gltexturepool_t *pool;
490 Con_Print("glsize input loaded mip alpha name\n");
491 for (pool = gltexturepoolchain;pool;pool = pool->next)
499 for (glt = pool->gltchain;glt;glt = glt->chain)
501 glsize = R_CalcTexelDataSize(glt);
502 isloaded = glt->texnum != 0;
504 pooltotalt += glsize;
505 pooltotalp += glt->inputdatasize;
509 poolloadedt += glsize;
510 poolloadedp += glt->inputdatasize;
513 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);
516 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);
517 sumtotal += pooltotal;
518 sumtotalt += pooltotalt;
519 sumtotalp += pooltotalp;
520 sumloaded += poolloaded;
521 sumloadedt += poolloadedt;
522 sumloadedp += poolloadedp;
525 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);
528 static void R_TextureStats_f(void)
530 R_TextureStats_Print(true, true, true);
533 static void r_textures_start(void)
535 // LordHavoc: allow any alignment
537 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
538 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
540 texturemempool = Mem_AllocPool("texture management", 0, NULL);
541 Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
543 // Disable JPEG screenshots if the DLL isn't loaded
544 if (! JPEG_OpenLibrary ())
545 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
546 if (! PNG_OpenLibrary ())
547 Cvar_SetValueQuick (&scr_screenshot_png, 0);
550 static void r_textures_shutdown(void)
552 rtexturepool_t *temp;
554 JPEG_CloseLibrary ();
556 while(gltexturepoolchain)
558 temp = (rtexturepool_t *) gltexturepoolchain;
559 R_FreeTexturePool(&temp);
562 resizebuffersize = 0;
564 colorconvertbuffer = NULL;
565 texturebuffer = NULL;
566 Mem_ExpandableArray_FreeArray(&texturearray);
567 Mem_FreePool(&texturemempool);
570 static void r_textures_newmap(void)
574 void R_Textures_Init (void)
576 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");
577 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
578 Cvar_RegisterVariable (&gl_max_size);
579 Cvar_RegisterVariable (&gl_picmip);
580 Cvar_RegisterVariable (&gl_picmip_world);
581 Cvar_RegisterVariable (&r_picmipworld);
582 Cvar_RegisterVariable (&gl_picmip_sprites);
583 Cvar_RegisterVariable (&r_picmipsprites);
584 Cvar_RegisterVariable (&gl_picmip_other);
585 Cvar_RegisterVariable (&gl_max_lightmapsize);
586 Cvar_RegisterVariable (&r_lerpimages);
587 Cvar_RegisterVariable (&gl_texture_anisotropy);
588 Cvar_RegisterVariable (&gl_texturecompression);
589 Cvar_RegisterVariable (&gl_texturecompression_color);
590 Cvar_RegisterVariable (&gl_texturecompression_normal);
591 Cvar_RegisterVariable (&gl_texturecompression_gloss);
592 Cvar_RegisterVariable (&gl_texturecompression_glow);
593 Cvar_RegisterVariable (&gl_texturecompression_2d);
594 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
595 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
596 Cvar_RegisterVariable (&gl_texturecompression_sky);
597 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
598 Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
599 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
601 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
604 void R_Textures_Frame (void)
606 static int old_aniso = 0;
608 // could do procedural texture animation here, if we keep track of which
609 // textures were accessed this frame...
611 // free the resize buffers
612 resizebuffersize = 0;
615 Mem_Free(resizebuffer);
618 if (colorconvertbuffer)
620 Mem_Free(colorconvertbuffer);
621 colorconvertbuffer = NULL;
624 if (old_aniso != gl_texture_anisotropy.integer)
627 gltexturepool_t *pool;
630 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
632 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
636 for (pool = gltexturepoolchain;pool;pool = pool->next)
638 for (glt = pool->gltchain;glt;glt = glt->chain)
640 // only update already uploaded images
641 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
643 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
645 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
646 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
648 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
655 void R_MakeResizeBufferBigger(int size)
657 if (resizebuffersize < size)
659 resizebuffersize = size;
661 Mem_Free(resizebuffer);
662 if (colorconvertbuffer)
663 Mem_Free(colorconvertbuffer);
664 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
665 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
666 if (!resizebuffer || !colorconvertbuffer)
667 Host_Error("R_Upload: out of memory");
671 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
673 int textureenum = gltexturetypeenums[texturetype];
674 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
678 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
680 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
681 if (gl_texture_anisotropy.integer != aniso)
682 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
683 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
685 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
686 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
687 if (gltexturetypedimensions[texturetype] >= 3)
689 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
693 if (!gl_filter_force && flags & TEXF_FORCENEAREST)
695 if (flags & TEXF_MIPMAP)
697 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
701 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
703 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
705 else if (!gl_filter_force && flags & TEXF_FORCELINEAR)
707 if (flags & TEXF_MIPMAP)
709 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
711 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
715 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
720 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
722 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
726 if (flags & TEXF_MIPMAP)
728 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
732 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
734 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
737 if (textype == TEXTYPE_SHADOWMAP)
739 if (vid.support.arb_shadow)
741 if (flags & TEXF_COMPARE)
743 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
747 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
749 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
751 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
757 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
759 int i, mip, width, height, depth;
761 const unsigned char *prevbuffer;
766 // we need to restore the texture binding after finishing the upload
768 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
769 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
771 // these are rounded up versions of the size to do better resampling
772 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
774 width = glt->inputwidth;
775 height = glt->inputheight;
776 depth = glt->inputdepth;
780 for (width = 1;width < glt->inputwidth ;width <<= 1);
781 for (height = 1;height < glt->inputheight;height <<= 1);
782 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
785 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
786 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
788 if (prevbuffer == NULL)
790 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
791 prevbuffer = resizebuffer;
793 else if (glt->textype->textype == TEXTYPE_PALETTE)
795 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
796 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
797 prevbuffer = colorconvertbuffer;
800 // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
802 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))
804 // update a portion of the image
805 switch(glt->texturetype)
807 case GLTEXTURETYPE_2D:
808 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
810 case GLTEXTURETYPE_3D:
811 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
814 Host_Error("R_Upload: partial update of type other than 2D");
820 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
821 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
823 // cubemaps contain multiple images and thus get processed a bit differently
824 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
826 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
828 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
829 prevbuffer = resizebuffer;
832 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
834 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
835 prevbuffer = resizebuffer;
839 if (qglGetCompressedTexImageARB)
841 if (gl_texturecompression.integer >= 2)
842 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
844 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
847 switch(glt->texturetype)
849 case GLTEXTURETYPE_2D:
850 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
851 if (glt->flags & TEXF_MIPMAP)
853 while (width > 1 || height > 1 || depth > 1)
855 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
856 prevbuffer = resizebuffer;
857 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
861 case GLTEXTURETYPE_3D:
862 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
863 if (glt->flags & TEXF_MIPMAP)
865 while (width > 1 || height > 1 || depth > 1)
867 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
868 prevbuffer = resizebuffer;
869 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
873 case GLTEXTURETYPE_CUBEMAP:
874 // convert and upload each side in turn,
875 // from a continuous block of input texels
876 texturebuffer = (unsigned char *)prevbuffer;
877 for (i = 0;i < 6;i++)
879 prevbuffer = texturebuffer;
880 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
881 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
883 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
884 prevbuffer = resizebuffer;
887 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
889 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
890 prevbuffer = resizebuffer;
893 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
894 if (glt->flags & TEXF_MIPMAP)
896 while (width > 1 || height > 1 || depth > 1)
898 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
899 prevbuffer = resizebuffer;
900 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
905 case GLTEXTURETYPE_RECTANGLE:
906 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
909 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
911 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
914 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)
918 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
919 textypeinfo_t *texinfo, *texinfo2;
921 if (cls.state == ca_dedicated)
924 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
926 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
929 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
931 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
934 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
936 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
940 texinfo = R_GetTexTypeInfo(textype, flags);
941 size = width * height * depth * sides * texinfo->inputbytesperpixel;
944 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
948 // clear the alpha flag if the texture has no transparent pixels
951 case TEXTYPE_PALETTE:
952 if (flags & TEXF_ALPHA)
954 flags &= ~TEXF_ALPHA;
957 for (i = 0;i < size;i++)
959 if (((unsigned char *)&palette[data[i]])[3] < 255)
970 if (flags & TEXF_ALPHA)
972 flags &= ~TEXF_ALPHA;
975 for (i = 3;i < size;i += 4)
986 case TEXTYPE_SHADOWMAP:
998 case TEXTYPE_COLORBUFFER:
1002 Host_Error("R_LoadTexture: unknown texture type");
1005 texinfo2 = R_GetTexTypeInfo(textype, flags);
1006 if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
1009 Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
1011 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
1013 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1015 glt->chain = pool->gltchain;
1016 pool->gltchain = glt;
1017 glt->inputwidth = width;
1018 glt->inputheight = height;
1019 glt->inputdepth = depth;
1021 glt->textype = texinfo;
1022 glt->texturetype = texturetype;
1023 glt->inputdatasize = size;
1024 glt->palette = palette;
1025 glt->glinternalformat = texinfo->glinternalformat;
1026 glt->glformat = texinfo->glformat;
1027 glt->gltype = texinfo->gltype;
1028 glt->bytesperpixel = texinfo->internalbytesperpixel;
1029 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1032 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1033 // init the dynamic texture attributes, too [11/22/2007 Black]
1034 glt->updatecallback = NULL;
1035 glt->updatacallback_data = NULL;
1037 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1039 // upload the texture
1040 // data may be NULL (blank texture for dynamic rendering)
1042 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1043 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1044 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1045 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1047 // texture converting and uploading can take a while, so make sure we're sending keepalives
1048 CL_KeepaliveMessage(false);
1050 return (rtexture_t *)glt;
1053 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)
1055 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1058 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)
1060 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1063 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)
1065 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1068 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)
1070 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1073 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1075 int flags = TEXF_CLAMP;
1077 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1079 flags |= TEXF_FORCENEAREST;
1080 if (precision <= 16)
1081 flags |= TEXF_LOWPRECISION;
1085 rtexture_t *R_LoadTextureShadowMapRectangle(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), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1090 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1092 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1095 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1097 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1100 int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
1102 gltexture_t *glt = (gltexture_t *)rt;
1105 int bytesperpixel = 0;
1106 int bytesperblock = 0;
1108 int dds_format_flags;
1116 GLint internalformat;
1117 const char *ddsfourcc;
1119 return -1; // NULL pointer
1120 if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
1121 return -2; // broken driver - crashes on reading internal format
1122 if (!qglGetTexLevelParameteriv)
1124 GL_ActiveTexture(0);
1125 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1126 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1127 qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
1128 switch(internalformat)
1130 default: ddsfourcc = NULL;bytesperpixel = 4;break;
1131 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1132 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
1133 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
1134 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
1136 if (!bytesperblock && skipuncompressed)
1137 return -3; // skipped
1138 memset(mipinfo, 0, sizeof(mipinfo));
1139 mipinfo[0][0] = glt->tilewidth;
1140 mipinfo[0][1] = glt->tileheight;
1142 if (glt->flags & TEXF_MIPMAP)
1144 for (mip = 1;mip < 16;mip++)
1146 mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
1147 mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
1148 if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
1156 for (mip = 0;mip < mipmaps;mip++)
1158 mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
1159 mipinfo[mip][3] = ddssize;
1160 ddssize += mipinfo[mip][2];
1162 dds = Mem_Alloc(tempmempool, ddssize);
1165 dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
1169 dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
1170 dds_format_flags = 0x4; // DDPF_FOURCC
1174 dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
1175 dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
1179 dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
1180 dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
1182 memcpy(dds, "DDS ", 4);
1183 StoreLittleLong(dds+4, ddssize);
1184 StoreLittleLong(dds+8, dds_flags);
1185 StoreLittleLong(dds+12, mipinfo[0][1]); // height
1186 StoreLittleLong(dds+16, mipinfo[0][0]); // width
1187 StoreLittleLong(dds+24, 1); // depth
1188 StoreLittleLong(dds+28, mipmaps); // mipmaps
1189 StoreLittleLong(dds+76, 32); // format size
1190 StoreLittleLong(dds+80, dds_format_flags);
1191 StoreLittleLong(dds+108, dds_caps1);
1192 StoreLittleLong(dds+112, dds_caps2);
1195 StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
1196 memcpy(dds+84, ddsfourcc, 4);
1197 for (mip = 0;mip < mipmaps;mip++)
1199 qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
1204 StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
1205 StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
1206 dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
1207 for (mip = 0;mip < mipmaps;mip++)
1209 qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
1212 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1213 ret = FS_WriteFile(filename, dds, ddssize);
1215 return ret ? ddssize : -5;
1218 rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
1220 int i, size, dds_format_flags, dds_miplevels, dds_width, dds_height;
1223 int bytesperblock, bytesperpixel;
1226 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
1227 textypeinfo_t *texinfo;
1228 int mip, mipwidth, mipheight, mipsize;
1230 GLint oldbindtexnum;
1231 const unsigned char *mippixels, *ddspixels;
1233 fs_offset_t ddsfilesize;
1234 unsigned int ddssize;
1236 if (cls.state == ca_dedicated)
1239 dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
1240 ddssize = ddsfilesize;
1244 Log_Printf("ddstexturefailures.log", "%s\n", filename);
1245 return NULL; // not found
1248 if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
1251 Con_Printf("^1%s: not a DDS image\n", filename);
1255 //dds_flags = BuffLittleLong(dds+8);
1256 dds_format_flags = BuffLittleLong(dds+80);
1257 dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
1258 dds_width = BuffLittleLong(dds+16);
1259 dds_height = BuffLittleLong(dds+12);
1260 ddspixels = dds + 128;
1262 flags &= ~TEXF_ALPHA;
1263 if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
1265 // very sloppy BGRA 32bit identification
1266 textype = TEXTYPE_BGRA;
1269 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
1270 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1273 Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
1277 for (i = 3;i < size;i += 4)
1278 if (ddspixels[i] < 255)
1281 flags &= ~TEXF_ALPHA;
1283 else if (!memcmp(dds+84, "DXT1", 4))
1285 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1286 // LordHavoc: it is my belief that this does not infringe on the
1287 // patent because it is not decoding pixels...
1288 textype = TEXTYPE_DXT1;
1291 //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1292 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1293 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1296 Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
1299 for (i = 0;i < size;i += bytesperblock)
1300 if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1303 textype = TEXTYPE_DXT1A;
1305 flags &= ~TEXF_ALPHA;
1307 else if (!memcmp(dds+84, "DXT3", 4))
1309 textype = TEXTYPE_DXT3;
1312 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1313 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1316 Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
1320 else if (!memcmp(dds+84, "DXT5", 4))
1322 textype = TEXTYPE_DXT5;
1325 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1326 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1329 Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
1336 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1340 // return whether this texture is transparent
1342 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1344 // calculate average color if requested
1348 Vector4Clear(avgcolor);
1351 for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1353 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1354 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1355 avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
1356 avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
1358 f = (float)bytesperblock / size;
1359 avgcolor[0] *= (0.5f / 31.0f) * f;
1360 avgcolor[1] *= (0.5f / 63.0f) * f;
1361 avgcolor[2] *= (0.5f / 31.0f) * f;
1362 avgcolor[3] = 1; // too hard to calculate
1366 for (i = 0;i < size;i += 4)
1368 avgcolor[0] += ddspixels[i+2];
1369 avgcolor[1] += ddspixels[i+1];
1370 avgcolor[2] += ddspixels[i];
1371 avgcolor[3] += ddspixels[i+3];
1373 f = (1.0f / 255.0f) * bytesperpixel / size;
1381 if (dds_miplevels > 1)
1382 flags |= TEXF_MIPMAP;
1384 flags &= ~TEXF_MIPMAP;
1386 // if S3TC is not supported, there's very little we can do about it
1387 if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1390 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1394 texinfo = R_GetTexTypeInfo(textype, flags);
1396 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
1397 strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1399 glt->chain = pool->gltchain;
1400 pool->gltchain = glt;
1401 glt->inputwidth = dds_width;
1402 glt->inputheight = dds_height;
1403 glt->inputdepth = 1;
1405 glt->textype = texinfo;
1406 glt->texturetype = GLTEXTURETYPE_2D;
1407 glt->inputdatasize = ddssize;
1408 glt->glinternalformat = texinfo->glinternalformat;
1409 glt->glformat = texinfo->glformat;
1410 glt->gltype = texinfo->gltype;
1411 glt->bytesperpixel = texinfo->internalbytesperpixel;
1413 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1414 glt->tilewidth = dds_width;
1415 glt->tileheight = dds_height;
1418 // texture uploading can take a while, so make sure we're sending keepalives
1419 CL_KeepaliveMessage(false);
1421 // upload the texture
1422 // we need to restore the texture binding after finishing the upload
1424 GL_ActiveTexture(0);
1425 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1426 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1427 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1428 mippixels = ddspixels;
1429 mipwidth = dds_width;
1430 mipheight = dds_height;
1431 mipcomplete = false;
1432 for (mip = 0;mip < dds_miplevels+1;mip++)
1434 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1435 if (mippixels + mipsize > dds + ddssize)
1439 qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1443 qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1445 mippixels += mipsize;
1446 if (mipwidth <= 1 && mipheight <= 1)
1456 if (dds_miplevels > 1 && !mipcomplete)
1458 // need to set GL_TEXTURE_MAX_LEVEL
1459 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1461 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1462 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1465 return (rtexture_t *)glt;
1468 int R_TextureWidth(rtexture_t *rt)
1470 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1473 int R_TextureHeight(rtexture_t *rt)
1475 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1478 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1480 gltexture_t *glt = (gltexture_t *)rt;
1482 Host_Error("R_UpdateTexture: no data supplied");
1484 Host_Error("R_UpdateTexture: no texture supplied");
1486 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1487 // update part of the texture
1488 if (glt->bufferpixels)
1491 int bpp = glt->bytesperpixel;
1492 int inputskip = width*bpp;
1493 int outputskip = glt->tilewidth*bpp;
1494 const unsigned char *input = data;
1495 unsigned char *output = glt->bufferpixels;
1505 input -= y*inputskip;
1508 if (width > glt->tilewidth - x)
1509 width = glt->tilewidth - x;
1510 if (height > glt->tileheight - y)
1511 height = glt->tileheight - y;
1512 if (width < 1 || height < 1)
1515 glt->buffermodified = true;
1516 output += y*outputskip + x*bpp;
1517 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1518 memcpy(output, input, width*bpp);
1521 R_Upload(glt, data, x, y, 0, width, height, 1);
1524 int R_RealGetTexture(rtexture_t *rt)
1529 glt = (gltexture_t *)rt;
1530 if (glt->flags & GLTEXF_DYNAMIC)
1531 R_UpdateDynamicTexture(glt);
1532 if (glt->buffermodified && glt->bufferpixels)
1534 glt->buffermodified = false;
1535 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1544 void R_ClearTexture (rtexture_t *rt)
1546 gltexture_t *glt = (gltexture_t *)rt;
1548 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );