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;
165 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
170 return &textype_dxt1;
172 return &textype_dxt1a;
174 return &textype_dxt3;
176 return &textype_dxt5;
177 case TEXTYPE_PALETTE:
178 return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
180 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
181 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
182 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
184 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
185 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
186 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
188 return &textype_alpha;
189 case TEXTYPE_SHADOWMAP:
190 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
191 case TEXTYPE_COLORBUFFER:
192 return &textype_colorbuffer;
194 Host_Error("R_GetTexTypeInfo: unknown texture format");
200 // dynamic texture code [11/22/2007 Black]
201 void R_MarkDirtyTexture(rtexture_t *rt) {
202 gltexture_t *glt = (gltexture_t*) rt;
207 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
208 if (glt->flags & GLTEXF_DYNAMIC)
210 // mark it as dirty, so R_RealGetTexture gets called
215 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
216 gltexture_t *glt = (gltexture_t*) rt;
221 glt->flags |= GLTEXF_DYNAMIC;
222 glt->updatecallback = updatecallback;
223 glt->updatacallback_data = data;
226 static void R_UpdateDynamicTexture(gltexture_t *glt) {
228 if( glt->updatecallback ) {
229 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
233 void R_PurgeTexture(rtexture_t *rt)
235 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
240 void R_FreeTexture(rtexture_t *rt)
242 gltexture_t *glt, **gltpointer;
244 glt = (gltexture_t *)rt;
246 Host_Error("R_FreeTexture: texture == NULL");
248 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
249 if (*gltpointer == glt)
250 *gltpointer = glt->chain;
252 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
257 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
260 if (glt->inputtexels)
261 Mem_Free(glt->inputtexels);
262 Mem_ExpandableArray_FreeRecord(&texturearray, glt);
265 rtexturepool_t *R_AllocTexturePool(void)
267 gltexturepool_t *pool;
268 if (texturemempool == NULL)
270 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
273 pool->next = gltexturepoolchain;
274 gltexturepoolchain = pool;
275 pool->sentinel = TEXTUREPOOL_SENTINEL;
276 return (rtexturepool_t *)pool;
279 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
281 gltexturepool_t *pool, **poolpointer;
282 if (rtexturepool == NULL)
284 if (*rtexturepool == NULL)
286 pool = (gltexturepool_t *)(*rtexturepool);
287 *rtexturepool = NULL;
288 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
289 Host_Error("R_FreeTexturePool: pool already freed");
290 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
291 if (*poolpointer == pool)
292 *poolpointer = pool->next;
294 Host_Error("R_FreeTexturePool: pool not linked");
295 while (pool->gltchain)
296 R_FreeTexture((rtexture_t *)pool->gltchain);
301 typedef struct glmode_s
304 int minification, magnification;
308 static glmode_t modes[6] =
310 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
311 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
312 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
313 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
314 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
315 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
318 static void GL_TextureMode_f (void)
323 gltexturepool_t *pool;
327 for (i = 0;i < 6;i++)
329 if (gl_filter_min == modes[i].minification)
331 Con_Printf("%s\n", modes[i].name);
335 Con_Print("current filter is unknown???\n");
339 for (i = 0;i < 6;i++)
340 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
344 Con_Print("bad filter name\n");
348 gl_filter_min = modes[i].minification;
349 gl_filter_mag = modes[i].magnification;
351 // change all the existing mipmap texture objects
352 // FIXME: force renderer(/client/something?) restart instead?
355 for (pool = gltexturepoolchain;pool;pool = pool->next)
357 for (glt = pool->gltchain;glt;glt = glt->chain)
359 // only update already uploaded images
360 if (glt->texnum && !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
362 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
363 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
364 if (glt->flags & TEXF_MIPMAP)
366 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
370 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
372 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
373 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
379 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
381 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
386 case GLTEXTURETYPE_2D:
387 maxsize = vid.maxtexturesize_2d;
388 if (flags & TEXF_PICMIP)
390 maxsize = bound(1, gl_max_size.integer, maxsize);
391 picmip = gl_picmip.integer;
394 case GLTEXTURETYPE_3D:
395 maxsize = vid.maxtexturesize_3d;
397 case GLTEXTURETYPE_CUBEMAP:
398 maxsize = vid.maxtexturesize_cubemap;
404 if (vid.support.arb_texture_non_power_of_two)
405 width2 = min(inwidth >> picmip, maxsize);
408 for (width2 = 1;width2 < inwidth;width2 <<= 1);
409 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
411 *outwidth = max(1, width2);
415 if (vid.support.arb_texture_non_power_of_two)
416 height2 = min(inheight >> picmip, maxsize);
419 for (height2 = 1;height2 < inheight;height2 <<= 1);
420 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
422 *outheight = max(1, height2);
426 if (vid.support.arb_texture_non_power_of_two)
427 depth2 = min(indepth >> picmip, maxsize);
430 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
431 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
433 *outdepth = max(1, depth2);
438 static int R_CalcTexelDataSize (gltexture_t *glt)
440 int width2, height2, depth2, size;
442 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
444 size = width2 * height2 * depth2;
446 if (glt->flags & TEXF_MIPMAP)
448 while (width2 > 1 || height2 > 1 || depth2 > 1)
456 size += width2 * height2 * depth2;
460 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
463 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
467 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
468 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
470 gltexturepool_t *pool;
472 Con_Print("glsize input loaded mip alpha name\n");
473 for (pool = gltexturepoolchain;pool;pool = pool->next)
481 for (glt = pool->gltchain;glt;glt = glt->chain)
483 glsize = R_CalcTexelDataSize(glt);
484 isloaded = glt->texnum != 0;
486 pooltotalt += glsize;
487 pooltotalp += glt->inputdatasize;
491 poolloadedt += glsize;
492 poolloadedp += glt->inputdatasize;
495 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);
498 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);
499 sumtotal += pooltotal;
500 sumtotalt += pooltotalt;
501 sumtotalp += pooltotalp;
502 sumloaded += poolloaded;
503 sumloadedt += poolloadedt;
504 sumloadedp += poolloadedp;
507 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);
510 static void R_TextureStats_f(void)
512 R_TextureStats_Print(true, true, true);
515 static void r_textures_start(void)
517 // LordHavoc: allow any alignment
519 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
520 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
522 texturemempool = Mem_AllocPool("texture management", 0, NULL);
523 Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
525 // Disable JPEG screenshots if the DLL isn't loaded
526 if (! JPEG_OpenLibrary ())
527 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
528 // TODO: support png screenshots?
532 static void r_textures_shutdown(void)
534 rtexturepool_t *temp;
536 JPEG_CloseLibrary ();
538 while(gltexturepoolchain)
540 temp = (rtexturepool_t *) gltexturepoolchain;
541 R_FreeTexturePool(&temp);
544 resizebuffersize = 0;
546 colorconvertbuffer = NULL;
547 texturebuffer = NULL;
548 Mem_ExpandableArray_FreeArray(&texturearray);
549 Mem_FreePool(&texturemempool);
552 static void r_textures_newmap(void)
556 void R_Textures_Init (void)
558 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
559 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
560 Cvar_RegisterVariable (&gl_max_size);
561 Cvar_RegisterVariable (&gl_picmip);
562 Cvar_RegisterVariable (&gl_max_lightmapsize);
563 Cvar_RegisterVariable (&r_lerpimages);
564 Cvar_RegisterVariable (&gl_texture_anisotropy);
565 Cvar_RegisterVariable (&gl_texturecompression);
566 Cvar_RegisterVariable (&gl_texturecompression_color);
567 Cvar_RegisterVariable (&gl_texturecompression_normal);
568 Cvar_RegisterVariable (&gl_texturecompression_gloss);
569 Cvar_RegisterVariable (&gl_texturecompression_glow);
570 Cvar_RegisterVariable (&gl_texturecompression_2d);
571 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
572 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
573 Cvar_RegisterVariable (&gl_texturecompression_sky);
574 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
575 Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
576 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
578 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
581 void R_Textures_Frame (void)
583 static int old_aniso = 0;
585 // could do procedural texture animation here, if we keep track of which
586 // textures were accessed this frame...
588 // free the resize buffers
589 resizebuffersize = 0;
592 Mem_Free(resizebuffer);
595 if (colorconvertbuffer)
597 Mem_Free(colorconvertbuffer);
598 colorconvertbuffer = NULL;
601 if (old_aniso != gl_texture_anisotropy.integer)
604 gltexturepool_t *pool;
607 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
609 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
613 for (pool = gltexturepoolchain;pool;pool = pool->next)
615 for (glt = pool->gltchain;glt;glt = glt->chain)
617 // only update already uploaded images
618 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
620 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
622 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
623 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
625 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
632 void R_MakeResizeBufferBigger(int size)
634 if (resizebuffersize < size)
636 resizebuffersize = size;
638 Mem_Free(resizebuffer);
639 if (colorconvertbuffer)
640 Mem_Free(colorconvertbuffer);
641 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
642 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
643 if (!resizebuffer || !colorconvertbuffer)
644 Host_Error("R_Upload: out of memory");
648 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
650 int textureenum = gltexturetypeenums[texturetype];
651 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
655 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
657 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
658 if (gl_texture_anisotropy.integer != aniso)
659 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
660 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
662 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
663 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
664 if (gltexturetypedimensions[texturetype] >= 3)
666 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
670 if (flags & TEXF_FORCENEAREST)
672 if (flags & TEXF_MIPMAP)
674 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
678 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
680 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
682 else if (flags & TEXF_FORCELINEAR)
684 if (flags & TEXF_MIPMAP)
686 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
688 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
692 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
697 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
699 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
703 if (flags & TEXF_MIPMAP)
705 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
709 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
711 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
714 if (textype == TEXTYPE_SHADOWMAP)
716 if (vid.support.arb_shadow)
718 if (flags & TEXF_COMPARE)
720 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
724 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
726 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
728 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
734 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
736 int i, mip, width, height, depth;
738 const unsigned char *prevbuffer;
743 // we need to restore the texture binding after finishing the upload
745 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
746 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
748 // these are rounded up versions of the size to do better resampling
749 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
751 width = glt->inputwidth;
752 height = glt->inputheight;
753 depth = glt->inputdepth;
757 for (width = 1;width < glt->inputwidth ;width <<= 1);
758 for (height = 1;height < glt->inputheight;height <<= 1);
759 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
762 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
763 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
765 if (prevbuffer == NULL)
767 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
768 prevbuffer = resizebuffer;
770 else if (glt->textype->textype == TEXTYPE_PALETTE)
772 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
773 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
774 prevbuffer = colorconvertbuffer;
777 // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
779 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))
781 // update a portion of the image
782 switch(glt->texturetype)
784 case GLTEXTURETYPE_2D:
785 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
787 case GLTEXTURETYPE_3D:
788 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
791 Host_Error("R_Upload: partial update of type other than 2D");
797 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
798 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
800 // cubemaps contain multiple images and thus get processed a bit differently
801 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
803 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
805 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
806 prevbuffer = resizebuffer;
809 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
811 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
812 prevbuffer = resizebuffer;
816 if (qglGetCompressedTexImageARB)
818 if (gl_texturecompression.integer >= 2)
819 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
821 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
824 switch(glt->texturetype)
826 case GLTEXTURETYPE_2D:
827 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
828 if (glt->flags & TEXF_MIPMAP)
830 while (width > 1 || height > 1 || depth > 1)
832 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
833 prevbuffer = resizebuffer;
834 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
838 case GLTEXTURETYPE_3D:
839 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
840 if (glt->flags & TEXF_MIPMAP)
842 while (width > 1 || height > 1 || depth > 1)
844 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
845 prevbuffer = resizebuffer;
846 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
850 case GLTEXTURETYPE_CUBEMAP:
851 // convert and upload each side in turn,
852 // from a continuous block of input texels
853 texturebuffer = (unsigned char *)prevbuffer;
854 for (i = 0;i < 6;i++)
856 prevbuffer = texturebuffer;
857 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
858 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
860 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
861 prevbuffer = resizebuffer;
864 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
866 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
867 prevbuffer = resizebuffer;
870 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
871 if (glt->flags & TEXF_MIPMAP)
873 while (width > 1 || height > 1 || depth > 1)
875 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
876 prevbuffer = resizebuffer;
877 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
882 case GLTEXTURETYPE_RECTANGLE:
883 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
886 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
888 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
891 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)
895 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
896 textypeinfo_t *texinfo, *texinfo2;
898 if (cls.state == ca_dedicated)
901 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
903 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
906 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
908 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
911 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
913 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
917 texinfo = R_GetTexTypeInfo(textype, flags);
918 size = width * height * depth * sides * texinfo->inputbytesperpixel;
921 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
925 // clear the alpha flag if the texture has no transparent pixels
928 case TEXTYPE_PALETTE:
929 if (flags & TEXF_ALPHA)
931 flags &= ~TEXF_ALPHA;
934 for (i = 0;i < size;i++)
936 if (((unsigned char *)&palette[data[i]])[3] < 255)
947 if (flags & TEXF_ALPHA)
949 flags &= ~TEXF_ALPHA;
952 for (i = 3;i < size;i += 4)
963 case TEXTYPE_SHADOWMAP:
975 case TEXTYPE_COLORBUFFER:
979 Host_Error("R_LoadTexture: unknown texture type");
982 texinfo2 = R_GetTexTypeInfo(textype, flags);
983 if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
986 Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
988 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
990 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
992 glt->chain = pool->gltchain;
993 pool->gltchain = glt;
994 glt->inputwidth = width;
995 glt->inputheight = height;
996 glt->inputdepth = depth;
998 glt->textype = texinfo;
999 glt->texturetype = texturetype;
1000 glt->inputdatasize = size;
1001 glt->palette = palette;
1002 glt->glinternalformat = texinfo->glinternalformat;
1003 glt->glformat = texinfo->glformat;
1004 glt->gltype = texinfo->gltype;
1005 glt->bytesperpixel = texinfo->internalbytesperpixel;
1006 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1009 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1010 // init the dynamic texture attributes, too [11/22/2007 Black]
1011 glt->updatecallback = NULL;
1012 glt->updatacallback_data = NULL;
1014 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1016 // upload the texture
1017 // data may be NULL (blank texture for dynamic rendering)
1019 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1020 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1021 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1022 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1024 // texture converting and uploading can take a while, so make sure we're sending keepalives
1025 CL_KeepaliveMessage(false);
1027 return (rtexture_t *)glt;
1030 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)
1032 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1035 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)
1037 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1040 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)
1042 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1045 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)
1047 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1050 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1052 int flags = TEXF_CLAMP;
1054 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1056 flags |= TEXF_FORCENEAREST;
1057 if (precision <= 16)
1058 flags |= TEXF_LOWPRECISION;
1062 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1064 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1067 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1069 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1072 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1074 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1077 int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
1079 gltexture_t *glt = (gltexture_t *)rt;
1082 int bytesperpixel = 0;
1083 int bytesperblock = 0;
1085 int dds_format_flags;
1093 GLint internalformat;
1094 const char *ddsfourcc;
1096 return -1; // NULL pointer
1097 if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
1098 return -2; // broken driver - crashes on reading internal format
1099 if (!qglGetTexLevelParameteriv)
1101 GL_ActiveTexture(0);
1102 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1103 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1104 qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
1105 switch(internalformat)
1107 default: ddsfourcc = NULL;bytesperpixel = 4;break;
1108 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1109 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
1110 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
1111 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
1113 if (!bytesperblock && skipuncompressed)
1114 return -3; // skipped
1115 memset(mipinfo, 0, sizeof(mipinfo));
1116 mipinfo[0][0] = glt->tilewidth;
1117 mipinfo[0][1] = glt->tileheight;
1119 if (glt->flags & TEXF_MIPMAP)
1121 for (mip = 1;mip < 16;mip++)
1123 mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
1124 mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
1125 if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
1133 for (mip = 0;mip < mipmaps;mip++)
1135 mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
1136 mipinfo[mip][3] = ddssize;
1137 ddssize += mipinfo[mip][2];
1139 dds = Mem_Alloc(tempmempool, ddssize);
1142 dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
1146 dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
1147 dds_format_flags = 0x4; // DDPF_FOURCC
1151 dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
1152 dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
1156 dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
1157 dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
1159 memcpy(dds, "DDS ", 4);
1160 StoreLittleLong(dds+4, ddssize);
1161 StoreLittleLong(dds+8, dds_flags);
1162 StoreLittleLong(dds+12, mipinfo[0][1]); // height
1163 StoreLittleLong(dds+16, mipinfo[0][0]); // width
1164 StoreLittleLong(dds+24, 1); // depth
1165 StoreLittleLong(dds+28, mipmaps); // mipmaps
1166 StoreLittleLong(dds+76, 32); // format size
1167 StoreLittleLong(dds+80, dds_format_flags);
1168 StoreLittleLong(dds+108, dds_caps1);
1169 StoreLittleLong(dds+112, dds_caps2);
1172 StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
1173 memcpy(dds+84, ddsfourcc, 4);
1174 for (mip = 0;mip < mipmaps;mip++)
1176 qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
1181 StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
1182 StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
1183 dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
1184 for (mip = 0;mip < mipmaps;mip++)
1186 qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
1189 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1190 ret = FS_WriteFile(filename, dds, ddssize);
1192 return ret ? ddssize : -5;
1195 rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
1197 int i, size, dds_format_flags, dds_miplevels, dds_width, dds_height;
1200 int bytesperblock, bytesperpixel;
1203 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
1204 textypeinfo_t *texinfo;
1205 int mip, mipwidth, mipheight, mipsize;
1207 GLint oldbindtexnum;
1208 const unsigned char *mippixels, *ddspixels;
1210 fs_offset_t ddsfilesize;
1211 unsigned int ddssize;
1213 if (cls.state == ca_dedicated)
1216 dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
1217 ddssize = ddsfilesize;
1221 Log_Printf("ddstexturefailures.log", "%s\n", filename);
1222 return NULL; // not found
1225 if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
1228 Con_Printf("^1%s: not a DDS image\n", filename);
1232 //dds_flags = BuffLittleLong(dds+8);
1233 dds_format_flags = BuffLittleLong(dds+80);
1234 dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
1235 dds_width = BuffLittleLong(dds+16);
1236 dds_height = BuffLittleLong(dds+12);
1237 ddspixels = dds + 128;
1239 flags &= ~TEXF_ALPHA;
1240 if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
1242 // very sloppy BGRA 32bit identification
1243 textype = TEXTYPE_BGRA;
1246 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
1247 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1250 Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
1254 for (i = 3;i < size;i += 4)
1255 if (ddspixels[i] < 255)
1258 flags &= ~TEXF_ALPHA;
1260 else if (!memcmp(dds+84, "DXT1", 4))
1262 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1263 // LordHavoc: it is my belief that this does not infringe on the
1264 // patent because it is not decoding pixels...
1265 textype = TEXTYPE_DXT1;
1268 //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1269 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1270 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1273 Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
1276 for (i = 0;i < size;i += bytesperblock)
1277 if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1280 textype = TEXTYPE_DXT1A;
1282 flags &= ~TEXF_ALPHA;
1284 else if (!memcmp(dds+84, "DXT3", 4))
1286 textype = TEXTYPE_DXT3;
1289 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1290 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1293 Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
1297 else if (!memcmp(dds+84, "DXT5", 4))
1299 textype = TEXTYPE_DXT5;
1302 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1303 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1306 Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
1313 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1317 // return whether this texture is transparent
1319 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1321 // calculate average color if requested
1325 Vector4Clear(avgcolor);
1328 for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1330 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1331 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1332 avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
1333 avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
1335 f = (float)bytesperblock / size;
1336 avgcolor[0] *= (0.5f / 31.0f) * f;
1337 avgcolor[1] *= (0.5f / 63.0f) * f;
1338 avgcolor[2] *= (0.5f / 31.0f) * f;
1339 avgcolor[3] = 1; // too hard to calculate
1343 for (i = 0;i < size;i += 4)
1345 avgcolor[0] += ddspixels[i+2];
1346 avgcolor[1] += ddspixels[i+1];
1347 avgcolor[2] += ddspixels[i];
1348 avgcolor[3] += ddspixels[i+3];
1350 f = (1.0f / 255.0f) * bytesperpixel / size;
1358 if (dds_miplevels > 1)
1359 flags |= TEXF_MIPMAP;
1361 flags &= ~TEXF_MIPMAP;
1363 // if S3TC is not supported, there's very little we can do about it
1364 if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1367 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1371 texinfo = R_GetTexTypeInfo(textype, flags);
1373 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
1374 strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1376 glt->chain = pool->gltchain;
1377 pool->gltchain = glt;
1378 glt->inputwidth = dds_width;
1379 glt->inputheight = dds_height;
1380 glt->inputdepth = 1;
1382 glt->textype = texinfo;
1383 glt->texturetype = GLTEXTURETYPE_2D;
1384 glt->inputdatasize = ddssize;
1385 glt->glinternalformat = texinfo->glinternalformat;
1386 glt->glformat = texinfo->glformat;
1387 glt->gltype = texinfo->gltype;
1388 glt->bytesperpixel = texinfo->internalbytesperpixel;
1390 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1391 glt->tilewidth = dds_width;
1392 glt->tileheight = dds_height;
1395 // texture uploading can take a while, so make sure we're sending keepalives
1396 CL_KeepaliveMessage(false);
1398 // upload the texture
1399 // we need to restore the texture binding after finishing the upload
1401 GL_ActiveTexture(0);
1402 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1403 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1404 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1405 mippixels = ddspixels;
1406 mipwidth = dds_width;
1407 mipheight = dds_height;
1408 mipcomplete = false;
1409 for (mip = 0;mip < dds_miplevels+1;mip++)
1411 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1412 if (mippixels + mipsize > dds + ddssize)
1416 qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1420 qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1422 mippixels += mipsize;
1423 if (mipwidth <= 1 && mipheight <= 1)
1433 if (dds_miplevels > 1 && !mipcomplete)
1435 // need to set GL_TEXTURE_MAX_LEVEL
1436 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1438 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1439 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1442 return (rtexture_t *)glt;
1445 int R_TextureWidth(rtexture_t *rt)
1447 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1450 int R_TextureHeight(rtexture_t *rt)
1452 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1455 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1457 gltexture_t *glt = (gltexture_t *)rt;
1459 Host_Error("R_UpdateTexture: no data supplied");
1461 Host_Error("R_UpdateTexture: no texture supplied");
1463 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1464 // update part of the texture
1465 if (glt->bufferpixels)
1468 int bpp = glt->bytesperpixel;
1469 int inputskip = width*bpp;
1470 int outputskip = glt->tilewidth*bpp;
1471 const unsigned char *input = data;
1472 unsigned char *output = glt->bufferpixels;
1482 input -= y*inputskip;
1485 if (width > glt->tilewidth - x)
1486 width = glt->tilewidth - x;
1487 if (height > glt->tileheight - y)
1488 height = glt->tileheight - y;
1489 if (width < 1 || height < 1)
1492 glt->buffermodified = true;
1493 output += y*outputskip + x*bpp;
1494 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1495 memcpy(output, input, width*bpp);
1498 R_Upload(glt, data, x, y, 0, width, height, 1);
1501 int R_RealGetTexture(rtexture_t *rt)
1506 glt = (gltexture_t *)rt;
1507 if (glt->flags & GLTEXF_DYNAMIC)
1508 R_UpdateDynamicTexture(glt);
1509 if (glt->buffermodified && glt->bufferpixels)
1511 glt->buffermodified = false;
1512 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1521 void R_ClearTexture (rtexture_t *rt)
1523 gltexture_t *glt = (gltexture_t *)rt;
1525 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );