6 #include "intoverflow.h"
8 cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
9 cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
10 cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0", "reduces resolution of textures by powers of 2, for example 1 will halve width/height, reducing texture memory usage by 75%"};
11 cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1", "bilinear filters images when scaling them up to power of 2 size (mode 1), looks better than glquake (mode 0)"};
12 cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
13 cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
14 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
15 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
16 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
17 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
18 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
19 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
20 cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
21 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
22 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
23 cvar_t gl_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)"};
24 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"};
26 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
27 int gl_filter_mag = GL_LINEAR;
30 static mempool_t *texturemempool;
31 static memexpandablearray_t texturearray;
33 // note: this must not conflict with TEXF_ flags in r_textures.h
34 // bitmask for mismatch checking
35 #define GLTEXF_IMPORTANTBITS (0)
36 // dynamic texture (treat texnum == 0 differently)
37 #define GLTEXF_DYNAMIC 0x00080000
39 typedef struct textypeinfo_s
42 int inputbytesperpixel;
43 int internalbytesperpixel;
44 float glinternalbytesperpixel;
52 static textypeinfo_t textype_palette = {TEXTYPE_PALETTE , 1, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
53 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE , 1, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
54 static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, 3 , GL_RGBA , GL_UNSIGNED_BYTE };
55 static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, 4 , GL_RGBA , GL_UNSIGNED_BYTE };
56 static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
57 static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA , GL_UNSIGNED_BYTE };
58 static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
59 static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
60 static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
61 static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_BGRA , GL_UNSIGNED_BYTE };
62 static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
63 static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
64 static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , GL_ALPHA , GL_UNSIGNED_BYTE };
65 static textypeinfo_t textype_dxt1 = {TEXTYPE_DXT1 , 4, 0, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , 0 , 0 };
66 static textypeinfo_t textype_dxt1a = {TEXTYPE_DXT1A , 4, 0, 0.5f, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0 , 0 };
67 static textypeinfo_t textype_dxt3 = {TEXTYPE_DXT3 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0 , 0 };
68 static textypeinfo_t textype_dxt5 = {TEXTYPE_DXT5 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0 , 0 };
69 static textypeinfo_t textype_colorbuffer = {TEXTYPE_COLORBUFFER, 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
72 typedef enum gltexturetype_e
76 GLTEXTURETYPE_CUBEMAP,
77 GLTEXTURETYPE_RECTANGLE,
82 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
83 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
84 static int cubemapside[6] =
86 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
87 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
88 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
89 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
90 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
91 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
94 typedef struct gltexture_s
96 // this portion of the struct is exposed to the R_GetTexture macro for
97 // speed reasons, must be identical in rtexture_t!
98 int texnum; // GL texture slot number
99 qboolean dirty; // indicates that R_RealGetTexture should be called
100 int gltexturetypeenum; // used by R_Mesh_TexBind
102 // dynamic texture stuff [11/22/2007 Black]
103 updatecallback_t updatecallback;
104 void *updatacallback_data;
105 // --- [11/22/2007 Black]
107 // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
108 unsigned char *bufferpixels;
109 qboolean buffermodified;
111 // pointer to texturepool (check this to see if the texture is allocated)
112 struct gltexturepool_s *pool;
113 // pointer to next texture in texturepool chain
114 struct gltexture_s *chain;
115 // name of the texture (this might be removed someday), no duplicates
116 char identifier[MAX_QPATH + 32];
117 // original data size in *inputtexels
118 int inputwidth, inputheight, inputdepth;
119 // copy of the original texture(s) supplied to the upload function, for
120 // delayed uploads (non-precached)
121 unsigned char *inputtexels;
122 // original data size in *inputtexels
124 // flags supplied to the LoadTexture function
125 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
127 // pointer to one of the textype_ structs
128 textypeinfo_t *textype;
129 // one of the GLTEXTURETYPE_ values
131 // palette if the texture is TEXTYPE_PALETTE
132 const unsigned int *palette;
133 // actual stored texture size after gl_picmip and gl_max_size are applied
134 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
135 int tilewidth, tileheight, tiledepth;
136 // 1 or 6 depending on texturetype
140 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
143 int glinternalformat;
144 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
149 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
151 typedef struct gltexturepool_s
153 unsigned int sentinel;
154 struct gltexture_s *gltchain;
155 struct gltexturepool_s *next;
159 static gltexturepool_t *gltexturepoolchain = NULL;
161 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
162 static int resizebuffersize = 0;
163 static const unsigned char *texturebuffer;
164 static int texturebuffersize = 0;
166 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
171 return &textype_dxt1;
173 return &textype_dxt1a;
175 return &textype_dxt3;
177 return &textype_dxt5;
178 case TEXTYPE_PALETTE:
179 return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
181 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
182 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
184 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
187 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
188 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
190 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
193 return &textype_alpha;
194 case TEXTYPE_SHADOWMAP:
195 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
196 case TEXTYPE_COLORBUFFER:
197 return &textype_colorbuffer;
199 Host_Error("R_GetTexTypeInfo: unknown texture format");
202 return NULL; // this line only to hush compiler warnings
205 // dynamic texture code [11/22/2007 Black]
206 void R_MarkDirtyTexture(rtexture_t *rt) {
207 gltexture_t *glt = (gltexture_t*) rt;
212 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
213 if (glt->flags & GLTEXF_DYNAMIC)
215 // mark it as dirty, so R_RealGetTexture gets called
220 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
221 gltexture_t *glt = (gltexture_t*) rt;
226 glt->flags |= GLTEXF_DYNAMIC;
227 glt->updatecallback = updatecallback;
228 glt->updatacallback_data = data;
231 static void R_UpdateDynamicTexture(gltexture_t *glt) {
233 if( glt->updatecallback ) {
234 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
238 void R_PurgeTexture(rtexture_t *rt)
240 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
245 void R_FreeTexture(rtexture_t *rt)
247 gltexture_t *glt, **gltpointer;
249 glt = (gltexture_t *)rt;
251 Host_Error("R_FreeTexture: texture == NULL");
253 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
254 if (*gltpointer == glt)
255 *gltpointer = glt->chain;
257 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
262 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
265 if (glt->inputtexels)
266 Mem_Free(glt->inputtexels);
267 Mem_ExpandableArray_FreeRecord(&texturearray, glt);
270 rtexturepool_t *R_AllocTexturePool(void)
272 gltexturepool_t *pool;
273 if (texturemempool == NULL)
275 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
278 pool->next = gltexturepoolchain;
279 gltexturepoolchain = pool;
280 pool->sentinel = TEXTUREPOOL_SENTINEL;
281 return (rtexturepool_t *)pool;
284 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
286 gltexturepool_t *pool, **poolpointer;
287 if (rtexturepool == NULL)
289 if (*rtexturepool == NULL)
291 pool = (gltexturepool_t *)(*rtexturepool);
292 *rtexturepool = NULL;
293 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
294 Host_Error("R_FreeTexturePool: pool already freed");
295 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
296 if (*poolpointer == pool)
297 *poolpointer = pool->next;
299 Host_Error("R_FreeTexturePool: pool not linked");
300 while (pool->gltchain)
301 R_FreeTexture((rtexture_t *)pool->gltchain);
306 typedef struct glmode_s
309 int minification, magnification;
313 static glmode_t modes[6] =
315 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
316 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
317 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
318 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
319 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
320 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
323 static void GL_TextureMode_f (void)
328 gltexturepool_t *pool;
332 for (i = 0;i < 6;i++)
334 if (gl_filter_min == modes[i].minification)
336 Con_Printf("%s\n", modes[i].name);
340 Con_Print("current filter is unknown???\n");
344 for (i = 0;i < 6;i++)
345 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
349 Con_Print("bad filter name\n");
353 gl_filter_min = modes[i].minification;
354 gl_filter_mag = modes[i].magnification;
356 // change all the existing mipmap texture objects
357 // FIXME: force renderer(/client/something?) restart instead?
360 for (pool = gltexturepoolchain;pool;pool = pool->next)
362 for (glt = pool->gltchain;glt;glt = glt->chain)
364 // only update already uploaded images
365 if (glt->texnum && !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
367 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
368 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
369 if (glt->flags & TEXF_MIPMAP)
371 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
375 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
377 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
378 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
384 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
386 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
391 case GLTEXTURETYPE_2D:
392 maxsize = vid.maxtexturesize_2d;
393 if (flags & TEXF_PICMIP)
395 maxsize = bound(1, gl_max_size.integer, maxsize);
396 picmip = gl_picmip.integer;
399 case GLTEXTURETYPE_3D:
400 maxsize = vid.maxtexturesize_3d;
402 case GLTEXTURETYPE_CUBEMAP:
403 maxsize = vid.maxtexturesize_cubemap;
409 if (vid.support.arb_texture_non_power_of_two)
410 width2 = min(inwidth >> picmip, maxsize);
413 for (width2 = 1;width2 < inwidth;width2 <<= 1);
414 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
416 *outwidth = max(1, width2);
420 if (vid.support.arb_texture_non_power_of_two)
421 height2 = min(inheight >> picmip, maxsize);
424 for (height2 = 1;height2 < inheight;height2 <<= 1);
425 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
427 *outheight = max(1, height2);
431 if (vid.support.arb_texture_non_power_of_two)
432 depth2 = min(indepth >> picmip, maxsize);
435 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
436 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
438 *outdepth = max(1, depth2);
443 static int R_CalcTexelDataSize (gltexture_t *glt)
445 int width2, height2, depth2, size;
447 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
449 size = width2 * height2 * depth2;
451 if (glt->flags & TEXF_MIPMAP)
453 while (width2 > 1 || height2 > 1 || depth2 > 1)
461 size += width2 * height2 * depth2;
465 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
468 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
472 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
473 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
475 gltexturepool_t *pool;
477 Con_Print("glsize input loaded mip alpha name\n");
478 for (pool = gltexturepoolchain;pool;pool = pool->next)
486 for (glt = pool->gltchain;glt;glt = glt->chain)
488 glsize = R_CalcTexelDataSize(glt);
489 isloaded = glt->texnum != 0;
491 pooltotalt += glsize;
492 pooltotalp += glt->inputdatasize;
496 poolloadedt += glsize;
497 poolloadedp += glt->inputdatasize;
500 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);
503 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);
504 sumtotal += pooltotal;
505 sumtotalt += pooltotalt;
506 sumtotalp += pooltotalp;
507 sumloaded += poolloaded;
508 sumloadedt += poolloadedt;
509 sumloadedp += poolloadedp;
512 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);
515 static void R_TextureStats_f(void)
517 R_TextureStats_Print(true, true, true);
520 static void r_textures_start(void)
522 // LordHavoc: allow any alignment
524 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
525 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
527 texturemempool = Mem_AllocPool("texture management", 0, NULL);
528 Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
530 // Disable JPEG screenshots if the DLL isn't loaded
531 if (! JPEG_OpenLibrary ())
532 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
533 // TODO: support png screenshots?
537 static void r_textures_shutdown(void)
539 rtexturepool_t *temp;
541 JPEG_CloseLibrary ();
543 while(gltexturepoolchain)
545 temp = (rtexturepool_t *) gltexturepoolchain;
546 R_FreeTexturePool(&temp);
549 resizebuffersize = 0;
550 texturebuffersize = 0;
552 colorconvertbuffer = NULL;
553 texturebuffer = NULL;
554 Mem_ExpandableArray_FreeArray(&texturearray);
555 Mem_FreePool(&texturemempool);
558 static void r_textures_newmap(void)
562 void R_Textures_Init (void)
564 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
565 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
566 Cvar_RegisterVariable (&gl_max_size);
567 Cvar_RegisterVariable (&gl_picmip);
568 Cvar_RegisterVariable (&gl_max_lightmapsize);
569 Cvar_RegisterVariable (&r_lerpimages);
570 Cvar_RegisterVariable (&gl_texture_anisotropy);
571 Cvar_RegisterVariable (&gl_texturecompression);
572 Cvar_RegisterVariable (&gl_texturecompression_color);
573 Cvar_RegisterVariable (&gl_texturecompression_normal);
574 Cvar_RegisterVariable (&gl_texturecompression_gloss);
575 Cvar_RegisterVariable (&gl_texturecompression_glow);
576 Cvar_RegisterVariable (&gl_texturecompression_2d);
577 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
578 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
579 Cvar_RegisterVariable (&gl_texturecompression_sky);
580 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
581 Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
582 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
584 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
587 void R_Textures_Frame (void)
589 static int old_aniso = 0;
591 // could do procedural texture animation here, if we keep track of which
592 // textures were accessed this frame...
594 // free the resize buffers
595 resizebuffersize = 0;
598 Mem_Free(resizebuffer);
601 if (colorconvertbuffer)
603 Mem_Free(colorconvertbuffer);
604 colorconvertbuffer = NULL;
607 if (old_aniso != gl_texture_anisotropy.integer)
610 gltexturepool_t *pool;
613 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
615 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
619 for (pool = gltexturepoolchain;pool;pool = pool->next)
621 for (glt = pool->gltchain;glt;glt = glt->chain)
623 // only update already uploaded images
624 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
626 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
628 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
629 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
631 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
638 void R_MakeResizeBufferBigger(int size)
640 if (resizebuffersize < size)
642 resizebuffersize = size;
644 Mem_Free(resizebuffer);
645 if (colorconvertbuffer)
646 Mem_Free(colorconvertbuffer);
647 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
648 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
649 if (!resizebuffer || !colorconvertbuffer)
650 Host_Error("R_Upload: out of memory");
654 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
656 int textureenum = gltexturetypeenums[texturetype];
657 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
661 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
663 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
664 if (gl_texture_anisotropy.integer != aniso)
665 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
666 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
668 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
669 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
670 if (gltexturetypedimensions[texturetype] >= 3)
672 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
676 if (flags & TEXF_FORCENEAREST)
678 if (flags & TEXF_MIPMAP)
680 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
684 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
686 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
688 else if (flags & TEXF_FORCELINEAR)
690 if (flags & TEXF_MIPMAP)
692 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
694 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
698 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
703 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
705 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
709 if (flags & TEXF_MIPMAP)
711 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
715 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
717 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
720 if (textype == TEXTYPE_SHADOWMAP)
722 if (vid.support.arb_shadow)
724 if (flags & TEXF_COMPARE)
726 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
730 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
732 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
734 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
740 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
742 int i, mip, width, height, depth;
744 const unsigned char *prevbuffer;
749 // we need to restore the texture binding after finishing the upload
751 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
752 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
754 // these are rounded up versions of the size to do better resampling
755 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
757 width = glt->inputwidth;
758 height = glt->inputheight;
759 depth = glt->inputdepth;
763 for (width = 1;width < glt->inputwidth ;width <<= 1);
764 for (height = 1;height < glt->inputheight;height <<= 1);
765 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
768 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
769 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
771 if (prevbuffer == NULL)
773 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
774 prevbuffer = resizebuffer;
776 else if (glt->textype->textype == TEXTYPE_PALETTE)
778 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
779 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
780 prevbuffer = colorconvertbuffer;
783 // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
785 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))
787 // update a portion of the image
788 switch(glt->texturetype)
790 case GLTEXTURETYPE_2D:
791 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
793 case GLTEXTURETYPE_3D:
794 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
797 Host_Error("R_Upload: partial update of type other than 2D");
803 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
804 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
806 // cubemaps contain multiple images and thus get processed a bit differently
807 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
809 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
811 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
812 prevbuffer = resizebuffer;
815 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
817 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
818 prevbuffer = resizebuffer;
822 if (qglGetCompressedTexImageARB)
824 if (gl_texturecompression.integer >= 2)
825 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
827 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
830 switch(glt->texturetype)
832 case GLTEXTURETYPE_2D:
833 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
834 if (glt->flags & TEXF_MIPMAP)
836 while (width > 1 || height > 1 || depth > 1)
838 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
839 prevbuffer = resizebuffer;
840 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
844 case GLTEXTURETYPE_3D:
845 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
846 if (glt->flags & TEXF_MIPMAP)
848 while (width > 1 || height > 1 || depth > 1)
850 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
851 prevbuffer = resizebuffer;
852 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
856 case GLTEXTURETYPE_CUBEMAP:
857 // convert and upload each side in turn,
858 // from a continuous block of input texels
859 texturebuffer = (unsigned char *)prevbuffer;
860 for (i = 0;i < 6;i++)
862 prevbuffer = texturebuffer;
863 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
864 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
866 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
867 prevbuffer = resizebuffer;
870 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
872 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
873 prevbuffer = resizebuffer;
876 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
877 if (glt->flags & TEXF_MIPMAP)
879 while (width > 1 || height > 1 || depth > 1)
881 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
882 prevbuffer = resizebuffer;
883 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
888 case GLTEXTURETYPE_RECTANGLE:
889 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
892 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
894 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
897 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)
901 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
902 textypeinfo_t *texinfo, *texinfo2;
904 if (cls.state == ca_dedicated)
907 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
909 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
912 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
914 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
917 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
919 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
923 texinfo = R_GetTexTypeInfo(textype, flags);
924 size = width * height * depth * sides * texinfo->inputbytesperpixel;
927 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
931 // clear the alpha flag if the texture has no transparent pixels
934 case TEXTYPE_PALETTE:
935 if (flags & TEXF_ALPHA)
937 flags &= ~TEXF_ALPHA;
940 for (i = 0;i < size;i++)
942 if (((unsigned char *)&palette[data[i]])[3] < 255)
953 if (flags & TEXF_ALPHA)
955 flags &= ~TEXF_ALPHA;
958 for (i = 3;i < size;i += 4)
969 case TEXTYPE_SHADOWMAP:
981 case TEXTYPE_COLORBUFFER:
985 Host_Error("R_LoadTexture: unknown texture type");
988 texinfo2 = R_GetTexTypeInfo(textype, flags);
989 if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
992 Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
994 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
996 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
998 glt->chain = pool->gltchain;
999 pool->gltchain = glt;
1000 glt->inputwidth = width;
1001 glt->inputheight = height;
1002 glt->inputdepth = depth;
1004 glt->textype = texinfo;
1005 glt->texturetype = texturetype;
1006 glt->inputdatasize = size;
1007 glt->palette = palette;
1008 glt->glinternalformat = texinfo->glinternalformat;
1009 glt->glformat = texinfo->glformat;
1010 glt->gltype = texinfo->gltype;
1011 glt->bytesperpixel = texinfo->internalbytesperpixel;
1012 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1015 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1016 // init the dynamic texture attributes, too [11/22/2007 Black]
1017 glt->updatecallback = NULL;
1018 glt->updatacallback_data = NULL;
1020 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1022 // upload the texture
1023 // data may be NULL (blank texture for dynamic rendering)
1025 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1026 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1027 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1028 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1030 // texture converting and uploading can take a while, so make sure we're sending keepalives
1031 CL_KeepaliveMessage(false);
1033 return (rtexture_t *)glt;
1036 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)
1038 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1041 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)
1043 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1046 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)
1048 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1051 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)
1053 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1056 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1058 int flags = TEXF_CLAMP;
1060 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1062 flags |= TEXF_FORCENEAREST;
1063 if (precision <= 16)
1064 flags |= TEXF_LOWPRECISION;
1068 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1070 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1073 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1075 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1078 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1080 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1083 int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
1085 gltexture_t *glt = (gltexture_t *)rt;
1088 int bytesperpixel = 0;
1089 int bytesperblock = 0;
1091 int dds_format_flags;
1099 GLint internalformat;
1100 const char *ddsfourcc;
1102 return -1; // NULL pointer
1103 if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
1104 return -2; // broken driver - crashes on reading internal format
1105 if (!qglGetTexLevelParameteriv)
1107 GL_ActiveTexture(0);
1108 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1109 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1110 qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
1111 switch(internalformat)
1113 default: ddsfourcc = NULL;bytesperpixel = 4;break;
1114 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1115 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
1116 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
1117 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
1119 if (!bytesperblock && skipuncompressed)
1120 return -3; // skipped
1121 memset(mipinfo, 0, sizeof(mipinfo));
1122 mipinfo[0][0] = glt->tilewidth;
1123 mipinfo[0][1] = glt->tileheight;
1125 if (glt->flags & TEXF_MIPMAP)
1127 for (mip = 1;mip < 16;mip++)
1129 mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
1130 mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
1131 if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
1139 for (mip = 0;mip < mipmaps;mip++)
1141 mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
1142 mipinfo[mip][3] = ddssize;
1143 ddssize += mipinfo[mip][2];
1145 dds = Mem_Alloc(tempmempool, ddssize);
1148 dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
1152 dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
1153 dds_format_flags = 0x4; // DDPF_FOURCC
1157 dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
1158 dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
1162 dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
1163 dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
1165 memcpy(dds, "DDS ", 4);
1166 StoreLittleLong(dds+4, ddssize);
1167 StoreLittleLong(dds+8, dds_flags);
1168 StoreLittleLong(dds+12, mipinfo[0][1]); // height
1169 StoreLittleLong(dds+16, mipinfo[0][0]); // width
1170 StoreLittleLong(dds+24, 1); // depth
1171 StoreLittleLong(dds+28, mipmaps); // mipmaps
1172 StoreLittleLong(dds+76, 32); // format size
1173 StoreLittleLong(dds+80, dds_format_flags);
1174 StoreLittleLong(dds+108, dds_caps1);
1175 StoreLittleLong(dds+112, dds_caps2);
1178 StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
1179 memcpy(dds+84, ddsfourcc, 4);
1180 for (mip = 0;mip < mipmaps;mip++)
1182 qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
1187 StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
1188 StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
1189 dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
1190 for (mip = 0;mip < mipmaps;mip++)
1192 qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
1195 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1196 ret = FS_WriteFile(filename, dds, ddssize);
1198 return ret ? ddssize : -5;
1201 rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
1203 int i, size, dds_flags, dds_format_flags, dds_miplevels, dds_width, dds_height, textype;
1204 int bytesperblock, bytesperpixel;
1207 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
1208 textypeinfo_t *texinfo;
1209 int mip, mipwidth, mipheight, mipsize;
1211 GLint oldbindtexnum;
1212 const unsigned char *mippixels, *ddspixels;
1214 fs_offset_t ddsfilesize;
1215 unsigned int ddssize;
1217 if (cls.state == ca_dedicated)
1220 dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
1221 ddssize = ddsfilesize;
1225 Log_Printf("ddstexturefailures.log", "%s\n", filename);
1226 return NULL; // not found
1229 if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
1232 Con_Printf("^1%s: not a DDS image\n", filename);
1236 dds_flags = BuffLittleLong(dds+8);
1237 dds_format_flags = BuffLittleLong(dds+80);
1238 dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
1239 dds_width = BuffLittleLong(dds+16);
1240 dds_height = BuffLittleLong(dds+12);
1241 ddspixels = dds + 128;
1243 flags &= ~TEXF_ALPHA;
1244 if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
1246 // very sloppy BGRA 32bit identification
1247 textype = TEXTYPE_BGRA;
1250 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
1251 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1254 Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
1258 for (i = 3;i < size;i += 4)
1259 if (ddspixels[i] < 255)
1262 flags &= ~TEXF_ALPHA;
1264 else if (!memcmp(dds+84, "DXT1", 4))
1266 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1267 // LordHavoc: it is my belief that this does not infringe on the
1268 // patent because it is not decoding pixels...
1269 textype = TEXTYPE_DXT1;
1272 //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1273 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1274 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1277 Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
1280 for (i = 0;i < size;i += bytesperblock)
1281 if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1284 textype = TEXTYPE_DXT1A;
1286 flags &= ~TEXF_ALPHA;
1288 else if (!memcmp(dds+84, "DXT3", 4))
1290 textype = TEXTYPE_DXT3;
1293 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1294 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1297 Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
1301 else if (!memcmp(dds+84, "DXT5", 4))
1303 textype = TEXTYPE_DXT5;
1306 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1307 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1310 Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
1317 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1321 // return whether this texture is transparent
1323 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1325 // calculate average color if requested
1329 Vector4Clear(avgcolor);
1332 for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1334 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1335 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1336 avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
1337 avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
1339 f = (float)bytesperblock / size;
1340 avgcolor[0] *= (0.5f / 31.0f) * f;
1341 avgcolor[1] *= (0.5f / 63.0f) * f;
1342 avgcolor[2] *= (0.5f / 31.0f) * f;
1343 avgcolor[3] = 1; // too hard to calculate
1347 for (i = 0;i < size;i += 4)
1349 avgcolor[0] += ddspixels[i+2];
1350 avgcolor[1] += ddspixels[i+1];
1351 avgcolor[2] += ddspixels[i];
1352 avgcolor[3] += ddspixels[i+3];
1354 f = (1.0f / 255.0f) * bytesperpixel / size;
1362 if (dds_miplevels > 1)
1363 flags |= TEXF_MIPMAP;
1365 flags &= ~TEXF_MIPMAP;
1367 // if S3TC is not supported, there's very little we can do about it
1368 if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1371 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1375 texinfo = R_GetTexTypeInfo(textype, flags);
1377 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
1378 strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1380 glt->chain = pool->gltchain;
1381 pool->gltchain = glt;
1382 glt->inputwidth = dds_width;
1383 glt->inputheight = dds_height;
1384 glt->inputdepth = 1;
1386 glt->textype = texinfo;
1387 glt->texturetype = GLTEXTURETYPE_2D;
1388 glt->inputdatasize = ddssize;
1389 glt->glinternalformat = texinfo->glinternalformat;
1390 glt->glformat = texinfo->glformat;
1391 glt->gltype = texinfo->gltype;
1392 glt->bytesperpixel = texinfo->internalbytesperpixel;
1394 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1395 glt->tilewidth = dds_width;
1396 glt->tileheight = dds_height;
1399 // texture uploading can take a while, so make sure we're sending keepalives
1400 CL_KeepaliveMessage(false);
1402 // upload the texture
1403 // we need to restore the texture binding after finishing the upload
1405 GL_ActiveTexture(0);
1406 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1407 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1408 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1409 mippixels = ddspixels;
1410 mipwidth = dds_width;
1411 mipheight = dds_height;
1412 mipcomplete = false;
1413 for (mip = 0;mip < dds_miplevels+1;mip++)
1415 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1416 if (mippixels + mipsize > dds + ddssize)
1420 qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1424 qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1426 mippixels += mipsize;
1427 if (mipwidth <= 1 && mipheight <= 1)
1437 if (dds_miplevels > 1 && !mipcomplete)
1439 // need to set GL_TEXTURE_MAX_LEVEL
1440 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1442 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1443 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1446 return (rtexture_t *)glt;
1449 int R_TextureWidth(rtexture_t *rt)
1451 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1454 int R_TextureHeight(rtexture_t *rt)
1456 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1459 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1461 gltexture_t *glt = (gltexture_t *)rt;
1463 Host_Error("R_UpdateTexture: no data supplied");
1465 Host_Error("R_UpdateTexture: no texture supplied");
1467 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1468 // update part of the texture
1469 if (glt->bufferpixels)
1472 int bpp = glt->bytesperpixel;
1473 int inputskip = width*bpp;
1474 int outputskip = glt->tilewidth*bpp;
1475 const unsigned char *input = data;
1476 unsigned char *output = glt->bufferpixels;
1486 input -= y*inputskip;
1489 if (width > glt->tilewidth - x)
1490 width = glt->tilewidth - x;
1491 if (height > glt->tileheight - y)
1492 height = glt->tileheight - y;
1493 if (width < 1 || height < 1)
1496 glt->buffermodified = true;
1497 output += y*outputskip + x*bpp;
1498 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1499 memcpy(output, input, width*bpp);
1502 R_Upload(glt, data, x, y, 0, width, height, 1);
1505 int R_RealGetTexture(rtexture_t *rt)
1510 glt = (gltexture_t *)rt;
1511 if (glt->flags & GLTEXF_DYNAMIC)
1512 R_UpdateDynamicTexture(glt);
1513 if (glt->buffermodified && glt->bufferpixels)
1515 glt->buffermodified = false;
1516 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1525 void R_ClearTexture (rtexture_t *rt)
1527 gltexture_t *glt = (gltexture_t *)rt;
1529 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );