6 #include "intoverflow.h"
8 cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
9 cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
10 cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0", "reduces resolution of textures by powers of 2, for example 1 will halve width/height, reducing texture memory usage by 75%"};
11 cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1", "bilinear filters images when scaling them up to power of 2 size (mode 1), looks better than glquake (mode 0)"};
12 cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
13 cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
14 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
15 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
16 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
17 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
18 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
19 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
20 cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
21 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
22 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
23 cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "1", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"};
25 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
26 int gl_filter_mag = GL_LINEAR;
29 static mempool_t *texturemempool;
31 // note: this must not conflict with TEXF_ flags in r_textures.h
32 // bitmask for mismatch checking
33 #define GLTEXF_IMPORTANTBITS (0)
34 // dynamic texture (treat texnum == 0 differently)
35 #define GLTEXF_DYNAMIC 0x00080000
37 typedef struct textypeinfo_s
40 int inputbytesperpixel;
41 int internalbytesperpixel;
42 float glinternalbytesperpixel;
50 static textypeinfo_t textype_palette = {TEXTYPE_PALETTE , 1, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
51 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE , 1, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
52 static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, 3 , GL_RGBA , GL_UNSIGNED_BYTE };
53 static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, 4 , GL_RGBA , GL_UNSIGNED_BYTE };
54 static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
55 static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA , GL_UNSIGNED_BYTE };
56 static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
57 static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
58 static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
59 static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_BGRA , GL_UNSIGNED_BYTE };
60 static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
61 static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
62 static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , GL_ALPHA , GL_UNSIGNED_BYTE };
63 static textypeinfo_t textype_dxt1 = {TEXTYPE_DXT1 , 4, 0, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , 0 , 0 };
64 static textypeinfo_t textype_dxt1a = {TEXTYPE_DXT1A , 4, 0, 0.5f, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0 , 0 };
65 static textypeinfo_t textype_dxt3 = {TEXTYPE_DXT3 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0 , 0 };
66 static textypeinfo_t textype_dxt5 = {TEXTYPE_DXT5 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0 , 0 };
67 static textypeinfo_t textype_colorbuffer = {TEXTYPE_COLORBUFFER, 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
70 typedef enum gltexturetype_e
74 GLTEXTURETYPE_CUBEMAP,
75 GLTEXTURETYPE_RECTANGLE,
80 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
81 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
82 static int cubemapside[6] =
84 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
85 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
86 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
87 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
88 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
89 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
92 typedef struct gltexture_s
94 // this portion of the struct is exposed to the R_GetTexture macro for
95 // speed reasons, must be identical in rtexture_t!
96 int texnum; // GL texture slot number
97 qboolean dirty; // indicates that R_RealGetTexture should be called
98 int gltexturetypeenum; // used by R_Mesh_TexBind
100 // dynamic texture stuff [11/22/2007 Black]
101 updatecallback_t updatecallback;
102 void *updatacallback_data;
103 // --- [11/22/2007 Black]
105 // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
106 unsigned char *bufferpixels;
107 qboolean buffermodified;
109 // pointer to texturepool (check this to see if the texture is allocated)
110 struct gltexturepool_s *pool;
111 // pointer to next texture in texturepool chain
112 struct gltexture_s *chain;
113 // name of the texture (this might be removed someday), no duplicates
114 char identifier[MAX_QPATH + 32];
115 // original data size in *inputtexels
116 int inputwidth, inputheight, inputdepth;
117 // copy of the original texture(s) supplied to the upload function, for
118 // delayed uploads (non-precached)
119 unsigned char *inputtexels;
120 // original data size in *inputtexels
122 // flags supplied to the LoadTexture function
123 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
125 // pointer to one of the textype_ structs
126 textypeinfo_t *textype;
127 // one of the GLTEXTURETYPE_ values
129 // palette if the texture is TEXTYPE_PALETTE
130 const unsigned int *palette;
131 // actual stored texture size after gl_picmip and gl_max_size are applied
132 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
133 int tilewidth, tileheight, tiledepth;
134 // 1 or 6 depending on texturetype
138 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
141 int glinternalformat;
142 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
147 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
149 typedef struct gltexturepool_s
151 unsigned int sentinel;
152 struct gltexture_s *gltchain;
153 struct gltexturepool_s *next;
157 static gltexturepool_t *gltexturepoolchain = NULL;
159 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
160 static int resizebuffersize = 0;
161 static const unsigned char *texturebuffer;
162 static int texturebuffersize = 0;
164 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
169 return &textype_dxt1;
171 return &textype_dxt1a;
173 return &textype_dxt3;
175 return &textype_dxt5;
176 case TEXTYPE_PALETTE:
177 return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
179 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
180 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
182 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
185 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
186 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
188 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
191 return &textype_alpha;
192 case TEXTYPE_SHADOWMAP:
193 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
194 case TEXTYPE_COLORBUFFER:
195 return &textype_colorbuffer;
197 Host_Error("R_GetTexTypeInfo: unknown texture format");
200 return NULL; // this line only to hush compiler warnings
203 // dynamic texture code [11/22/2007 Black]
204 void R_MarkDirtyTexture(rtexture_t *rt) {
205 gltexture_t *glt = (gltexture_t*) rt;
210 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
211 if (glt->flags & GLTEXF_DYNAMIC)
213 // mark it as dirty, so R_RealGetTexture gets called
218 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
219 gltexture_t *glt = (gltexture_t*) rt;
224 glt->flags |= GLTEXF_DYNAMIC;
225 glt->updatecallback = updatecallback;
226 glt->updatacallback_data = data;
229 static void R_UpdateDynamicTexture(gltexture_t *glt) {
231 if( glt->updatecallback ) {
232 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
236 void R_PurgeTexture(rtexture_t *rt)
238 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
243 void R_FreeTexture(rtexture_t *rt)
245 gltexture_t *glt, **gltpointer;
247 glt = (gltexture_t *)rt;
249 Host_Error("R_FreeTexture: texture == NULL");
251 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
252 if (*gltpointer == glt)
253 *gltpointer = glt->chain;
255 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
260 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
263 if (glt->inputtexels)
264 Mem_Free(glt->inputtexels);
268 rtexturepool_t *R_AllocTexturePool(void)
270 gltexturepool_t *pool;
271 if (texturemempool == NULL)
273 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
276 pool->next = gltexturepoolchain;
277 gltexturepoolchain = pool;
278 pool->sentinel = TEXTUREPOOL_SENTINEL;
279 return (rtexturepool_t *)pool;
282 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
284 gltexturepool_t *pool, **poolpointer;
285 if (rtexturepool == NULL)
287 if (*rtexturepool == NULL)
289 pool = (gltexturepool_t *)(*rtexturepool);
290 *rtexturepool = NULL;
291 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
292 Host_Error("R_FreeTexturePool: pool already freed");
293 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
294 if (*poolpointer == pool)
295 *poolpointer = pool->next;
297 Host_Error("R_FreeTexturePool: pool not linked");
298 while (pool->gltchain)
299 R_FreeTexture((rtexture_t *)pool->gltchain);
304 typedef struct glmode_s
307 int minification, magnification;
311 static glmode_t modes[6] =
313 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
314 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
315 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
316 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
317 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
318 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
321 static void GL_TextureMode_f (void)
326 gltexturepool_t *pool;
330 for (i = 0;i < 6;i++)
332 if (gl_filter_min == modes[i].minification)
334 Con_Printf("%s\n", modes[i].name);
338 Con_Print("current filter is unknown???\n");
342 for (i = 0;i < 6;i++)
343 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
347 Con_Print("bad filter name\n");
351 gl_filter_min = modes[i].minification;
352 gl_filter_mag = modes[i].magnification;
354 // change all the existing mipmap texture objects
355 // FIXME: force renderer(/client/something?) restart instead?
358 for (pool = gltexturepoolchain;pool;pool = pool->next)
360 for (glt = pool->gltchain;glt;glt = glt->chain)
362 // only update already uploaded images
363 if (glt->texnum && !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
365 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
366 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
367 if (glt->flags & TEXF_MIPMAP)
369 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
373 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
375 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
376 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
382 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
384 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
389 case GLTEXTURETYPE_2D:
390 maxsize = vid.maxtexturesize_2d;
391 if (flags & TEXF_PICMIP)
393 maxsize = bound(1, gl_max_size.integer, maxsize);
394 picmip = gl_picmip.integer;
397 case GLTEXTURETYPE_3D:
398 maxsize = vid.maxtexturesize_3d;
400 case GLTEXTURETYPE_CUBEMAP:
401 maxsize = vid.maxtexturesize_cubemap;
407 if (vid.support.arb_texture_non_power_of_two)
408 width2 = min(inwidth >> picmip, maxsize);
411 for (width2 = 1;width2 < inwidth;width2 <<= 1);
412 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
414 *outwidth = max(1, width2);
418 if (vid.support.arb_texture_non_power_of_two)
419 height2 = min(inheight >> picmip, maxsize);
422 for (height2 = 1;height2 < inheight;height2 <<= 1);
423 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
425 *outheight = max(1, height2);
429 if (vid.support.arb_texture_non_power_of_two)
430 depth2 = min(indepth >> picmip, maxsize);
433 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
434 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
436 *outdepth = max(1, depth2);
441 static int R_CalcTexelDataSize (gltexture_t *glt)
443 int width2, height2, depth2, size;
445 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
447 size = width2 * height2 * depth2;
449 if (glt->flags & TEXF_MIPMAP)
451 while (width2 > 1 || height2 > 1 || depth2 > 1)
459 size += width2 * height2 * depth2;
463 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
466 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
470 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
471 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
473 gltexturepool_t *pool;
475 Con_Print("glsize input loaded mip alpha name\n");
476 for (pool = gltexturepoolchain;pool;pool = pool->next)
484 for (glt = pool->gltchain;glt;glt = glt->chain)
486 glsize = R_CalcTexelDataSize(glt);
487 isloaded = glt->texnum != 0;
489 pooltotalt += glsize;
490 pooltotalp += glt->inputdatasize;
494 poolloadedt += glsize;
495 poolloadedp += glt->inputdatasize;
498 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);
501 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);
502 sumtotal += pooltotal;
503 sumtotalt += pooltotalt;
504 sumtotalp += pooltotalp;
505 sumloaded += poolloaded;
506 sumloadedt += poolloadedt;
507 sumloadedp += poolloadedp;
510 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);
513 static void R_TextureStats_f(void)
515 R_TextureStats_Print(true, true, true);
518 static void r_textures_start(void)
520 // LordHavoc: allow any alignment
522 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
523 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
525 texturemempool = Mem_AllocPool("texture management", 0, NULL);
527 // Disable JPEG screenshots if the DLL isn't loaded
528 if (! JPEG_OpenLibrary ())
529 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
530 // TODO: support png screenshots?
534 static void r_textures_shutdown(void)
536 rtexturepool_t *temp;
538 JPEG_CloseLibrary ();
540 while(gltexturepoolchain)
542 temp = (rtexturepool_t *) gltexturepoolchain;
543 R_FreeTexturePool(&temp);
546 resizebuffersize = 0;
547 texturebuffersize = 0;
549 colorconvertbuffer = NULL;
550 texturebuffer = NULL;
551 Mem_FreePool(&texturemempool);
554 static void r_textures_newmap(void)
558 void R_Textures_Init (void)
560 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
561 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
562 Cvar_RegisterVariable (&gl_max_size);
563 Cvar_RegisterVariable (&gl_picmip);
564 Cvar_RegisterVariable (&gl_max_lightmapsize);
565 Cvar_RegisterVariable (&r_lerpimages);
566 Cvar_RegisterVariable (&gl_texture_anisotropy);
567 Cvar_RegisterVariable (&gl_texturecompression);
568 Cvar_RegisterVariable (&gl_texturecompression_color);
569 Cvar_RegisterVariable (&gl_texturecompression_normal);
570 Cvar_RegisterVariable (&gl_texturecompression_gloss);
571 Cvar_RegisterVariable (&gl_texturecompression_glow);
572 Cvar_RegisterVariable (&gl_texturecompression_2d);
573 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
574 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
575 Cvar_RegisterVariable (&gl_texturecompression_sky);
576 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
577 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
579 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
582 void R_Textures_Frame (void)
584 static int old_aniso = 0;
586 // could do procedural texture animation here, if we keep track of which
587 // textures were accessed this frame...
589 // free the resize buffers
590 resizebuffersize = 0;
593 Mem_Free(resizebuffer);
596 if (colorconvertbuffer)
598 Mem_Free(colorconvertbuffer);
599 colorconvertbuffer = NULL;
602 if (old_aniso != gl_texture_anisotropy.integer)
605 gltexturepool_t *pool;
608 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
610 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
614 for (pool = gltexturepoolchain;pool;pool = pool->next)
616 for (glt = pool->gltchain;glt;glt = glt->chain)
618 // only update already uploaded images
619 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
621 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
623 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
624 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
626 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
633 void R_MakeResizeBufferBigger(int size)
635 if (resizebuffersize < size)
637 resizebuffersize = size;
639 Mem_Free(resizebuffer);
640 if (colorconvertbuffer)
641 Mem_Free(colorconvertbuffer);
642 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
643 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
644 if (!resizebuffer || !colorconvertbuffer)
645 Host_Error("R_Upload: out of memory");
649 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
651 int textureenum = gltexturetypeenums[texturetype];
652 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
656 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
658 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
659 if (gl_texture_anisotropy.integer != aniso)
660 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
661 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
663 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
664 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
665 if (gltexturetypedimensions[texturetype] >= 3)
667 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
671 if (flags & TEXF_FORCENEAREST)
673 if (flags & TEXF_MIPMAP)
675 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
679 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
681 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
683 else if (flags & TEXF_FORCELINEAR)
685 if (flags & TEXF_MIPMAP)
687 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
689 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
693 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
698 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
700 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
704 if (flags & TEXF_MIPMAP)
706 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
710 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
712 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
715 if (textype == TEXTYPE_SHADOWMAP)
717 if (vid.support.arb_shadow)
719 if (flags & TEXF_COMPARE)
721 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
725 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
727 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
729 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
735 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
737 int i, mip, width, height, depth;
739 const unsigned char *prevbuffer;
744 // we need to restore the texture binding after finishing the upload
746 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
747 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
749 // these are rounded up versions of the size to do better resampling
750 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
752 width = glt->inputwidth;
753 height = glt->inputheight;
754 depth = glt->inputdepth;
758 for (width = 1;width < glt->inputwidth ;width <<= 1);
759 for (height = 1;height < glt->inputheight;height <<= 1);
760 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
763 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
764 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
766 if (prevbuffer == NULL)
768 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
769 prevbuffer = resizebuffer;
771 else if (glt->textype->textype == TEXTYPE_PALETTE)
773 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
774 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
775 prevbuffer = colorconvertbuffer;
778 // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
780 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))
782 // update a portion of the image
783 switch(glt->texturetype)
785 case GLTEXTURETYPE_2D:
786 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
788 case GLTEXTURETYPE_3D:
789 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
792 Host_Error("R_Upload: partial update of type other than 2D");
798 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
799 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
801 // cubemaps contain multiple images and thus get processed a bit differently
802 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
804 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
806 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
807 prevbuffer = resizebuffer;
810 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
812 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
813 prevbuffer = resizebuffer;
817 if (qglGetCompressedTexImageARB)
819 if (gl_texturecompression.integer >= 2)
820 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
822 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
825 switch(glt->texturetype)
827 case GLTEXTURETYPE_2D:
828 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
829 if (glt->flags & TEXF_MIPMAP)
831 while (width > 1 || height > 1 || depth > 1)
833 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
834 prevbuffer = resizebuffer;
835 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
839 case GLTEXTURETYPE_3D:
840 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
841 if (glt->flags & TEXF_MIPMAP)
843 while (width > 1 || height > 1 || depth > 1)
845 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
846 prevbuffer = resizebuffer;
847 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
851 case GLTEXTURETYPE_CUBEMAP:
852 // convert and upload each side in turn,
853 // from a continuous block of input texels
854 texturebuffer = (unsigned char *)prevbuffer;
855 for (i = 0;i < 6;i++)
857 prevbuffer = texturebuffer;
858 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
859 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
861 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
862 prevbuffer = resizebuffer;
865 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
867 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
868 prevbuffer = resizebuffer;
871 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
872 if (glt->flags & TEXF_MIPMAP)
874 while (width > 1 || height > 1 || depth > 1)
876 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
877 prevbuffer = resizebuffer;
878 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
883 case GLTEXTURETYPE_RECTANGLE:
884 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
887 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
889 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
892 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)
896 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
897 textypeinfo_t *texinfo, *texinfo2;
899 if (cls.state == ca_dedicated)
902 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
904 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
907 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
909 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
912 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
914 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
918 texinfo = R_GetTexTypeInfo(textype, flags);
919 size = width * height * depth * sides * texinfo->inputbytesperpixel;
922 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
926 // clear the alpha flag if the texture has no transparent pixels
929 case TEXTYPE_PALETTE:
930 if (flags & TEXF_ALPHA)
932 flags &= ~TEXF_ALPHA;
935 for (i = 0;i < size;i++)
937 if (((unsigned char *)&palette[data[i]])[3] < 255)
948 if (flags & TEXF_ALPHA)
950 flags &= ~TEXF_ALPHA;
953 for (i = 3;i < size;i += 4)
964 case TEXTYPE_SHADOWMAP:
976 case TEXTYPE_COLORBUFFER:
980 Host_Error("R_LoadTexture: unknown texture type");
983 texinfo2 = R_GetTexTypeInfo(textype, flags);
984 if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
987 Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
989 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
991 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
993 glt->chain = pool->gltchain;
994 pool->gltchain = glt;
995 glt->inputwidth = width;
996 glt->inputheight = height;
997 glt->inputdepth = depth;
999 glt->textype = texinfo;
1000 glt->texturetype = texturetype;
1001 glt->inputdatasize = size;
1002 glt->palette = palette;
1003 glt->glinternalformat = texinfo->glinternalformat;
1004 glt->glformat = texinfo->glformat;
1005 glt->gltype = texinfo->gltype;
1006 glt->bytesperpixel = texinfo->internalbytesperpixel;
1007 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1010 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1011 // init the dynamic texture attributes, too [11/22/2007 Black]
1012 glt->updatecallback = NULL;
1013 glt->updatacallback_data = NULL;
1015 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1017 // upload the texture
1018 // data may be NULL (blank texture for dynamic rendering)
1020 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1021 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1022 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1023 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1025 // texture converting and uploading can take a while, so make sure we're sending keepalives
1026 CL_KeepaliveMessage(false);
1028 return (rtexture_t *)glt;
1031 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)
1033 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1036 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)
1038 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1041 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)
1043 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1046 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)
1048 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1051 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1053 int flags = TEXF_CLAMP;
1055 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1057 flags |= TEXF_FORCENEAREST;
1058 if (precision <= 16)
1059 flags |= TEXF_LOWPRECISION;
1063 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1065 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1068 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1070 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1073 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1075 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1078 int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
1080 gltexture_t *glt = (gltexture_t *)rt;
1083 int bytesperpixel = 0;
1084 int bytesperblock = 0;
1086 int dds_format_flags;
1094 GLint internalformat;
1095 const char *ddsfourcc;
1097 return -1; // NULL pointer
1098 if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
1099 return -2; // broken driver - crashes on reading internal format
1100 if (!qglGetTexLevelParameteriv)
1102 GL_ActiveTexture(0);
1103 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1104 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1105 qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
1106 switch(internalformat)
1108 default: ddsfourcc = NULL;bytesperpixel = 4;break;
1109 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1110 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
1111 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
1112 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
1114 if (!bytesperblock && skipuncompressed)
1115 return -3; // skipped
1116 memset(mipinfo, 0, sizeof(mipinfo));
1117 mipinfo[0][0] = glt->tilewidth;
1118 mipinfo[0][1] = glt->tileheight;
1120 if (glt->flags & TEXF_MIPMAP)
1122 for (mip = 1;mip < 16;mip++)
1124 mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
1125 mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
1126 if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
1134 for (mip = 0;mip < mipmaps;mip++)
1136 mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
1137 mipinfo[mip][3] = ddssize;
1138 ddssize += mipinfo[mip][2];
1140 dds = Mem_Alloc(tempmempool, ddssize);
1143 dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
1147 dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
1148 dds_format_flags = 0x4; // DDPF_FOURCC
1152 dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
1153 dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
1157 dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
1158 dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
1160 memcpy(dds, "DDS ", 4);
1161 StoreLittleLong(dds+4, ddssize);
1162 StoreLittleLong(dds+8, dds_flags);
1163 StoreLittleLong(dds+12, mipinfo[0][1]); // height
1164 StoreLittleLong(dds+16, mipinfo[0][0]); // width
1165 StoreLittleLong(dds+24, 1); // depth
1166 StoreLittleLong(dds+28, mipmaps); // mipmaps
1167 StoreLittleLong(dds+76, 32); // format size
1168 StoreLittleLong(dds+80, dds_format_flags);
1169 StoreLittleLong(dds+108, dds_caps1);
1170 StoreLittleLong(dds+112, dds_caps2);
1173 StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
1174 memcpy(dds+84, ddsfourcc, 4);
1175 for (mip = 0;mip < mipmaps;mip++)
1177 qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
1182 StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
1183 StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
1184 dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
1185 for (mip = 0;mip < mipmaps;mip++)
1187 qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
1190 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1191 ret = FS_WriteFile(filename, dds, ddssize);
1193 return ret ? ddssize : -5;
1196 rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
1198 int i, size, dds_flags, dds_format_flags, dds_miplevels, dds_width, dds_height, textype;
1199 int bytesperblock, bytesperpixel;
1202 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
1203 textypeinfo_t *texinfo;
1204 int mip, mipwidth, mipheight, mipsize;
1206 GLint oldbindtexnum;
1207 const unsigned char *mippixels, *ddspixels;
1209 fs_offset_t ddsfilesize;
1210 unsigned int ddssize;
1212 if (cls.state == ca_dedicated)
1215 dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
1216 ddssize = ddsfilesize;
1219 return NULL; // not found
1221 if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
1224 Con_Printf("^1%s: not a DDS image\n", filename);
1228 dds_flags = BuffLittleLong(dds+8);
1229 dds_format_flags = BuffLittleLong(dds+80);
1230 dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
1231 dds_width = BuffLittleLong(dds+16);
1232 dds_height = BuffLittleLong(dds+12);
1233 ddspixels = dds + 128;
1235 flags &= ~TEXF_ALPHA;
1236 if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
1238 // very sloppy BGRA 32bit identification
1239 textype = TEXTYPE_BGRA;
1242 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
1243 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1246 Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
1250 for (i = 3;i < size;i += 4)
1251 if (ddspixels[i] < 255)
1254 flags &= ~TEXF_ALPHA;
1256 else if (!memcmp(dds+84, "DXT1", 4))
1258 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1259 // LordHavoc: it is my belief that this does not infringe on the
1260 // patent because it is not decoding pixels...
1261 textype = TEXTYPE_DXT1;
1264 //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1265 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1266 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1269 Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
1272 for (i = 0;i < size;i += bytesperblock)
1273 if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1276 textype = TEXTYPE_DXT1A;
1278 flags &= ~TEXF_ALPHA;
1280 else if (!memcmp(dds+84, "DXT3", 4))
1282 textype = TEXTYPE_DXT3;
1285 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1286 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1289 Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
1293 else if (!memcmp(dds+84, "DXT5", 4))
1295 textype = TEXTYPE_DXT5;
1298 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1299 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1302 Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
1309 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1313 // return whether this texture is transparent
1315 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1317 // calculate average color if requested
1321 Vector4Clear(avgcolor);
1324 for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1326 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1327 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1328 avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
1329 avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
1331 f = (float)bytesperblock / size;
1332 avgcolor[0] *= (0.5f / 31.0f) * f;
1333 avgcolor[1] *= (0.5f / 63.0f) * f;
1334 avgcolor[2] *= (0.5f / 31.0f) * f;
1335 avgcolor[3] = 1; // too hard to calculate
1339 for (i = 0;i < size;i += 4)
1341 avgcolor[0] += ddspixels[i+2];
1342 avgcolor[1] += ddspixels[i+1];
1343 avgcolor[2] += ddspixels[i];
1344 avgcolor[3] += ddspixels[i+3];
1346 f = (1.0f / 255.0f) * bytesperpixel / size;
1354 if (dds_miplevels > 1)
1355 flags |= TEXF_MIPMAP;
1357 flags &= ~TEXF_MIPMAP;
1359 // if S3TC is not supported, there's very little we can do about it
1360 if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1363 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1367 texinfo = R_GetTexTypeInfo(textype, flags);
1369 glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1370 strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1372 glt->chain = pool->gltchain;
1373 pool->gltchain = glt;
1374 glt->inputwidth = dds_width;
1375 glt->inputheight = dds_height;
1376 glt->inputdepth = 1;
1378 glt->textype = texinfo;
1379 glt->texturetype = GLTEXTURETYPE_2D;
1380 glt->inputdatasize = ddssize;
1381 glt->glinternalformat = texinfo->glinternalformat;
1382 glt->glformat = texinfo->glformat;
1383 glt->gltype = texinfo->gltype;
1384 glt->bytesperpixel = texinfo->internalbytesperpixel;
1386 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1387 glt->tilewidth = dds_width;
1388 glt->tileheight = dds_height;
1391 // texture uploading can take a while, so make sure we're sending keepalives
1392 CL_KeepaliveMessage(false);
1394 // upload the texture
1395 // we need to restore the texture binding after finishing the upload
1397 GL_ActiveTexture(0);
1398 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1399 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1400 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1401 mippixels = ddspixels;
1402 mipwidth = dds_width;
1403 mipheight = dds_height;
1404 mipcomplete = false;
1405 for (mip = 0;mip < dds_miplevels+1;mip++)
1407 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1408 if (mippixels + mipsize > dds + ddssize)
1412 qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1416 qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1418 mippixels += mipsize;
1419 if (mipwidth <= 1 && mipheight <= 1)
1429 if (dds_miplevels > 1 && !mipcomplete)
1431 // need to set GL_TEXTURE_MAX_LEVEL
1432 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1434 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1435 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1438 return (rtexture_t *)glt;
1441 int R_TextureWidth(rtexture_t *rt)
1443 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1446 int R_TextureHeight(rtexture_t *rt)
1448 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1451 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1453 gltexture_t *glt = (gltexture_t *)rt;
1455 Host_Error("R_UpdateTexture: no data supplied");
1457 Host_Error("R_UpdateTexture: no texture supplied");
1459 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1460 // update part of the texture
1461 if (glt->bufferpixels)
1464 int bpp = glt->bytesperpixel;
1465 int inputskip = width*bpp;
1466 int outputskip = glt->tilewidth*bpp;
1467 const unsigned char *input = data;
1468 unsigned char *output = glt->bufferpixels;
1478 input -= y*inputskip;
1481 if (width > glt->tilewidth - x)
1482 width = glt->tilewidth - x;
1483 if (height > glt->tileheight - y)
1484 height = glt->tileheight - y;
1485 if (width < 1 || height < 1)
1488 glt->buffermodified = true;
1489 output += y*outputskip + x*bpp;
1490 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1491 memcpy(output, input, width*bpp);
1494 R_Upload(glt, data, x, y, 0, width, height, 1);
1497 int R_RealGetTexture(rtexture_t *rt)
1502 glt = (gltexture_t *)rt;
1503 if (glt->flags & GLTEXF_DYNAMIC)
1504 R_UpdateDynamicTexture(glt);
1505 if (glt->buffermodified && glt->bufferpixels)
1507 glt->buffermodified = false;
1508 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1517 void R_ClearTexture (rtexture_t *rt)
1519 gltexture_t *glt = (gltexture_t *)rt;
1521 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );