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;
1229 for (i = 3;i < size;i += 4)
1230 if (ddspixels[i] < 255)
1233 flags |= TEXF_ALPHA;
1235 else if (!memcmp(dds+84, "DXT1", 4))
1237 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1238 // LordHavoc: it is my belief that this does not infringe on the
1239 // patent because it is not decoding pixels...
1240 textype = TEXTYPE_DXT1;
1243 size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1244 for (i = 0;i < size;i += bytesperblock)
1245 if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1249 textype = TEXTYPE_DXT1A;
1250 flags |= TEXF_ALPHA;
1253 else if (!memcmp(dds+84, "DXT3", 4))
1255 textype = TEXTYPE_DXT3;
1258 size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1259 flags |= TEXF_ALPHA;
1261 else if (!memcmp(dds+84, "DXT5", 4))
1263 textype = TEXTYPE_DXT5;
1266 size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1267 flags |= TEXF_ALPHA;
1272 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1276 // return whether this texture is transparent
1278 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1280 // calculate average color if requested
1284 Vector4Clear(avgcolor);
1287 for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1289 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1290 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1291 avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
1292 avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
1294 f = (float)bytesperblock / size;
1295 avgcolor[0] *= (0.5f / 31.0f) * f;
1296 avgcolor[1] *= (0.5f / 63.0f) * f;
1297 avgcolor[2] *= (0.5f / 31.0f) * f;
1298 avgcolor[3] = 1; // too hard to calculate
1302 for (i = 0;i < size;i += 4)
1304 avgcolor[0] += ddspixels[i+2];
1305 avgcolor[1] += ddspixels[i+1];
1306 avgcolor[2] += ddspixels[i];
1307 avgcolor[3] += ddspixels[i+3];
1309 f = (1.0f / 255.0f) * bytesperpixel / size;
1317 if (dds_miplevels > 1)
1318 flags |= TEXF_MIPMAP;
1320 flags &= ~TEXF_MIPMAP;
1322 // if S3TC is not supported, there's very little we can do about it
1323 if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1326 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1330 texinfo = R_GetTexTypeInfo(textype, flags);
1332 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1333 strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1335 glt->chain = pool->gltchain;
1336 pool->gltchain = glt;
1337 glt->inputwidth = dds_width;
1338 glt->inputheight = dds_height;
1339 glt->inputdepth = 1;
1341 glt->textype = texinfo;
1342 glt->texturetype = GLTEXTURETYPE_2D;
1343 glt->inputdatasize = ddssize;
1344 glt->glinternalformat = texinfo->glinternalformat;
1345 glt->glformat = texinfo->glformat;
1346 glt->gltype = texinfo->gltype;
1347 glt->bytesperpixel = texinfo->internalbytesperpixel;
1349 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1350 glt->tilewidth = dds_width;
1351 glt->tileheight = dds_height;
1354 // texture uploading can take a while, so make sure we're sending keepalives
1355 CL_KeepaliveMessage(false);
1357 // upload the texture
1358 // we need to restore the texture binding after finishing the upload
1360 GL_ActiveTexture(0);
1361 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1362 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1363 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1364 mippixels = ddspixels;
1365 mipwidth = dds_width;
1366 mipheight = dds_height;
1367 mipcomplete = false;
1368 for (mip = 0;mip < dds_miplevels+1;mip++)
1370 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1371 if (mippixels + mipsize > dds + ddssize)
1375 qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1379 qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1381 mippixels += mipsize;
1382 if (mipwidth <= 1 && mipheight <= 1)
1392 if (dds_miplevels > 1 && !mipcomplete)
1394 // need to set GL_TEXTURE_MAX_LEVEL
1395 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1397 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1398 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1401 return (rtexture_t *)glt;
1404 int R_TextureWidth(rtexture_t *rt)
1406 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1409 int R_TextureHeight(rtexture_t *rt)
1411 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1414 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1416 gltexture_t *glt = (gltexture_t *)rt;
1418 Host_Error("R_UpdateTexture: no data supplied");
1420 Host_Error("R_UpdateTexture: no texture supplied");
1422 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1423 // update part of the texture
1424 if (glt->bufferpixels)
1427 int bpp = glt->bytesperpixel;
1428 int inputskip = width*bpp;
1429 int outputskip = glt->tilewidth*bpp;
1430 const unsigned char *input = data;
1431 unsigned char *output = glt->bufferpixels;
1441 input -= y*inputskip;
1444 if (width > glt->tilewidth - x)
1445 width = glt->tilewidth - x;
1446 if (height > glt->tileheight - y)
1447 height = glt->tileheight - y;
1448 if (width < 1 || height < 1)
1451 glt->buffermodified = true;
1452 output += y*outputskip + x*bpp;
1453 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1454 memcpy(output, input, width*bpp);
1457 R_Upload(glt, data, x, y, 0, width, height, 1);
1460 int R_RealGetTexture(rtexture_t *rt)
1465 glt = (gltexture_t *)rt;
1466 if (glt->flags & GLTEXF_DYNAMIC)
1467 R_UpdateDynamicTexture(glt);
1468 if (glt->buffermodified && glt->bufferpixels)
1470 glt->buffermodified = false;
1471 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1480 void R_ClearTexture (rtexture_t *rt)
1482 gltexture_t *glt = (gltexture_t *)rt;
1484 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );