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 gl_picmip_world = {CVAR_SAVE, "gl_picmip_world", "0", "extra picmip level for world textures (may be negative, which will then reduce gl_picmip for these)"};
12 cvar_t r_picmipworld = {CVAR_SAVE, "r_picmipworld", "1", "whether gl_picmip shall apply to world textures too (setting this to 0 is a shorthand for gl_picmip_world -9999999)"};
13 cvar_t gl_picmip_sprites = {CVAR_SAVE, "gl_picmip_sprites", "0", "extra picmip level for sprite textures (may be negative, which will then reduce gl_picmip for these)"};
14 cvar_t r_picmipsprites = {CVAR_SAVE, "r_picmipsprites", "1", "make gl_picmip affect sprites too (saves some graphics memory in sprite heavy games) (setting this to 0 is a shorthand for gl_picmip_sprites -9999999)"};
15 cvar_t gl_picmip_other = {CVAR_SAVE, "gl_picmip_other", "0", "extra picmip level for other textures (may be negative, which will then reduce gl_picmip for these)"};
16 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)"};
17 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"};
18 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"};
19 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
20 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
21 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
22 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
23 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
24 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
25 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)"};
26 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
27 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
28 cvar_t gl_texturecompression_reflectmask = {CVAR_SAVE, "gl_texturecompression_reflectmask", "1", "whether to compress reflection cubemap masks (mask of which areas of the texture should reflect the generic shiny cubemap)"};
29 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"};
31 qboolean gl_filter_force = false;
32 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
33 int gl_filter_mag = GL_LINEAR;
36 static mempool_t *texturemempool;
37 static memexpandablearray_t texturearray;
39 // note: this must not conflict with TEXF_ flags in r_textures.h
40 // bitmask for mismatch checking
41 #define GLTEXF_IMPORTANTBITS (0)
42 // dynamic texture (treat texnum == 0 differently)
43 #define GLTEXF_DYNAMIC 0x00080000
45 typedef struct textypeinfo_s
48 int inputbytesperpixel;
49 int internalbytesperpixel;
50 float glinternalbytesperpixel;
58 static textypeinfo_t textype_palette = {TEXTYPE_PALETTE , 1, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
59 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE , 1, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
60 static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, 3 , GL_RGBA , GL_UNSIGNED_BYTE };
61 static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, 4 , GL_RGBA , GL_UNSIGNED_BYTE };
62 static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
63 static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA , GL_UNSIGNED_BYTE };
64 static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
65 static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
66 static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
67 static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_BGRA , GL_UNSIGNED_BYTE };
68 static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
69 static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
70 static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , GL_ALPHA , GL_UNSIGNED_BYTE };
71 static textypeinfo_t textype_dxt1 = {TEXTYPE_DXT1 , 4, 0, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , 0 , 0 };
72 static textypeinfo_t textype_dxt1a = {TEXTYPE_DXT1A , 4, 0, 0.5f, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0 , 0 };
73 static textypeinfo_t textype_dxt3 = {TEXTYPE_DXT3 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0 , 0 };
74 static textypeinfo_t textype_dxt5 = {TEXTYPE_DXT5 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0 , 0 };
75 static textypeinfo_t textype_colorbuffer = {TEXTYPE_COLORBUFFER, 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
78 typedef enum gltexturetype_e
82 GLTEXTURETYPE_CUBEMAP,
83 GLTEXTURETYPE_RECTANGLE,
88 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
89 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
90 static int cubemapside[6] =
92 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
93 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
94 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
95 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
96 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
97 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
100 typedef struct gltexture_s
102 // this portion of the struct is exposed to the R_GetTexture macro for
103 // speed reasons, must be identical in rtexture_t!
104 int texnum; // GL texture slot number
105 qboolean dirty; // indicates that R_RealGetTexture should be called
106 int gltexturetypeenum; // used by R_Mesh_TexBind
108 // dynamic texture stuff [11/22/2007 Black]
109 updatecallback_t updatecallback;
110 void *updatacallback_data;
111 // --- [11/22/2007 Black]
113 // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
114 unsigned char *bufferpixels;
115 qboolean buffermodified;
117 // pointer to texturepool (check this to see if the texture is allocated)
118 struct gltexturepool_s *pool;
119 // pointer to next texture in texturepool chain
120 struct gltexture_s *chain;
121 // name of the texture (this might be removed someday), no duplicates
122 char identifier[MAX_QPATH + 32];
123 // original data size in *inputtexels
124 int inputwidth, inputheight, inputdepth;
125 // copy of the original texture(s) supplied to the upload function, for
126 // delayed uploads (non-precached)
127 unsigned char *inputtexels;
128 // original data size in *inputtexels
130 // flags supplied to the LoadTexture function
131 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
135 // pointer to one of the textype_ structs
136 textypeinfo_t *textype;
137 // one of the GLTEXTURETYPE_ values
139 // palette if the texture is TEXTYPE_PALETTE
140 const unsigned int *palette;
141 // actual stored texture size after gl_picmip and gl_max_size are applied
142 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
143 int tilewidth, tileheight, tiledepth;
144 // 1 or 6 depending on texturetype
148 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
151 int glinternalformat;
152 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
157 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
159 typedef struct gltexturepool_s
161 unsigned int sentinel;
162 struct gltexture_s *gltchain;
163 struct gltexturepool_s *next;
167 static gltexturepool_t *gltexturepoolchain = NULL;
169 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
170 static int resizebuffersize = 0;
171 static const unsigned char *texturebuffer;
173 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
178 return &textype_dxt1;
180 return &textype_dxt1a;
182 return &textype_dxt3;
184 return &textype_dxt5;
185 case TEXTYPE_PALETTE:
186 return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
188 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
189 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
190 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
192 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
193 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
194 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
196 return &textype_alpha;
197 case TEXTYPE_SHADOWMAP:
198 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
199 case TEXTYPE_COLORBUFFER:
200 return &textype_colorbuffer;
202 Host_Error("R_GetTexTypeInfo: unknown texture format");
208 // dynamic texture code [11/22/2007 Black]
209 void R_MarkDirtyTexture(rtexture_t *rt) {
210 gltexture_t *glt = (gltexture_t*) rt;
215 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
216 if (glt->flags & GLTEXF_DYNAMIC)
218 // mark it as dirty, so R_RealGetTexture gets called
223 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
224 gltexture_t *glt = (gltexture_t*) rt;
229 glt->flags |= GLTEXF_DYNAMIC;
230 glt->updatecallback = updatecallback;
231 glt->updatacallback_data = data;
234 static void R_UpdateDynamicTexture(gltexture_t *glt) {
236 if( glt->updatecallback ) {
237 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
241 void R_PurgeTexture(rtexture_t *rt)
243 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
248 void R_FreeTexture(rtexture_t *rt)
250 gltexture_t *glt, **gltpointer;
252 glt = (gltexture_t *)rt;
254 Host_Error("R_FreeTexture: texture == NULL");
256 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
257 if (*gltpointer == glt)
258 *gltpointer = glt->chain;
260 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
265 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
268 if (glt->inputtexels)
269 Mem_Free(glt->inputtexels);
270 Mem_ExpandableArray_FreeRecord(&texturearray, glt);
273 rtexturepool_t *R_AllocTexturePool(void)
275 gltexturepool_t *pool;
276 if (texturemempool == NULL)
278 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
281 pool->next = gltexturepoolchain;
282 gltexturepoolchain = pool;
283 pool->sentinel = TEXTUREPOOL_SENTINEL;
284 return (rtexturepool_t *)pool;
287 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
289 gltexturepool_t *pool, **poolpointer;
290 if (rtexturepool == NULL)
292 if (*rtexturepool == NULL)
294 pool = (gltexturepool_t *)(*rtexturepool);
295 *rtexturepool = NULL;
296 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
297 Host_Error("R_FreeTexturePool: pool already freed");
298 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
299 if (*poolpointer == pool)
300 *poolpointer = pool->next;
302 Host_Error("R_FreeTexturePool: pool not linked");
303 while (pool->gltchain)
304 R_FreeTexture((rtexture_t *)pool->gltchain);
309 typedef struct glmode_s
312 int minification, magnification;
316 static glmode_t modes[6] =
318 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
319 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
320 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
321 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
322 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
323 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
326 static void GL_TextureMode_f (void)
331 gltexturepool_t *pool;
335 Con_Printf("Texture mode is %sforced\n", gl_filter_force ? "" : "not ");
336 for (i = 0;i < 6;i++)
338 if (gl_filter_min == modes[i].minification)
340 Con_Printf("%s\n", modes[i].name);
344 Con_Print("current filter is unknown???\n");
348 for (i = 0;i < (int)(sizeof(modes)/sizeof(*modes));i++)
349 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
353 Con_Print("bad filter name\n");
357 gl_filter_min = modes[i].minification;
358 gl_filter_mag = modes[i].magnification;
359 gl_filter_force = ((Cmd_Argc() > 2) && !strcasecmp(Cmd_Argv(2), "force"));
361 // change all the existing mipmap texture objects
362 // FIXME: force renderer(/client/something?) restart instead?
365 for (pool = gltexturepoolchain;pool;pool = pool->next)
367 for (glt = pool->gltchain;glt;glt = glt->chain)
369 // only update already uploaded images
370 if (glt->texnum && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
372 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
373 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
374 if (glt->flags & TEXF_MIPMAP)
376 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
380 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
382 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
383 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
389 static void GL_Texture_CalcImageSize(int texturetype, int flags, int miplevel, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
391 int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
396 case GLTEXTURETYPE_2D:
397 maxsize = vid.maxtexturesize_2d;
398 if (flags & TEXF_PICMIP)
400 maxsize = bound(1, gl_max_size.integer, maxsize);
404 case GLTEXTURETYPE_3D:
405 maxsize = vid.maxtexturesize_3d;
407 case GLTEXTURETYPE_CUBEMAP:
408 maxsize = vid.maxtexturesize_cubemap;
414 if (vid.support.arb_texture_non_power_of_two)
415 width2 = min(inwidth >> picmip, maxsize);
418 for (width2 = 1;width2 < inwidth;width2 <<= 1);
419 for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
421 *outwidth = max(1, width2);
425 if (vid.support.arb_texture_non_power_of_two)
426 height2 = min(inheight >> picmip, maxsize);
429 for (height2 = 1;height2 < inheight;height2 <<= 1);
430 for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
432 *outheight = max(1, height2);
436 if (vid.support.arb_texture_non_power_of_two)
437 depth2 = min(indepth >> picmip, maxsize);
440 for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
441 for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
443 *outdepth = max(1, depth2);
448 static int R_CalcTexelDataSize (gltexture_t *glt)
450 int width2, height2, depth2, size;
452 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
454 size = width2 * height2 * depth2;
456 if (glt->flags & TEXF_MIPMAP)
458 while (width2 > 1 || height2 > 1 || depth2 > 1)
466 size += width2 * height2 * depth2;
470 return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
473 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
477 int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
478 int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
480 gltexturepool_t *pool;
482 Con_Print("glsize input loaded mip alpha name\n");
483 for (pool = gltexturepoolchain;pool;pool = pool->next)
491 for (glt = pool->gltchain;glt;glt = glt->chain)
493 glsize = R_CalcTexelDataSize(glt);
494 isloaded = glt->texnum != 0;
496 pooltotalt += glsize;
497 pooltotalp += glt->inputdatasize;
501 poolloadedt += glsize;
502 poolloadedp += glt->inputdatasize;
505 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);
508 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);
509 sumtotal += pooltotal;
510 sumtotalt += pooltotalt;
511 sumtotalp += pooltotalp;
512 sumloaded += poolloaded;
513 sumloadedt += poolloadedt;
514 sumloadedp += poolloadedp;
517 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);
520 static void R_TextureStats_f(void)
522 R_TextureStats_Print(true, true, true);
525 static void r_textures_start(void)
527 // LordHavoc: allow any alignment
529 qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
530 qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
532 texturemempool = Mem_AllocPool("texture management", 0, NULL);
533 Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
535 // Disable JPEG screenshots if the DLL isn't loaded
536 if (! JPEG_OpenLibrary ())
537 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
538 if (! PNG_OpenLibrary ())
539 Cvar_SetValueQuick (&scr_screenshot_png, 0);
542 static void r_textures_shutdown(void)
544 rtexturepool_t *temp;
546 JPEG_CloseLibrary ();
548 while(gltexturepoolchain)
550 temp = (rtexturepool_t *) gltexturepoolchain;
551 R_FreeTexturePool(&temp);
554 resizebuffersize = 0;
556 colorconvertbuffer = NULL;
557 texturebuffer = NULL;
558 Mem_ExpandableArray_FreeArray(&texturearray);
559 Mem_FreePool(&texturemempool);
562 static void r_textures_newmap(void)
566 void R_Textures_Init (void)
568 Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc); an additional argument 'force' forces the texture mode even in cases where it may not be appropriate");
569 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
570 Cvar_RegisterVariable (&gl_max_size);
571 Cvar_RegisterVariable (&gl_picmip);
572 Cvar_RegisterVariable (&gl_picmip_world);
573 Cvar_RegisterVariable (&r_picmipworld);
574 Cvar_RegisterVariable (&gl_picmip_sprites);
575 Cvar_RegisterVariable (&r_picmipsprites);
576 Cvar_RegisterVariable (&gl_picmip_other);
577 Cvar_RegisterVariable (&gl_max_lightmapsize);
578 Cvar_RegisterVariable (&r_lerpimages);
579 Cvar_RegisterVariable (&gl_texture_anisotropy);
580 Cvar_RegisterVariable (&gl_texturecompression);
581 Cvar_RegisterVariable (&gl_texturecompression_color);
582 Cvar_RegisterVariable (&gl_texturecompression_normal);
583 Cvar_RegisterVariable (&gl_texturecompression_gloss);
584 Cvar_RegisterVariable (&gl_texturecompression_glow);
585 Cvar_RegisterVariable (&gl_texturecompression_2d);
586 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
587 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
588 Cvar_RegisterVariable (&gl_texturecompression_sky);
589 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
590 Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
591 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
593 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
596 void R_Textures_Frame (void)
598 static int old_aniso = 0;
600 // could do procedural texture animation here, if we keep track of which
601 // textures were accessed this frame...
603 // free the resize buffers
604 resizebuffersize = 0;
607 Mem_Free(resizebuffer);
610 if (colorconvertbuffer)
612 Mem_Free(colorconvertbuffer);
613 colorconvertbuffer = NULL;
616 if (old_aniso != gl_texture_anisotropy.integer)
619 gltexturepool_t *pool;
622 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
624 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
628 for (pool = gltexturepoolchain;pool;pool = pool->next)
630 for (glt = pool->gltchain;glt;glt = glt->chain)
632 // only update already uploaded images
633 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
635 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
637 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
638 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
640 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
647 void R_MakeResizeBufferBigger(int size)
649 if (resizebuffersize < size)
651 resizebuffersize = size;
653 Mem_Free(resizebuffer);
654 if (colorconvertbuffer)
655 Mem_Free(colorconvertbuffer);
656 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
657 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
658 if (!resizebuffer || !colorconvertbuffer)
659 Host_Error("R_Upload: out of memory");
663 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
665 int textureenum = gltexturetypeenums[texturetype];
666 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
670 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
672 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
673 if (gl_texture_anisotropy.integer != aniso)
674 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
675 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
677 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
678 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
679 if (gltexturetypedimensions[texturetype] >= 3)
681 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
685 if (!gl_filter_force && flags & TEXF_FORCENEAREST)
687 if (flags & TEXF_MIPMAP)
689 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
693 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
695 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
697 else if (!gl_filter_force && flags & TEXF_FORCELINEAR)
699 if (flags & TEXF_MIPMAP)
701 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
703 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
707 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
712 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
714 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
718 if (flags & TEXF_MIPMAP)
720 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
724 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
726 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
729 if (textype == TEXTYPE_SHADOWMAP)
731 if (vid.support.arb_shadow)
733 if (flags & TEXF_COMPARE)
735 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
739 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
741 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
743 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
749 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
751 int i, mip, width, height, depth;
753 const unsigned char *prevbuffer;
758 // we need to restore the texture binding after finishing the upload
760 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
761 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
763 // these are rounded up versions of the size to do better resampling
764 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
766 width = glt->inputwidth;
767 height = glt->inputheight;
768 depth = glt->inputdepth;
772 for (width = 1;width < glt->inputwidth ;width <<= 1);
773 for (height = 1;height < glt->inputheight;height <<= 1);
774 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
777 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
778 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
780 if (prevbuffer == NULL)
782 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
783 prevbuffer = resizebuffer;
785 else if (glt->textype->textype == TEXTYPE_PALETTE)
787 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
788 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
789 prevbuffer = colorconvertbuffer;
792 // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
794 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))
796 // update a portion of the image
797 switch(glt->texturetype)
799 case GLTEXTURETYPE_2D:
800 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
802 case GLTEXTURETYPE_3D:
803 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
806 Host_Error("R_Upload: partial update of type other than 2D");
812 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
813 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
815 // cubemaps contain multiple images and thus get processed a bit differently
816 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
818 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
820 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
821 prevbuffer = resizebuffer;
824 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
826 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
827 prevbuffer = resizebuffer;
831 if (qglGetCompressedTexImageARB)
833 if (gl_texturecompression.integer >= 2)
834 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
836 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
839 switch(glt->texturetype)
841 case GLTEXTURETYPE_2D:
842 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
843 if (glt->flags & TEXF_MIPMAP)
845 while (width > 1 || height > 1 || depth > 1)
847 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
848 prevbuffer = resizebuffer;
849 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
853 case GLTEXTURETYPE_3D:
854 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
855 if (glt->flags & TEXF_MIPMAP)
857 while (width > 1 || height > 1 || depth > 1)
859 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
860 prevbuffer = resizebuffer;
861 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
865 case GLTEXTURETYPE_CUBEMAP:
866 // convert and upload each side in turn,
867 // from a continuous block of input texels
868 texturebuffer = (unsigned char *)prevbuffer;
869 for (i = 0;i < 6;i++)
871 prevbuffer = texturebuffer;
872 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
873 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
875 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
876 prevbuffer = resizebuffer;
879 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
881 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
882 prevbuffer = resizebuffer;
885 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
886 if (glt->flags & TEXF_MIPMAP)
888 while (width > 1 || height > 1 || depth > 1)
890 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
891 prevbuffer = resizebuffer;
892 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
897 case GLTEXTURETYPE_RECTANGLE:
898 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
901 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
903 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
906 static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, int sides, int flags, int miplevel, textype_t textype, int texturetype, const unsigned char *data, const unsigned int *palette)
910 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
911 textypeinfo_t *texinfo, *texinfo2;
913 if (cls.state == ca_dedicated)
916 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
918 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
921 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
923 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
926 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
928 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
932 texinfo = R_GetTexTypeInfo(textype, flags);
933 size = width * height * depth * sides * texinfo->inputbytesperpixel;
936 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
940 // clear the alpha flag if the texture has no transparent pixels
943 case TEXTYPE_PALETTE:
944 if (flags & TEXF_ALPHA)
946 flags &= ~TEXF_ALPHA;
949 for (i = 0;i < size;i++)
951 if (((unsigned char *)&palette[data[i]])[3] < 255)
962 if (flags & TEXF_ALPHA)
964 flags &= ~TEXF_ALPHA;
967 for (i = 3;i < size;i += 4)
978 case TEXTYPE_SHADOWMAP:
990 case TEXTYPE_COLORBUFFER:
994 Host_Error("R_LoadTexture: unknown texture type");
997 texinfo2 = R_GetTexTypeInfo(textype, flags);
998 if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
1001 Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
1003 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
1005 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1007 glt->chain = pool->gltchain;
1008 pool->gltchain = glt;
1009 glt->inputwidth = width;
1010 glt->inputheight = height;
1011 glt->inputdepth = depth;
1013 glt->miplevel = (miplevel < 0) ? R_PicmipForFlags(flags) : miplevel; // note: if miplevel is -1, we know the texture is in original size and we can picmip it normally
1014 glt->textype = texinfo;
1015 glt->texturetype = texturetype;
1016 glt->inputdatasize = size;
1017 glt->palette = palette;
1018 glt->glinternalformat = texinfo->glinternalformat;
1019 glt->glformat = texinfo->glformat;
1020 glt->gltype = texinfo->gltype;
1021 glt->bytesperpixel = texinfo->internalbytesperpixel;
1022 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1025 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1026 // init the dynamic texture attributes, too [11/22/2007 Black]
1027 glt->updatecallback = NULL;
1028 glt->updatacallback_data = NULL;
1030 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1032 // upload the texture
1033 // data may be NULL (blank texture for dynamic rendering)
1035 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1036 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1037 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1038 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1040 // texture converting and uploading can take a while, so make sure we're sending keepalives
1041 CL_KeepaliveMessage(false);
1043 return (rtexture_t *)glt;
1046 rtexture_t *R_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
1048 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, miplevel, textype, GLTEXTURETYPE_2D, data, palette);
1051 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, int miplevel, const unsigned int *palette)
1053 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, miplevel, textype, GLTEXTURETYPE_3D, data, palette);
1056 rtexture_t *R_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
1058 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, miplevel, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1061 rtexture_t *R_LoadTextureRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
1063 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, miplevel, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1066 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1068 int flags = TEXF_CLAMP;
1070 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1072 flags |= TEXF_FORCENEAREST;
1073 if (precision <= 16)
1074 flags |= TEXF_LOWPRECISION;
1078 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1080 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), -1, TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1083 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1085 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), -1, TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1088 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1090 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), -1, TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1093 int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
1095 gltexture_t *glt = (gltexture_t *)rt;
1098 int bytesperpixel = 0;
1099 int bytesperblock = 0;
1101 int dds_format_flags;
1109 GLint internalformat;
1110 const char *ddsfourcc;
1112 return -1; // NULL pointer
1113 if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
1114 return -2; // broken driver - crashes on reading internal format
1115 if (!qglGetTexLevelParameteriv)
1117 GL_ActiveTexture(0);
1118 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1119 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1120 qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
1121 switch(internalformat)
1123 default: ddsfourcc = NULL;bytesperpixel = 4;break;
1124 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1125 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
1126 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
1127 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
1129 if (!bytesperblock && skipuncompressed)
1130 return -3; // skipped
1131 memset(mipinfo, 0, sizeof(mipinfo));
1132 mipinfo[0][0] = glt->tilewidth;
1133 mipinfo[0][1] = glt->tileheight;
1135 if (glt->flags & TEXF_MIPMAP)
1137 for (mip = 1;mip < 16;mip++)
1139 mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
1140 mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
1141 if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
1149 for (mip = 0;mip < mipmaps;mip++)
1151 mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
1152 mipinfo[mip][3] = ddssize;
1153 ddssize += mipinfo[mip][2];
1155 dds = Mem_Alloc(tempmempool, ddssize);
1158 dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
1162 dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
1163 dds_format_flags = 0x4; // DDPF_FOURCC
1167 dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
1168 dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
1172 dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
1173 dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
1175 memcpy(dds, "DDS ", 4);
1176 StoreLittleLong(dds+4, ddssize);
1177 StoreLittleLong(dds+8, dds_flags);
1178 StoreLittleLong(dds+12, mipinfo[0][1]); // height
1179 StoreLittleLong(dds+16, mipinfo[0][0]); // width
1180 StoreLittleLong(dds+24, 1); // depth
1181 StoreLittleLong(dds+28, mipmaps); // mipmaps
1182 StoreLittleLong(dds+76, 32); // format size
1183 StoreLittleLong(dds+80, dds_format_flags);
1184 StoreLittleLong(dds+108, dds_caps1);
1185 StoreLittleLong(dds+112, dds_caps2);
1188 StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
1189 memcpy(dds+84, ddsfourcc, 4);
1190 for (mip = 0;mip < mipmaps;mip++)
1192 qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
1197 StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
1198 StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
1199 dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
1200 for (mip = 0;mip < mipmaps;mip++)
1202 qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
1205 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1206 ret = FS_WriteFile(filename, dds, ddssize);
1208 return ret ? ddssize : -5;
1211 rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor, int miplevel) // DDS textures are opaque, so miplevel isn't a pointer but just seen as a hint
1213 int i, size, dds_format_flags, dds_miplevels, dds_width, dds_height;
1216 int bytesperblock, bytesperpixel;
1219 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
1220 textypeinfo_t *texinfo;
1221 int mip, mipwidth, mipheight, mipsize;
1223 GLint oldbindtexnum;
1224 const unsigned char *mippixels, *ddspixels;
1226 fs_offset_t ddsfilesize;
1227 unsigned int ddssize;
1229 if (cls.state == ca_dedicated)
1232 dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
1233 ddssize = ddsfilesize;
1237 Log_Printf("ddstexturefailures.log", "%s\n", filename);
1238 return NULL; // not found
1241 if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
1244 Con_Printf("^1%s: not a DDS image\n", filename);
1248 //dds_flags = BuffLittleLong(dds+8);
1249 dds_format_flags = BuffLittleLong(dds+80);
1250 dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
1251 dds_width = BuffLittleLong(dds+16);
1252 dds_height = BuffLittleLong(dds+12);
1253 ddspixels = dds + 128;
1255 flags &= ~TEXF_ALPHA;
1256 if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
1258 // very sloppy BGRA 32bit identification
1259 textype = TEXTYPE_BGRA;
1262 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
1263 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1266 Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
1270 for (i = 3;i < size;i += 4)
1271 if (ddspixels[i] < 255)
1274 flags &= ~TEXF_ALPHA;
1276 else if (!memcmp(dds+84, "DXT1", 4))
1278 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1279 // LordHavoc: it is my belief that this does not infringe on the
1280 // patent because it is not decoding pixels...
1281 textype = TEXTYPE_DXT1;
1284 //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
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 DXT1 DDS image\n", filename);
1292 for (i = 0;i < size;i += bytesperblock)
1293 if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1296 textype = TEXTYPE_DXT1A;
1298 flags &= ~TEXF_ALPHA;
1300 else if (!memcmp(dds+84, "DXT3", 4))
1302 textype = TEXTYPE_DXT3;
1305 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1306 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1309 Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
1313 else if (!memcmp(dds+84, "DXT5", 4))
1315 textype = TEXTYPE_DXT5;
1318 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1319 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1322 Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
1329 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1333 // return whether this texture is transparent
1335 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1337 // calculate average color if requested
1341 Vector4Clear(avgcolor);
1344 for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1346 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1347 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1348 avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
1349 avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
1351 f = (float)bytesperblock / size;
1352 avgcolor[0] *= (0.5f / 31.0f) * f;
1353 avgcolor[1] *= (0.5f / 63.0f) * f;
1354 avgcolor[2] *= (0.5f / 31.0f) * f;
1355 avgcolor[3] = 1; // too hard to calculate
1359 for (i = 0;i < size;i += 4)
1361 avgcolor[0] += ddspixels[i+2];
1362 avgcolor[1] += ddspixels[i+1];
1363 avgcolor[2] += ddspixels[i];
1364 avgcolor[3] += ddspixels[i+3];
1366 f = (1.0f / 255.0f) * bytesperpixel / size;
1374 // this is where we apply gl_picmip
1375 mippixels = ddspixels;
1376 mipwidth = dds_width;
1377 mipheight = dds_height;
1378 while(miplevel >= 1 && dds_miplevels >= 1)
1380 if (mipwidth <= 1 && mipheight <= 1)
1382 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1383 mippixels += mipsize; // just skip
1392 if (dds_miplevels > 1)
1393 flags |= TEXF_MIPMAP;
1395 flags &= ~TEXF_MIPMAP;
1397 // if S3TC is not supported, there's very little we can do about it
1398 if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1401 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1405 texinfo = R_GetTexTypeInfo(textype, flags);
1407 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
1408 strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1410 glt->chain = pool->gltchain;
1411 pool->gltchain = glt;
1412 glt->inputwidth = mipwidth;
1413 glt->inputheight = mipheight;
1414 glt->inputdepth = 1;
1416 glt->textype = texinfo;
1417 glt->texturetype = GLTEXTURETYPE_2D;
1418 glt->inputdatasize = ddssize;
1419 glt->glinternalformat = texinfo->glinternalformat;
1420 glt->glformat = texinfo->glformat;
1421 glt->gltype = texinfo->gltype;
1422 glt->bytesperpixel = texinfo->internalbytesperpixel;
1424 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1425 glt->tilewidth = mipwidth;
1426 glt->tileheight = mipheight;
1429 // texture uploading can take a while, so make sure we're sending keepalives
1430 CL_KeepaliveMessage(false);
1432 // upload the texture
1433 // we need to restore the texture binding after finishing the upload
1435 GL_ActiveTexture(0);
1436 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1437 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1438 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1439 mipcomplete = false;
1441 for (mip = 0;mip <= dds_miplevels;mip++) // <= to include the not-counted "largest" miplevel
1443 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1444 if (mippixels + mipsize > dds + ddssize)
1448 qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1452 qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1454 mippixels += mipsize;
1455 if (mipwidth <= 1 && mipheight <= 1)
1465 if (dds_miplevels >= 1 && !mipcomplete)
1467 // need to set GL_TEXTURE_MAX_LEVEL
1468 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1470 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1471 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1474 return (rtexture_t *)glt;
1477 int R_TextureWidth(rtexture_t *rt)
1479 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1482 int R_TextureHeight(rtexture_t *rt)
1484 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1487 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1489 gltexture_t *glt = (gltexture_t *)rt;
1491 Host_Error("R_UpdateTexture: no data supplied");
1493 Host_Error("R_UpdateTexture: no texture supplied");
1495 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1496 // update part of the texture
1497 if (glt->bufferpixels)
1500 int bpp = glt->bytesperpixel;
1501 int inputskip = width*bpp;
1502 int outputskip = glt->tilewidth*bpp;
1503 const unsigned char *input = data;
1504 unsigned char *output = glt->bufferpixels;
1514 input -= y*inputskip;
1517 if (width > glt->tilewidth - x)
1518 width = glt->tilewidth - x;
1519 if (height > glt->tileheight - y)
1520 height = glt->tileheight - y;
1521 if (width < 1 || height < 1)
1524 glt->buffermodified = true;
1525 output += y*outputskip + x*bpp;
1526 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1527 memcpy(output, input, width*bpp);
1530 R_Upload(glt, data, x, y, 0, width, height, 1);
1533 int R_RealGetTexture(rtexture_t *rt)
1538 glt = (gltexture_t *)rt;
1539 if (glt->flags & GLTEXF_DYNAMIC)
1540 R_UpdateDynamicTexture(glt);
1541 if (glt->buffermodified && glt->bufferpixels)
1543 glt->buffermodified = false;
1544 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1553 void R_ClearTexture (rtexture_t *rt)
1555 gltexture_t *glt = (gltexture_t *)rt;
1557 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );
1560 int R_PicmipForFlags(int flags)
1563 if(flags & TEXF_PICMIP)
1565 miplevel += gl_picmip.integer;
1566 if (flags & TEXF_ISWORLD)
1568 if (r_picmipworld.integer)
1569 miplevel += gl_picmip_world.integer;
1573 else if (flags & TEXF_ISSPRITE)
1575 if (r_picmipsprites.integer)
1576 miplevel += gl_picmip_sprites.integer;
1581 miplevel += gl_picmip_other.integer;