7 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)"};
8 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)"};
9 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%"};
10 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)"};
11 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"};
12 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"};
13 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
14 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
15 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
16 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
17 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
18 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
19 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)"};
20 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
21 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
22 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"};
24 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
25 int gl_filter_mag = GL_LINEAR;
28 static mempool_t *texturemempool;
30 // note: this must not conflict with TEXF_ flags in r_textures.h
31 // bitmask for mismatch checking
32 #define GLTEXF_IMPORTANTBITS (0)
33 // dynamic texture (treat texnum == 0 differently)
34 #define GLTEXF_DYNAMIC 0x00080000
36 typedef struct textypeinfo_s
39 int inputbytesperpixel;
40 int internalbytesperpixel;
41 float glinternalbytesperpixel;
48 static textypeinfo_t textype_palette = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 3, GL_UNSIGNED_BYTE};
49 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 4, GL_UNSIGNED_BYTE};
50 static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 3, GL_UNSIGNED_BYTE};
51 static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 4, GL_UNSIGNED_BYTE};
52 static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_RGBA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
53 static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_RGBA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
54 static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 3, GL_UNSIGNED_BYTE};
55 static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 4, GL_UNSIGNED_BYTE};
56 static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_BGRA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
57 static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_BGRA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
58 static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP,2,2, 2.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16_ARB, GL_UNSIGNED_SHORT};
59 static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP,4,4, 4.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24_ARB, GL_UNSIGNED_INT};
60 static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , 4, GL_UNSIGNED_BYTE};
61 static textypeinfo_t textype_dxt1 = {TEXTYPE_DXT1 , 4, 0, 0.5f, 0 , GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 0};
62 static textypeinfo_t textype_dxt1a = {TEXTYPE_DXT1A , 4, 0, 0.5f, 0 , GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0};
63 static textypeinfo_t textype_dxt3 = {TEXTYPE_DXT3 , 4, 0, 1.0f, 0 , GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0};
64 static textypeinfo_t textype_dxt5 = {TEXTYPE_DXT5 , 4, 0, 1.0f, 0 , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0};
66 typedef enum gltexturetype_e
70 GLTEXTURETYPE_CUBEMAP,
71 GLTEXTURETYPE_RECTANGLE,
76 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
77 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
78 static int cubemapside[6] =
80 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
81 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
82 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
83 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
84 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
85 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
88 typedef struct gltexture_s
90 // this portion of the struct is exposed to the R_GetTexture macro for
91 // speed reasons, must be identical in rtexture_t!
92 int texnum; // GL texture slot number
93 qboolean dirty; // indicates that R_RealGetTexture should be called
94 int gltexturetypeenum; // used by R_Mesh_TexBind
96 // dynamic texture stuff [11/22/2007 Black]
97 updatecallback_t updatecallback;
98 void *updatacallback_data;
99 // --- [11/22/2007 Black]
101 // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
102 unsigned char *bufferpixels;
103 qboolean buffermodified;
105 // pointer to texturepool (check this to see if the texture is allocated)
106 struct gltexturepool_s *pool;
107 // pointer to next texture in texturepool chain
108 struct gltexture_s *chain;
109 // name of the texture (this might be removed someday), no duplicates
110 char identifier[MAX_QPATH + 32];
111 // original data size in *inputtexels
112 int inputwidth, inputheight, inputdepth;
113 // copy of the original texture(s) supplied to the upload function, for
114 // delayed uploads (non-precached)
115 unsigned char *inputtexels;
116 // original data size in *inputtexels
118 // flags supplied to the LoadTexture function
119 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
121 // pointer to one of the textype_ structs
122 textypeinfo_t *textype;
123 // one of the GLTEXTURETYPE_ values
125 // palette if the texture is TEXTYPE_PALETTE
126 const unsigned int *palette;
127 // actual stored texture size after gl_picmip and gl_max_size are applied
128 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
129 int tilewidth, tileheight, tiledepth;
130 // 1 or 6 depending on texturetype
134 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
137 int glinternalformat;
138 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
143 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
145 typedef struct gltexturepool_s
147 unsigned int sentinel;
148 struct gltexture_s *gltchain;
149 struct gltexturepool_s *next;
153 static gltexturepool_t *gltexturepoolchain = NULL;
155 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
156 static int resizebuffersize = 0;
157 static const unsigned char *texturebuffer;
158 static int texturebuffersize = 0;
160 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
165 return &textype_dxt1;
167 return &textype_dxt1a;
169 return &textype_dxt3;
171 return &textype_dxt5;
172 case TEXTYPE_PALETTE:
173 return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
175 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
176 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
178 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
181 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
182 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
184 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
187 return &textype_alpha;
188 case TEXTYPE_SHADOWMAP:
189 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
191 Host_Error("R_GetTexTypeInfo: unknown texture format");
194 return NULL; // this line only to hush compiler warnings
197 // dynamic texture code [11/22/2007 Black]
198 void R_MarkDirtyTexture(rtexture_t *rt) {
199 gltexture_t *glt = (gltexture_t*) rt;
204 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
205 if (glt->flags & GLTEXF_DYNAMIC)
207 // mark it as dirty, so R_RealGetTexture gets called
212 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
213 gltexture_t *glt = (gltexture_t*) rt;
218 glt->flags |= GLTEXF_DYNAMIC;
219 glt->updatecallback = updatecallback;
220 glt->updatacallback_data = data;
223 static void R_UpdateDynamicTexture(gltexture_t *glt) {
225 if( glt->updatecallback ) {
226 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
230 void R_PurgeTexture(rtexture_t *rt)
232 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
237 void R_FreeTexture(rtexture_t *rt)
239 gltexture_t *glt, **gltpointer;
241 glt = (gltexture_t *)rt;
243 Host_Error("R_FreeTexture: texture == NULL");
245 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
246 if (*gltpointer == glt)
247 *gltpointer = glt->chain;
249 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
254 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
257 if (glt->inputtexels)
258 Mem_Free(glt->inputtexels);
262 rtexturepool_t *R_AllocTexturePool(void)
264 gltexturepool_t *pool;
265 if (texturemempool == NULL)
267 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
270 pool->next = gltexturepoolchain;
271 gltexturepoolchain = pool;
272 pool->sentinel = TEXTUREPOOL_SENTINEL;
273 return (rtexturepool_t *)pool;
276 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
278 gltexturepool_t *pool, **poolpointer;
279 if (rtexturepool == NULL)
281 if (*rtexturepool == NULL)
283 pool = (gltexturepool_t *)(*rtexturepool);
284 *rtexturepool = NULL;
285 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
286 Host_Error("R_FreeTexturePool: pool already freed");
287 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
288 if (*poolpointer == pool)
289 *poolpointer = pool->next;
291 Host_Error("R_FreeTexturePool: pool not linked");
292 while (pool->gltchain)
293 R_FreeTexture((rtexture_t *)pool->gltchain);
298 typedef struct glmode_s
301 int minification, magnification;
305 static glmode_t modes[6] =
307 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
308 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
309 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
310 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
311 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
312 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
315 static void GL_TextureMode_f (void)
320 gltexturepool_t *pool;
324 for (i = 0;i < 6;i++)
326 if (gl_filter_min == modes[i].minification)
328 Con_Printf("%s\n", modes[i].name);
332 Con_Print("current filter is unknown???\n");
336 for (i = 0;i < 6;i++)
337 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
341 Con_Print("bad filter name\n");
345 gl_filter_min = modes[i].minification;
346 gl_filter_mag = modes[i].magnification;
348 // change all the existing mipmap texture objects
349 // FIXME: force renderer(/client/something?) restart instead?
352 for (pool = gltexturepoolchain;pool;pool = pool->next)
354 for (glt = pool->gltchain;glt;glt = glt->chain)
356 // only update already uploaded images
357 if (glt->texnum && !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
359 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
360 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
361 if (glt->flags & TEXF_MIPMAP)
363 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
367 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
369 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
370 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
376 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
378 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
383 case GLTEXTURETYPE_2D:
384 maxsize = vid.maxtexturesize_2d;
385 if (flags & TEXF_PICMIP)
387 maxsize = bound(1, gl_max_size.integer, maxsize);
388 picmip = gl_picmip.integer;
391 case GLTEXTURETYPE_3D:
392 maxsize = vid.maxtexturesize_3d;
394 case GLTEXTURETYPE_CUBEMAP:
395 maxsize = vid.maxtexturesize_cubemap;
401 if (vid.support.arb_texture_non_power_of_two)
402 width2 = min(inwidth >> picmip, maxsize);
405 for (width2 = 1;width2 < inwidth;width2 <<= 1);
406 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
408 *outwidth = max(1, width2);
412 if (vid.support.arb_texture_non_power_of_two)
413 height2 = min(inheight >> picmip, maxsize);
416 for (height2 = 1;height2 < inheight;height2 <<= 1);
417 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
419 *outheight = max(1, height2);
423 if (vid.support.arb_texture_non_power_of_two)
424 depth2 = min(indepth >> picmip, maxsize);
427 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
428 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
430 *outdepth = max(1, depth2);
435 static int R_CalcTexelDataSize (gltexture_t *glt)
437 int width2, height2, depth2, size;
439 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
441 size = width2 * height2 * depth2;
443 if (glt->flags & TEXF_MIPMAP)
445 while (width2 > 1 || height2 > 1 || depth2 > 1)
453 size += width2 * height2 * depth2;
457 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
460 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
464 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
465 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
467 gltexturepool_t *pool;
469 Con_Print("glsize input loaded mip alpha name\n");
470 for (pool = gltexturepoolchain;pool;pool = pool->next)
478 for (glt = pool->gltchain;glt;glt = glt->chain)
480 glsize = R_CalcTexelDataSize(glt);
481 isloaded = glt->texnum != 0;
483 pooltotalt += glsize;
484 pooltotalp += glt->inputdatasize;
488 poolloadedt += glsize;
489 poolloadedp += glt->inputdatasize;
492 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);
495 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);
496 sumtotal += pooltotal;
497 sumtotalt += pooltotalt;
498 sumtotalp += pooltotalp;
499 sumloaded += poolloaded;
500 sumloadedt += poolloadedt;
501 sumloadedp += poolloadedp;
504 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);
507 static void R_TextureStats_f(void)
509 R_TextureStats_Print(true, true, true);
512 static void r_textures_start(void)
514 // LordHavoc: allow any alignment
516 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
517 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
519 texturemempool = Mem_AllocPool("texture management", 0, NULL);
521 // Disable JPEG screenshots if the DLL isn't loaded
522 if (! JPEG_OpenLibrary ())
523 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
524 // TODO: support png screenshots?
528 static void r_textures_shutdown(void)
530 rtexturepool_t *temp;
532 JPEG_CloseLibrary ();
534 while(gltexturepoolchain)
536 temp = (rtexturepool_t *) gltexturepoolchain;
537 R_FreeTexturePool(&temp);
540 resizebuffersize = 0;
541 texturebuffersize = 0;
543 colorconvertbuffer = NULL;
544 texturebuffer = NULL;
545 Mem_FreePool(&texturemempool);
548 static void r_textures_newmap(void)
552 void R_Textures_Init (void)
554 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
555 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
556 Cvar_RegisterVariable (&gl_max_size);
557 Cvar_RegisterVariable (&gl_picmip);
558 Cvar_RegisterVariable (&gl_max_lightmapsize);
559 Cvar_RegisterVariable (&r_lerpimages);
560 Cvar_RegisterVariable (&gl_texture_anisotropy);
561 Cvar_RegisterVariable (&gl_texturecompression);
562 Cvar_RegisterVariable (&gl_texturecompression_color);
563 Cvar_RegisterVariable (&gl_texturecompression_normal);
564 Cvar_RegisterVariable (&gl_texturecompression_gloss);
565 Cvar_RegisterVariable (&gl_texturecompression_glow);
566 Cvar_RegisterVariable (&gl_texturecompression_2d);
567 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
568 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
569 Cvar_RegisterVariable (&gl_texturecompression_sky);
570 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
571 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
573 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
576 void R_Textures_Frame (void)
578 static int old_aniso = 0;
580 // could do procedural texture animation here, if we keep track of which
581 // textures were accessed this frame...
583 // free the resize buffers
584 resizebuffersize = 0;
587 Mem_Free(resizebuffer);
590 if (colorconvertbuffer)
592 Mem_Free(colorconvertbuffer);
593 colorconvertbuffer = NULL;
596 if (old_aniso != gl_texture_anisotropy.integer)
599 gltexturepool_t *pool;
602 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
604 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
608 for (pool = gltexturepoolchain;pool;pool = pool->next)
610 for (glt = pool->gltchain;glt;glt = glt->chain)
612 // only update already uploaded images
613 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
615 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
617 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
618 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
620 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
627 void R_MakeResizeBufferBigger(int size)
629 if (resizebuffersize < size)
631 resizebuffersize = size;
633 Mem_Free(resizebuffer);
634 if (colorconvertbuffer)
635 Mem_Free(colorconvertbuffer);
636 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
637 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
638 if (!resizebuffer || !colorconvertbuffer)
639 Host_Error("R_Upload: out of memory");
643 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
645 int textureenum = gltexturetypeenums[texturetype];
646 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
650 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
652 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
653 if (gl_texture_anisotropy.integer != aniso)
654 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
655 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
657 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
658 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
659 if (gltexturetypedimensions[texturetype] >= 3)
661 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
665 if (flags & TEXF_FORCENEAREST)
667 if (flags & TEXF_MIPMAP)
669 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
673 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
675 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
677 else if (flags & TEXF_FORCELINEAR)
679 if (flags & TEXF_MIPMAP)
681 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
683 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
687 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
692 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
694 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
698 if (flags & TEXF_MIPMAP)
700 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
704 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
706 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
709 if (textype == TEXTYPE_SHADOWMAP)
711 if (vid.support.arb_shadow)
713 if (flags & TEXF_COMPARE)
715 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
719 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
721 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
723 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
729 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
731 int i, mip, width, height, depth;
733 const unsigned char *prevbuffer;
738 // we need to restore the texture binding after finishing the upload
740 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
741 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
743 // these are rounded up versions of the size to do better resampling
744 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
746 width = glt->inputwidth;
747 height = glt->inputheight;
748 depth = glt->inputdepth;
752 for (width = 1;width < glt->inputwidth ;width <<= 1);
753 for (height = 1;height < glt->inputheight;height <<= 1);
754 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
757 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
758 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
760 if (prevbuffer == NULL)
762 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
763 prevbuffer = resizebuffer;
765 else if (glt->textype->textype == TEXTYPE_PALETTE)
767 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
768 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
769 prevbuffer = colorconvertbuffer;
772 // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
774 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))
776 // update a portion of the image
777 switch(glt->texturetype)
779 case GLTEXTURETYPE_2D:
780 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
782 case GLTEXTURETYPE_3D:
783 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
786 Host_Error("R_Upload: partial update of type other than 2D");
792 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
793 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
795 // cubemaps contain multiple images and thus get processed a bit differently
796 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
798 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
800 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
801 prevbuffer = resizebuffer;
804 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
806 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
807 prevbuffer = resizebuffer;
811 if (qglGetCompressedTexImageARB)
813 if (gl_texturecompression.integer >= 2)
814 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
816 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
819 switch(glt->texturetype)
821 case GLTEXTURETYPE_2D:
822 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
823 if (glt->flags & TEXF_MIPMAP)
825 while (width > 1 || height > 1 || depth > 1)
827 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
828 prevbuffer = resizebuffer;
829 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
833 case GLTEXTURETYPE_3D:
834 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
835 if (glt->flags & TEXF_MIPMAP)
837 while (width > 1 || height > 1 || depth > 1)
839 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
840 prevbuffer = resizebuffer;
841 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
845 case GLTEXTURETYPE_CUBEMAP:
846 // convert and upload each side in turn,
847 // from a continuous block of input texels
848 texturebuffer = (unsigned char *)prevbuffer;
849 for (i = 0;i < 6;i++)
851 prevbuffer = texturebuffer;
852 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
853 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
855 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
856 prevbuffer = resizebuffer;
859 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
861 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
862 prevbuffer = resizebuffer;
865 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
866 if (glt->flags & TEXF_MIPMAP)
868 while (width > 1 || height > 1 || depth > 1)
870 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
871 prevbuffer = resizebuffer;
872 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
877 case GLTEXTURETYPE_RECTANGLE:
878 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
881 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
883 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
886 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)
890 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
891 textypeinfo_t *texinfo;
893 if (cls.state == ca_dedicated)
896 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
898 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
901 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
903 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
906 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
908 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
912 texinfo = R_GetTexTypeInfo(textype, flags);
913 size = width * height * depth * sides * texinfo->inputbytesperpixel;
916 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
920 // clear the alpha flag if the texture has no transparent pixels
923 case TEXTYPE_PALETTE:
924 if (flags & TEXF_ALPHA)
926 flags &= ~TEXF_ALPHA;
929 for (i = 0;i < size;i++)
931 if (((unsigned char *)&palette[data[i]])[3] < 255)
942 if (flags & TEXF_ALPHA)
944 flags &= ~TEXF_ALPHA;
947 for (i = 3;i < size;i += 4)
958 case TEXTYPE_SHADOWMAP:
971 Host_Error("R_LoadTexture: unknown texture type");
974 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
976 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
978 glt->chain = pool->gltchain;
979 pool->gltchain = glt;
980 glt->inputwidth = width;
981 glt->inputheight = height;
982 glt->inputdepth = depth;
984 glt->textype = texinfo;
985 glt->texturetype = texturetype;
986 glt->inputdatasize = size;
987 glt->palette = palette;
988 glt->glinternalformat = texinfo->glinternalformat;
989 glt->glformat = texinfo->glformat;
990 glt->gltype = texinfo->gltype;
991 glt->bytesperpixel = texinfo->internalbytesperpixel;
992 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
995 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
996 // init the dynamic texture attributes, too [11/22/2007 Black]
997 glt->updatecallback = NULL;
998 glt->updatacallback_data = NULL;
1000 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1002 // upload the texture
1003 // data may be NULL (blank texture for dynamic rendering)
1005 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1006 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1007 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1008 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1010 // texture converting and uploading can take a while, so make sure we're sending keepalives
1011 CL_KeepaliveMessage(false);
1013 return (rtexture_t *)glt;
1016 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)
1018 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1021 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)
1023 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1026 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)
1028 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1031 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)
1033 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1036 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1038 int flags = TEXF_CLAMP;
1040 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1042 flags |= TEXF_FORCENEAREST;
1043 if (precision <= 16)
1044 flags |= TEXF_LOWPRECISION;
1048 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1050 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1053 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1055 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1058 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1060 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1063 int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
1065 gltexture_t *glt = (gltexture_t *)rt;
1068 int bytesperpixel = 0;
1069 int bytesperblock = 0;
1071 int dds_format_flags;
1079 GLint internalformat;
1080 const char *ddsfourcc;
1082 return -1; // NULL pointer
1083 if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
1084 return -2; // broken driver - crashes on reading internal format
1085 if (!qglGetTexLevelParameteriv)
1087 GL_ActiveTexture(0);
1088 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1089 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1090 qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
1091 switch(internalformat)
1093 default: ddsfourcc = NULL;bytesperpixel = 4;break;
1094 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1095 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
1096 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
1097 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
1099 if (!bytesperblock && skipuncompressed)
1100 return -3; // skipped
1101 memset(mipinfo, 0, sizeof(mipinfo));
1102 mipinfo[0][0] = glt->tilewidth;
1103 mipinfo[0][1] = glt->tileheight;
1105 if (glt->flags & TEXF_MIPMAP)
1107 for (mip = 1;mip < 16;mip++)
1109 mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
1110 mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
1111 if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
1119 for (mip = 0;mip < mipmaps;mip++)
1121 mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
1122 mipinfo[mip][3] = ddssize;
1123 ddssize += mipinfo[mip][2];
1125 dds = Mem_Alloc(tempmempool, ddssize);
1128 dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
1132 dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
1133 dds_format_flags = 0x4; // DDPF_FOURCC
1137 dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
1138 dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
1142 dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
1143 dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
1145 memcpy(dds, "DDS ", 4);
1146 StoreLittleLong(dds+4, ddssize);
1147 StoreLittleLong(dds+8, dds_flags);
1148 StoreLittleLong(dds+12, mipinfo[0][1]); // height
1149 StoreLittleLong(dds+16, mipinfo[0][0]); // width
1150 StoreLittleLong(dds+24, 1); // depth
1151 StoreLittleLong(dds+28, mipmaps); // mipmaps
1152 StoreLittleLong(dds+76, 32); // format size
1153 StoreLittleLong(dds+80, dds_format_flags);
1154 StoreLittleLong(dds+108, dds_caps1);
1155 StoreLittleLong(dds+112, dds_caps2);
1158 StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
1159 memcpy(dds+84, ddsfourcc, 4);
1160 for (mip = 0;mip < mipmaps;mip++)
1162 qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
1167 StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
1168 StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
1169 dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
1170 for (mip = 0;mip < mipmaps;mip++)
1172 qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
1175 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1176 ret = FS_WriteFile(filename, dds, ddssize);
1178 return ret ? ddssize : -5;
1181 rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
1183 int i, size, dds_flags, dds_format_flags, dds_miplevels, dds_width, dds_height, textype;
1184 int bytesperblock, bytesperpixel;
1187 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
1188 textypeinfo_t *texinfo;
1189 int mip, mipwidth, mipheight, mipsize;
1191 GLint oldbindtexnum;
1192 const unsigned char *mippixels, *ddspixels;
1194 fs_offset_t ddsfilesize;
1195 unsigned int ddssize;
1197 if (cls.state == ca_dedicated)
1200 dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
1201 ddssize = ddsfilesize;
1204 return NULL; // not found
1206 if (memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
1209 Con_Printf("^1%s: not a DDS image\n", filename);
1213 dds_flags = BuffLittleLong(dds+8);
1214 dds_format_flags = BuffLittleLong(dds+80);
1215 dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
1216 dds_width = BuffLittleLong(dds+16);
1217 dds_height = BuffLittleLong(dds+12);
1218 ddspixels = dds + 128;
1220 flags &= ~TEXF_ALPHA;
1221 if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
1223 // very sloppy BGRA 32bit identification
1224 textype = TEXTYPE_BGRA;
1227 size = dds_width*dds_height*bytesperpixel;
1228 if(128 + size > ddsfilesize)
1231 Con_Printf("^1%s: invalid BGRA DDS image\n");
1235 for (i = 3;i < size;i += 4)
1236 if (ddspixels[i] < 255)
1239 flags |= TEXF_ALPHA;
1241 else if (!memcmp(dds+84, "DXT1", 4))
1243 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1244 // LordHavoc: it is my belief that this does not infringe on the
1245 // patent because it is not decoding pixels...
1246 textype = TEXTYPE_DXT1;
1249 size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1250 if(128 + size > ddsfilesize)
1253 Con_Printf("^1%s: invalid DXT1 DDS image\n");
1256 for (i = 0;i < size;i += bytesperblock)
1257 if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1261 textype = TEXTYPE_DXT1A;
1262 flags |= TEXF_ALPHA;
1265 else if (!memcmp(dds+84, "DXT3", 4))
1267 textype = TEXTYPE_DXT3;
1270 size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1271 if(128 + size > ddsfilesize)
1274 Con_Printf("^1%s: invalid DXT3 DDS image\n");
1277 flags |= TEXF_ALPHA;
1279 else if (!memcmp(dds+84, "DXT5", 4))
1281 textype = TEXTYPE_DXT5;
1284 size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1285 if(128 + size > ddsfilesize)
1288 Con_Printf("^1%s: invalid DXT5 DDS image\n");
1291 flags |= TEXF_ALPHA;
1296 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1300 // return whether this texture is transparent
1302 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1304 // calculate average color if requested
1308 Vector4Clear(avgcolor);
1311 for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1313 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1314 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1315 avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
1316 avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
1318 f = (float)bytesperblock / size;
1319 avgcolor[0] *= (0.5f / 31.0f) * f;
1320 avgcolor[1] *= (0.5f / 63.0f) * f;
1321 avgcolor[2] *= (0.5f / 31.0f) * f;
1322 avgcolor[3] = 1; // too hard to calculate
1326 for (i = 0;i < size;i += 4)
1328 avgcolor[0] += ddspixels[i+2];
1329 avgcolor[1] += ddspixels[i+1];
1330 avgcolor[2] += ddspixels[i];
1331 avgcolor[3] += ddspixels[i+3];
1333 f = (1.0f / 255.0f) * bytesperpixel / size;
1341 if (dds_miplevels > 1)
1342 flags |= TEXF_MIPMAP;
1344 flags &= ~TEXF_MIPMAP;
1346 // if S3TC is not supported, there's very little we can do about it
1347 if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1350 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1354 texinfo = R_GetTexTypeInfo(textype, flags);
1356 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1357 strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1359 glt->chain = pool->gltchain;
1360 pool->gltchain = glt;
1361 glt->inputwidth = dds_width;
1362 glt->inputheight = dds_height;
1363 glt->inputdepth = 1;
1365 glt->textype = texinfo;
1366 glt->texturetype = GLTEXTURETYPE_2D;
1367 glt->inputdatasize = ddssize;
1368 glt->glinternalformat = texinfo->glinternalformat;
1369 glt->glformat = texinfo->glformat;
1370 glt->gltype = texinfo->gltype;
1371 glt->bytesperpixel = texinfo->internalbytesperpixel;
1373 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1374 glt->tilewidth = dds_width;
1375 glt->tileheight = dds_height;
1378 // texture uploading can take a while, so make sure we're sending keepalives
1379 CL_KeepaliveMessage(false);
1381 // upload the texture
1382 // we need to restore the texture binding after finishing the upload
1384 GL_ActiveTexture(0);
1385 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1386 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1387 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1388 mippixels = ddspixels;
1389 mipwidth = dds_width;
1390 mipheight = dds_height;
1391 mipcomplete = false;
1392 for (mip = 0;mip < dds_miplevels+1;mip++)
1394 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1395 if (mippixels + mipsize > dds + ddssize)
1399 qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1403 qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1405 mippixels += mipsize;
1406 if (mipwidth <= 1 && mipheight <= 1)
1416 if (dds_miplevels > 1 && !mipcomplete)
1418 // need to set GL_TEXTURE_MAX_LEVEL
1419 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1421 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1422 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1425 return (rtexture_t *)glt;
1428 int R_TextureWidth(rtexture_t *rt)
1430 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1433 int R_TextureHeight(rtexture_t *rt)
1435 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1438 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1440 gltexture_t *glt = (gltexture_t *)rt;
1442 Host_Error("R_UpdateTexture: no data supplied");
1444 Host_Error("R_UpdateTexture: no texture supplied");
1446 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1447 // update part of the texture
1448 if (glt->bufferpixels)
1451 int bpp = glt->bytesperpixel;
1452 int inputskip = width*bpp;
1453 int outputskip = glt->tilewidth*bpp;
1454 const unsigned char *input = data;
1455 unsigned char *output = glt->bufferpixels;
1465 input -= y*inputskip;
1468 if (width > glt->tilewidth - x)
1469 width = glt->tilewidth - x;
1470 if (height > glt->tileheight - y)
1471 height = glt->tileheight - y;
1472 if (width < 1 || height < 1)
1475 glt->buffermodified = true;
1476 output += y*outputskip + x*bpp;
1477 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1478 memcpy(output, input, width*bpp);
1481 R_Upload(glt, data, x, y, 0, width, height, 1);
1484 int R_RealGetTexture(rtexture_t *rt)
1489 glt = (gltexture_t *)rt;
1490 if (glt->flags & GLTEXF_DYNAMIC)
1491 R_UpdateDynamicTexture(glt);
1492 if (glt->buffermodified && glt->bufferpixels)
1494 glt->buffermodified = false;
1495 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1504 void R_ClearTexture (rtexture_t *rt)
1506 gltexture_t *glt = (gltexture_t *)rt;
1508 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );