6 #include "intoverflow.h"
8 cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
9 cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
10 cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0", "reduces resolution of textures by powers of 2, for example 1 will halve width/height, reducing texture memory usage by 75%"};
11 cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1", "bilinear filters images when scaling them up to power of 2 size (mode 1), looks better than glquake (mode 0)"};
12 cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
13 cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
14 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
15 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
16 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
17 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
18 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
19 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
20 cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
21 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
22 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
23 cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "1", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"};
25 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
26 int gl_filter_mag = GL_LINEAR;
29 static mempool_t *texturemempool;
31 // note: this must not conflict with TEXF_ flags in r_textures.h
32 // bitmask for mismatch checking
33 #define GLTEXF_IMPORTANTBITS (0)
34 // dynamic texture (treat texnum == 0 differently)
35 #define GLTEXF_DYNAMIC 0x00080000
37 typedef struct textypeinfo_s
40 int inputbytesperpixel;
41 int internalbytesperpixel;
42 float glinternalbytesperpixel;
49 static textypeinfo_t textype_palette = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 3, GL_UNSIGNED_BYTE};
50 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 4, GL_UNSIGNED_BYTE};
51 static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 3, GL_UNSIGNED_BYTE};
52 static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 4, GL_UNSIGNED_BYTE};
53 static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_RGBA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
54 static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_RGBA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
55 static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 3, GL_UNSIGNED_BYTE};
56 static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 4, GL_UNSIGNED_BYTE};
57 static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_BGRA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
58 static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_BGRA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
59 static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP,2,2, 2.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16_ARB, GL_UNSIGNED_SHORT};
60 static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP,4,4, 4.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24_ARB, GL_UNSIGNED_INT};
61 static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , 4, GL_UNSIGNED_BYTE};
62 static textypeinfo_t textype_dxt1 = {TEXTYPE_DXT1 , 4, 0, 0.5f, 0 , GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 0};
63 static textypeinfo_t textype_dxt1a = {TEXTYPE_DXT1A , 4, 0, 0.5f, 0 , GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0};
64 static textypeinfo_t textype_dxt3 = {TEXTYPE_DXT3 , 4, 0, 1.0f, 0 , GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0};
65 static textypeinfo_t textype_dxt5 = {TEXTYPE_DXT5 , 4, 0, 1.0f, 0 , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0};
67 typedef enum gltexturetype_e
71 GLTEXTURETYPE_CUBEMAP,
72 GLTEXTURETYPE_RECTANGLE,
77 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
78 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
79 static int cubemapside[6] =
81 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
82 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
83 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
84 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
85 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
86 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
89 typedef struct gltexture_s
91 // this portion of the struct is exposed to the R_GetTexture macro for
92 // speed reasons, must be identical in rtexture_t!
93 int texnum; // GL texture slot number
94 qboolean dirty; // indicates that R_RealGetTexture should be called
95 int gltexturetypeenum; // used by R_Mesh_TexBind
97 // dynamic texture stuff [11/22/2007 Black]
98 updatecallback_t updatecallback;
99 void *updatacallback_data;
100 // --- [11/22/2007 Black]
102 // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
103 unsigned char *bufferpixels;
104 qboolean buffermodified;
106 // pointer to texturepool (check this to see if the texture is allocated)
107 struct gltexturepool_s *pool;
108 // pointer to next texture in texturepool chain
109 struct gltexture_s *chain;
110 // name of the texture (this might be removed someday), no duplicates
111 char identifier[MAX_QPATH + 32];
112 // original data size in *inputtexels
113 int inputwidth, inputheight, inputdepth;
114 // copy of the original texture(s) supplied to the upload function, for
115 // delayed uploads (non-precached)
116 unsigned char *inputtexels;
117 // original data size in *inputtexels
119 // flags supplied to the LoadTexture function
120 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
122 // pointer to one of the textype_ structs
123 textypeinfo_t *textype;
124 // one of the GLTEXTURETYPE_ values
126 // palette if the texture is TEXTYPE_PALETTE
127 const unsigned int *palette;
128 // actual stored texture size after gl_picmip and gl_max_size are applied
129 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
130 int tilewidth, tileheight, tiledepth;
131 // 1 or 6 depending on texturetype
135 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
138 int glinternalformat;
139 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
144 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
146 typedef struct gltexturepool_s
148 unsigned int sentinel;
149 struct gltexture_s *gltchain;
150 struct gltexturepool_s *next;
154 static gltexturepool_t *gltexturepoolchain = NULL;
156 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
157 static int resizebuffersize = 0;
158 static const unsigned char *texturebuffer;
159 static int texturebuffersize = 0;
161 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
166 return &textype_dxt1;
168 return &textype_dxt1a;
170 return &textype_dxt3;
172 return &textype_dxt5;
173 case TEXTYPE_PALETTE:
174 return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
176 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
177 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
179 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
182 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
183 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
185 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;
192 Host_Error("R_GetTexTypeInfo: unknown texture format");
195 return NULL; // this line only to hush compiler warnings
198 // dynamic texture code [11/22/2007 Black]
199 void R_MarkDirtyTexture(rtexture_t *rt) {
200 gltexture_t *glt = (gltexture_t*) rt;
205 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
206 if (glt->flags & GLTEXF_DYNAMIC)
208 // mark it as dirty, so R_RealGetTexture gets called
213 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
214 gltexture_t *glt = (gltexture_t*) rt;
219 glt->flags |= GLTEXF_DYNAMIC;
220 glt->updatecallback = updatecallback;
221 glt->updatacallback_data = data;
224 static void R_UpdateDynamicTexture(gltexture_t *glt) {
226 if( glt->updatecallback ) {
227 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
231 void R_PurgeTexture(rtexture_t *rt)
233 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
238 void R_FreeTexture(rtexture_t *rt)
240 gltexture_t *glt, **gltpointer;
242 glt = (gltexture_t *)rt;
244 Host_Error("R_FreeTexture: texture == NULL");
246 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
247 if (*gltpointer == glt)
248 *gltpointer = glt->chain;
250 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
255 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
258 if (glt->inputtexels)
259 Mem_Free(glt->inputtexels);
263 rtexturepool_t *R_AllocTexturePool(void)
265 gltexturepool_t *pool;
266 if (texturemempool == NULL)
268 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
271 pool->next = gltexturepoolchain;
272 gltexturepoolchain = pool;
273 pool->sentinel = TEXTUREPOOL_SENTINEL;
274 return (rtexturepool_t *)pool;
277 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
279 gltexturepool_t *pool, **poolpointer;
280 if (rtexturepool == NULL)
282 if (*rtexturepool == NULL)
284 pool = (gltexturepool_t *)(*rtexturepool);
285 *rtexturepool = NULL;
286 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
287 Host_Error("R_FreeTexturePool: pool already freed");
288 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
289 if (*poolpointer == pool)
290 *poolpointer = pool->next;
292 Host_Error("R_FreeTexturePool: pool not linked");
293 while (pool->gltchain)
294 R_FreeTexture((rtexture_t *)pool->gltchain);
299 typedef struct glmode_s
302 int minification, magnification;
306 static glmode_t modes[6] =
308 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
309 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
310 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
311 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
312 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
313 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
316 static void GL_TextureMode_f (void)
321 gltexturepool_t *pool;
325 for (i = 0;i < 6;i++)
327 if (gl_filter_min == modes[i].minification)
329 Con_Printf("%s\n", modes[i].name);
333 Con_Print("current filter is unknown???\n");
337 for (i = 0;i < 6;i++)
338 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
342 Con_Print("bad filter name\n");
346 gl_filter_min = modes[i].minification;
347 gl_filter_mag = modes[i].magnification;
349 // change all the existing mipmap texture objects
350 // FIXME: force renderer(/client/something?) restart instead?
353 for (pool = gltexturepoolchain;pool;pool = pool->next)
355 for (glt = pool->gltchain;glt;glt = glt->chain)
357 // only update already uploaded images
358 if (glt->texnum && !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
360 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
361 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
362 if (glt->flags & TEXF_MIPMAP)
364 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
368 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
370 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
371 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
377 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
379 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
384 case GLTEXTURETYPE_2D:
385 maxsize = vid.maxtexturesize_2d;
386 if (flags & TEXF_PICMIP)
388 maxsize = bound(1, gl_max_size.integer, maxsize);
389 picmip = gl_picmip.integer;
392 case GLTEXTURETYPE_3D:
393 maxsize = vid.maxtexturesize_3d;
395 case GLTEXTURETYPE_CUBEMAP:
396 maxsize = vid.maxtexturesize_cubemap;
402 if (vid.support.arb_texture_non_power_of_two)
403 width2 = min(inwidth >> picmip, maxsize);
406 for (width2 = 1;width2 < inwidth;width2 <<= 1);
407 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
409 *outwidth = max(1, width2);
413 if (vid.support.arb_texture_non_power_of_two)
414 height2 = min(inheight >> picmip, maxsize);
417 for (height2 = 1;height2 < inheight;height2 <<= 1);
418 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
420 *outheight = max(1, height2);
424 if (vid.support.arb_texture_non_power_of_two)
425 depth2 = min(indepth >> picmip, maxsize);
428 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
429 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
431 *outdepth = max(1, depth2);
436 static int R_CalcTexelDataSize (gltexture_t *glt)
438 int width2, height2, depth2, size;
440 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
442 size = width2 * height2 * depth2;
444 if (glt->flags & TEXF_MIPMAP)
446 while (width2 > 1 || height2 > 1 || depth2 > 1)
454 size += width2 * height2 * depth2;
458 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
461 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
465 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
466 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
468 gltexturepool_t *pool;
470 Con_Print("glsize input loaded mip alpha name\n");
471 for (pool = gltexturepoolchain;pool;pool = pool->next)
479 for (glt = pool->gltchain;glt;glt = glt->chain)
481 glsize = R_CalcTexelDataSize(glt);
482 isloaded = glt->texnum != 0;
484 pooltotalt += glsize;
485 pooltotalp += glt->inputdatasize;
489 poolloadedt += glsize;
490 poolloadedp += glt->inputdatasize;
493 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);
496 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);
497 sumtotal += pooltotal;
498 sumtotalt += pooltotalt;
499 sumtotalp += pooltotalp;
500 sumloaded += poolloaded;
501 sumloadedt += poolloadedt;
502 sumloadedp += poolloadedp;
505 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);
508 static void R_TextureStats_f(void)
510 R_TextureStats_Print(true, true, true);
513 static void r_textures_start(void)
515 // LordHavoc: allow any alignment
517 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
518 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
520 texturemempool = Mem_AllocPool("texture management", 0, NULL);
522 // Disable JPEG screenshots if the DLL isn't loaded
523 if (! JPEG_OpenLibrary ())
524 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
525 // TODO: support png screenshots?
529 static void r_textures_shutdown(void)
531 rtexturepool_t *temp;
533 JPEG_CloseLibrary ();
535 while(gltexturepoolchain)
537 temp = (rtexturepool_t *) gltexturepoolchain;
538 R_FreeTexturePool(&temp);
541 resizebuffersize = 0;
542 texturebuffersize = 0;
544 colorconvertbuffer = NULL;
545 texturebuffer = NULL;
546 Mem_FreePool(&texturemempool);
549 static void r_textures_newmap(void)
553 void R_Textures_Init (void)
555 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
556 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
557 Cvar_RegisterVariable (&gl_max_size);
558 Cvar_RegisterVariable (&gl_picmip);
559 Cvar_RegisterVariable (&gl_max_lightmapsize);
560 Cvar_RegisterVariable (&r_lerpimages);
561 Cvar_RegisterVariable (&gl_texture_anisotropy);
562 Cvar_RegisterVariable (&gl_texturecompression);
563 Cvar_RegisterVariable (&gl_texturecompression_color);
564 Cvar_RegisterVariable (&gl_texturecompression_normal);
565 Cvar_RegisterVariable (&gl_texturecompression_gloss);
566 Cvar_RegisterVariable (&gl_texturecompression_glow);
567 Cvar_RegisterVariable (&gl_texturecompression_2d);
568 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
569 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
570 Cvar_RegisterVariable (&gl_texturecompression_sky);
571 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
572 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
574 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
577 void R_Textures_Frame (void)
579 static int old_aniso = 0;
581 // could do procedural texture animation here, if we keep track of which
582 // textures were accessed this frame...
584 // free the resize buffers
585 resizebuffersize = 0;
588 Mem_Free(resizebuffer);
591 if (colorconvertbuffer)
593 Mem_Free(colorconvertbuffer);
594 colorconvertbuffer = NULL;
597 if (old_aniso != gl_texture_anisotropy.integer)
600 gltexturepool_t *pool;
603 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
605 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
609 for (pool = gltexturepoolchain;pool;pool = pool->next)
611 for (glt = pool->gltchain;glt;glt = glt->chain)
613 // only update already uploaded images
614 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
616 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
618 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
619 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
621 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
628 void R_MakeResizeBufferBigger(int size)
630 if (resizebuffersize < size)
632 resizebuffersize = size;
634 Mem_Free(resizebuffer);
635 if (colorconvertbuffer)
636 Mem_Free(colorconvertbuffer);
637 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
638 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
639 if (!resizebuffer || !colorconvertbuffer)
640 Host_Error("R_Upload: out of memory");
644 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
646 int textureenum = gltexturetypeenums[texturetype];
647 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
651 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
653 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
654 if (gl_texture_anisotropy.integer != aniso)
655 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
656 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
658 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
659 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
660 if (gltexturetypedimensions[texturetype] >= 3)
662 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
666 if (flags & TEXF_FORCENEAREST)
668 if (flags & TEXF_MIPMAP)
670 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
674 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
676 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
678 else if (flags & TEXF_FORCELINEAR)
680 if (flags & TEXF_MIPMAP)
682 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
684 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
688 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
693 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
695 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
699 if (flags & TEXF_MIPMAP)
701 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
705 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
707 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
710 if (textype == TEXTYPE_SHADOWMAP)
712 if (vid.support.arb_shadow)
714 if (flags & TEXF_COMPARE)
716 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
720 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
722 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
724 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
730 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
732 int i, mip, width, height, depth;
734 const unsigned char *prevbuffer;
739 // we need to restore the texture binding after finishing the upload
741 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
742 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
744 // these are rounded up versions of the size to do better resampling
745 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
747 width = glt->inputwidth;
748 height = glt->inputheight;
749 depth = glt->inputdepth;
753 for (width = 1;width < glt->inputwidth ;width <<= 1);
754 for (height = 1;height < glt->inputheight;height <<= 1);
755 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
758 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
759 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
761 if (prevbuffer == NULL)
763 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
764 prevbuffer = resizebuffer;
766 else if (glt->textype->textype == TEXTYPE_PALETTE)
768 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
769 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
770 prevbuffer = colorconvertbuffer;
773 // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
775 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))
777 // update a portion of the image
778 switch(glt->texturetype)
780 case GLTEXTURETYPE_2D:
781 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
783 case GLTEXTURETYPE_3D:
784 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
787 Host_Error("R_Upload: partial update of type other than 2D");
793 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
794 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
796 // cubemaps contain multiple images and thus get processed a bit differently
797 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
799 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
801 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
802 prevbuffer = resizebuffer;
805 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
807 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
808 prevbuffer = resizebuffer;
812 if (qglGetCompressedTexImageARB)
814 if (gl_texturecompression.integer >= 2)
815 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
817 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
820 switch(glt->texturetype)
822 case GLTEXTURETYPE_2D:
823 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
824 if (glt->flags & TEXF_MIPMAP)
826 while (width > 1 || height > 1 || depth > 1)
828 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
829 prevbuffer = resizebuffer;
830 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
834 case GLTEXTURETYPE_3D:
835 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
836 if (glt->flags & TEXF_MIPMAP)
838 while (width > 1 || height > 1 || depth > 1)
840 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
841 prevbuffer = resizebuffer;
842 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
846 case GLTEXTURETYPE_CUBEMAP:
847 // convert and upload each side in turn,
848 // from a continuous block of input texels
849 texturebuffer = (unsigned char *)prevbuffer;
850 for (i = 0;i < 6;i++)
852 prevbuffer = texturebuffer;
853 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
854 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
856 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
857 prevbuffer = resizebuffer;
860 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
862 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
863 prevbuffer = resizebuffer;
866 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
867 if (glt->flags & TEXF_MIPMAP)
869 while (width > 1 || height > 1 || depth > 1)
871 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
872 prevbuffer = resizebuffer;
873 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
878 case GLTEXTURETYPE_RECTANGLE:
879 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
882 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
884 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
887 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)
891 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
892 textypeinfo_t *texinfo;
894 if (cls.state == ca_dedicated)
897 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
899 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
902 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
904 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
907 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
909 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
913 texinfo = R_GetTexTypeInfo(textype, flags);
914 size = width * height * depth * sides * texinfo->inputbytesperpixel;
917 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
921 // clear the alpha flag if the texture has no transparent pixels
924 case TEXTYPE_PALETTE:
925 if (flags & TEXF_ALPHA)
927 flags &= ~TEXF_ALPHA;
930 for (i = 0;i < size;i++)
932 if (((unsigned char *)&palette[data[i]])[3] < 255)
943 if (flags & TEXF_ALPHA)
945 flags &= ~TEXF_ALPHA;
948 for (i = 3;i < size;i += 4)
959 case TEXTYPE_SHADOWMAP:
972 Host_Error("R_LoadTexture: unknown texture type");
975 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
977 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
979 glt->chain = pool->gltchain;
980 pool->gltchain = glt;
981 glt->inputwidth = width;
982 glt->inputheight = height;
983 glt->inputdepth = depth;
985 glt->textype = texinfo;
986 glt->texturetype = texturetype;
987 glt->inputdatasize = size;
988 glt->palette = palette;
989 glt->glinternalformat = texinfo->glinternalformat;
990 glt->glformat = texinfo->glformat;
991 glt->gltype = texinfo->gltype;
992 glt->bytesperpixel = texinfo->internalbytesperpixel;
993 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
996 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
997 // init the dynamic texture attributes, too [11/22/2007 Black]
998 glt->updatecallback = NULL;
999 glt->updatacallback_data = NULL;
1001 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1003 // upload the texture
1004 // data may be NULL (blank texture for dynamic rendering)
1006 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1007 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1008 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1009 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1011 // texture converting and uploading can take a while, so make sure we're sending keepalives
1012 CL_KeepaliveMessage(false);
1014 return (rtexture_t *)glt;
1017 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)
1019 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1022 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)
1024 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1027 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)
1029 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1032 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)
1034 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1037 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1039 int flags = TEXF_CLAMP;
1041 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1043 flags |= TEXF_FORCENEAREST;
1044 if (precision <= 16)
1045 flags |= TEXF_LOWPRECISION;
1049 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1051 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1054 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1056 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1059 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1061 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1064 int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
1066 gltexture_t *glt = (gltexture_t *)rt;
1069 int bytesperpixel = 0;
1070 int bytesperblock = 0;
1072 int dds_format_flags;
1080 GLint internalformat;
1081 const char *ddsfourcc;
1083 return -1; // NULL pointer
1084 if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
1085 return -2; // broken driver - crashes on reading internal format
1086 if (!qglGetTexLevelParameteriv)
1088 GL_ActiveTexture(0);
1089 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1090 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1091 qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
1092 switch(internalformat)
1094 default: ddsfourcc = NULL;bytesperpixel = 4;break;
1095 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1096 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
1097 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
1098 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
1100 if (!bytesperblock && skipuncompressed)
1101 return -3; // skipped
1102 memset(mipinfo, 0, sizeof(mipinfo));
1103 mipinfo[0][0] = glt->tilewidth;
1104 mipinfo[0][1] = glt->tileheight;
1106 if (glt->flags & TEXF_MIPMAP)
1108 for (mip = 1;mip < 16;mip++)
1110 mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
1111 mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
1112 if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
1120 for (mip = 0;mip < mipmaps;mip++)
1122 mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
1123 mipinfo[mip][3] = ddssize;
1124 ddssize += mipinfo[mip][2];
1126 dds = Mem_Alloc(tempmempool, ddssize);
1129 dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
1133 dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
1134 dds_format_flags = 0x4; // DDPF_FOURCC
1138 dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
1139 dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
1143 dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
1144 dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
1146 memcpy(dds, "DDS ", 4);
1147 StoreLittleLong(dds+4, ddssize);
1148 StoreLittleLong(dds+8, dds_flags);
1149 StoreLittleLong(dds+12, mipinfo[0][1]); // height
1150 StoreLittleLong(dds+16, mipinfo[0][0]); // width
1151 StoreLittleLong(dds+24, 1); // depth
1152 StoreLittleLong(dds+28, mipmaps); // mipmaps
1153 StoreLittleLong(dds+76, 32); // format size
1154 StoreLittleLong(dds+80, dds_format_flags);
1155 StoreLittleLong(dds+108, dds_caps1);
1156 StoreLittleLong(dds+112, dds_caps2);
1159 StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
1160 memcpy(dds+84, ddsfourcc, 4);
1161 for (mip = 0;mip < mipmaps;mip++)
1163 qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
1168 StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
1169 StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
1170 dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
1171 for (mip = 0;mip < mipmaps;mip++)
1173 qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
1176 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1177 ret = FS_WriteFile(filename, dds, ddssize);
1179 return ret ? ddssize : -5;
1182 rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
1184 int i, size, dds_flags, dds_format_flags, dds_miplevels, dds_width, dds_height, textype;
1185 int bytesperblock, bytesperpixel;
1188 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
1189 textypeinfo_t *texinfo;
1190 int mip, mipwidth, mipheight, mipsize;
1192 GLint oldbindtexnum;
1193 const unsigned char *mippixels, *ddspixels;
1195 fs_offset_t ddsfilesize;
1196 unsigned int ddssize;
1198 if (cls.state == ca_dedicated)
1201 dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
1202 ddssize = ddsfilesize;
1205 return NULL; // not found
1207 if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
1210 Con_Printf("^1%s: not a DDS image\n", filename);
1214 dds_flags = BuffLittleLong(dds+8);
1215 dds_format_flags = BuffLittleLong(dds+80);
1216 dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
1217 dds_width = BuffLittleLong(dds+16);
1218 dds_height = BuffLittleLong(dds+12);
1219 ddspixels = dds + 128;
1221 flags &= ~TEXF_ALPHA;
1222 if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
1224 // very sloppy BGRA 32bit identification
1225 textype = TEXTYPE_BGRA;
1228 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
1229 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1232 Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
1236 for (i = 3;i < size;i += 4)
1237 if (ddspixels[i] < 255)
1240 flags |= TEXF_ALPHA;
1242 else if (!memcmp(dds+84, "DXT1", 4))
1244 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1245 // LordHavoc: it is my belief that this does not infringe on the
1246 // patent because it is not decoding pixels...
1247 textype = TEXTYPE_DXT1;
1250 //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1251 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1252 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1255 Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
1258 for (i = 0;i < size;i += bytesperblock)
1259 if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1263 textype = TEXTYPE_DXT1A;
1264 flags |= TEXF_ALPHA;
1267 else if (!memcmp(dds+84, "DXT3", 4))
1269 textype = TEXTYPE_DXT3;
1272 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1273 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1276 Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
1279 flags |= TEXF_ALPHA;
1281 else if (!memcmp(dds+84, "DXT5", 4))
1283 textype = TEXTYPE_DXT5;
1286 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1287 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1290 Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
1293 flags |= TEXF_ALPHA;
1298 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1302 // return whether this texture is transparent
1304 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1306 // calculate average color if requested
1310 Vector4Clear(avgcolor);
1313 for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1315 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1316 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1317 avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
1318 avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
1320 f = (float)bytesperblock / size;
1321 avgcolor[0] *= (0.5f / 31.0f) * f;
1322 avgcolor[1] *= (0.5f / 63.0f) * f;
1323 avgcolor[2] *= (0.5f / 31.0f) * f;
1324 avgcolor[3] = 1; // too hard to calculate
1328 for (i = 0;i < size;i += 4)
1330 avgcolor[0] += ddspixels[i+2];
1331 avgcolor[1] += ddspixels[i+1];
1332 avgcolor[2] += ddspixels[i];
1333 avgcolor[3] += ddspixels[i+3];
1335 f = (1.0f / 255.0f) * bytesperpixel / size;
1343 if (dds_miplevels > 1)
1344 flags |= TEXF_MIPMAP;
1346 flags &= ~TEXF_MIPMAP;
1348 // if S3TC is not supported, there's very little we can do about it
1349 if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1352 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1356 texinfo = R_GetTexTypeInfo(textype, flags);
1358 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1359 strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1361 glt->chain = pool->gltchain;
1362 pool->gltchain = glt;
1363 glt->inputwidth = dds_width;
1364 glt->inputheight = dds_height;
1365 glt->inputdepth = 1;
1367 glt->textype = texinfo;
1368 glt->texturetype = GLTEXTURETYPE_2D;
1369 glt->inputdatasize = ddssize;
1370 glt->glinternalformat = texinfo->glinternalformat;
1371 glt->glformat = texinfo->glformat;
1372 glt->gltype = texinfo->gltype;
1373 glt->bytesperpixel = texinfo->internalbytesperpixel;
1375 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1376 glt->tilewidth = dds_width;
1377 glt->tileheight = dds_height;
1380 // texture uploading can take a while, so make sure we're sending keepalives
1381 CL_KeepaliveMessage(false);
1383 // upload the texture
1384 // we need to restore the texture binding after finishing the upload
1386 GL_ActiveTexture(0);
1387 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1388 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1389 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1390 mippixels = ddspixels;
1391 mipwidth = dds_width;
1392 mipheight = dds_height;
1393 mipcomplete = false;
1394 for (mip = 0;mip < dds_miplevels+1;mip++)
1396 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1397 if (mippixels + mipsize > dds + ddssize)
1401 qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1405 qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1407 mippixels += mipsize;
1408 if (mipwidth <= 1 && mipheight <= 1)
1418 if (dds_miplevels > 1 && !mipcomplete)
1420 // need to set GL_TEXTURE_MAX_LEVEL
1421 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1423 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1424 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1427 return (rtexture_t *)glt;
1430 int R_TextureWidth(rtexture_t *rt)
1432 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1435 int R_TextureHeight(rtexture_t *rt)
1437 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1440 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1442 gltexture_t *glt = (gltexture_t *)rt;
1444 Host_Error("R_UpdateTexture: no data supplied");
1446 Host_Error("R_UpdateTexture: no texture supplied");
1448 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1449 // update part of the texture
1450 if (glt->bufferpixels)
1453 int bpp = glt->bytesperpixel;
1454 int inputskip = width*bpp;
1455 int outputskip = glt->tilewidth*bpp;
1456 const unsigned char *input = data;
1457 unsigned char *output = glt->bufferpixels;
1467 input -= y*inputskip;
1470 if (width > glt->tilewidth - x)
1471 width = glt->tilewidth - x;
1472 if (height > glt->tileheight - y)
1473 height = glt->tileheight - y;
1474 if (width < 1 || height < 1)
1477 glt->buffermodified = true;
1478 output += y*outputskip + x*bpp;
1479 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1480 memcpy(output, input, width*bpp);
1483 R_Upload(glt, data, x, y, 0, width, height, 1);
1486 int R_RealGetTexture(rtexture_t *rt)
1491 glt = (gltexture_t *)rt;
1492 if (glt->flags & GLTEXF_DYNAMIC)
1493 R_UpdateDynamicTexture(glt);
1494 if (glt->buffermodified && glt->bufferpixels)
1496 glt->buffermodified = false;
1497 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1506 void R_ClearTexture (rtexture_t *rt)
1508 gltexture_t *glt = (gltexture_t *)rt;
1510 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );