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_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)"};
24 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"};
26 qboolean gl_filter_force = false;
27 int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
28 int gl_filter_mag = GL_LINEAR;
31 static mempool_t *texturemempool;
32 static memexpandablearray_t texturearray;
34 // note: this must not conflict with TEXF_ flags in r_textures.h
35 // bitmask for mismatch checking
36 #define GLTEXF_IMPORTANTBITS (0)
37 // dynamic texture (treat texnum == 0 differently)
38 #define GLTEXF_DYNAMIC 0x00080000
40 typedef struct textypeinfo_s
43 int inputbytesperpixel;
44 int internalbytesperpixel;
45 float glinternalbytesperpixel;
53 static textypeinfo_t textype_palette = {TEXTYPE_PALETTE , 1, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
54 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE , 1, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
55 static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, 3 , GL_RGBA , GL_UNSIGNED_BYTE };
56 static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, 4 , GL_RGBA , GL_UNSIGNED_BYTE };
57 static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
58 static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA , GL_UNSIGNED_BYTE };
59 static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
60 static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
61 static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
62 static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_BGRA , GL_UNSIGNED_BYTE };
63 static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
64 static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
65 static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , GL_ALPHA , GL_UNSIGNED_BYTE };
66 static textypeinfo_t textype_dxt1 = {TEXTYPE_DXT1 , 4, 0, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , 0 , 0 };
67 static textypeinfo_t textype_dxt1a = {TEXTYPE_DXT1A , 4, 0, 0.5f, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0 , 0 };
68 static textypeinfo_t textype_dxt3 = {TEXTYPE_DXT3 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0 , 0 };
69 static textypeinfo_t textype_dxt5 = {TEXTYPE_DXT5 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0 , 0 };
70 static textypeinfo_t textype_colorbuffer = {TEXTYPE_COLORBUFFER, 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
73 typedef enum gltexturetype_e
77 GLTEXTURETYPE_CUBEMAP,
78 GLTEXTURETYPE_RECTANGLE,
83 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
84 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
85 static int cubemapside[6] =
87 GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
88 GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
89 GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
90 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
91 GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
92 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
95 typedef struct gltexture_s
97 // this portion of the struct is exposed to the R_GetTexture macro for
98 // speed reasons, must be identical in rtexture_t!
99 int texnum; // GL texture slot number
100 qboolean dirty; // indicates that R_RealGetTexture should be called
101 int gltexturetypeenum; // used by R_Mesh_TexBind
103 // dynamic texture stuff [11/22/2007 Black]
104 updatecallback_t updatecallback;
105 void *updatacallback_data;
106 // --- [11/22/2007 Black]
108 // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
109 unsigned char *bufferpixels;
110 qboolean buffermodified;
112 // pointer to texturepool (check this to see if the texture is allocated)
113 struct gltexturepool_s *pool;
114 // pointer to next texture in texturepool chain
115 struct gltexture_s *chain;
116 // name of the texture (this might be removed someday), no duplicates
117 char identifier[MAX_QPATH + 32];
118 // original data size in *inputtexels
119 int inputwidth, inputheight, inputdepth;
120 // copy of the original texture(s) supplied to the upload function, for
121 // delayed uploads (non-precached)
122 unsigned char *inputtexels;
123 // original data size in *inputtexels
125 // flags supplied to the LoadTexture function
126 // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
128 // pointer to one of the textype_ structs
129 textypeinfo_t *textype;
130 // one of the GLTEXTURETYPE_ values
132 // palette if the texture is TEXTYPE_PALETTE
133 const unsigned int *palette;
134 // actual stored texture size after gl_picmip and gl_max_size are applied
135 // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
136 int tilewidth, tileheight, tiledepth;
137 // 1 or 6 depending on texturetype
141 // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
144 int glinternalformat;
145 // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
150 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
152 typedef struct gltexturepool_s
154 unsigned int sentinel;
155 struct gltexture_s *gltchain;
156 struct gltexturepool_s *next;
160 static gltexturepool_t *gltexturepoolchain = NULL;
162 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
163 static int resizebuffersize = 0;
164 static const unsigned char *texturebuffer;
166 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
171 return &textype_dxt1;
173 return &textype_dxt1a;
175 return &textype_dxt3;
177 return &textype_dxt5;
178 case TEXTYPE_PALETTE:
179 return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
181 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
182 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
183 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;
187 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
189 return &textype_alpha;
190 case TEXTYPE_SHADOWMAP:
191 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
192 case TEXTYPE_COLORBUFFER:
193 return &textype_colorbuffer;
195 Host_Error("R_GetTexTypeInfo: unknown texture format");
201 // dynamic texture code [11/22/2007 Black]
202 void R_MarkDirtyTexture(rtexture_t *rt) {
203 gltexture_t *glt = (gltexture_t*) rt;
208 // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
209 if (glt->flags & GLTEXF_DYNAMIC)
211 // mark it as dirty, so R_RealGetTexture gets called
216 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
217 gltexture_t *glt = (gltexture_t*) rt;
222 glt->flags |= GLTEXF_DYNAMIC;
223 glt->updatecallback = updatecallback;
224 glt->updatacallback_data = data;
227 static void R_UpdateDynamicTexture(gltexture_t *glt) {
229 if( glt->updatecallback ) {
230 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
234 void R_PurgeTexture(rtexture_t *rt)
236 if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
241 void R_FreeTexture(rtexture_t *rt)
243 gltexture_t *glt, **gltpointer;
245 glt = (gltexture_t *)rt;
247 Host_Error("R_FreeTexture: texture == NULL");
249 for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
250 if (*gltpointer == glt)
251 *gltpointer = glt->chain;
253 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
258 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
261 if (glt->inputtexels)
262 Mem_Free(glt->inputtexels);
263 Mem_ExpandableArray_FreeRecord(&texturearray, glt);
266 rtexturepool_t *R_AllocTexturePool(void)
268 gltexturepool_t *pool;
269 if (texturemempool == NULL)
271 pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
274 pool->next = gltexturepoolchain;
275 gltexturepoolchain = pool;
276 pool->sentinel = TEXTUREPOOL_SENTINEL;
277 return (rtexturepool_t *)pool;
280 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
282 gltexturepool_t *pool, **poolpointer;
283 if (rtexturepool == NULL)
285 if (*rtexturepool == NULL)
287 pool = (gltexturepool_t *)(*rtexturepool);
288 *rtexturepool = NULL;
289 if (pool->sentinel != TEXTUREPOOL_SENTINEL)
290 Host_Error("R_FreeTexturePool: pool already freed");
291 for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
292 if (*poolpointer == pool)
293 *poolpointer = pool->next;
295 Host_Error("R_FreeTexturePool: pool not linked");
296 while (pool->gltchain)
297 R_FreeTexture((rtexture_t *)pool->gltchain);
302 typedef struct glmode_s
305 int minification, magnification;
309 static glmode_t modes[6] =
311 {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
312 {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
313 {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
314 {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
315 {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
316 {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
319 static void GL_TextureMode_f (void)
324 gltexturepool_t *pool;
328 Con_Printf("Texture mode is %sforced\n", gl_filter_force ? "" : "not ");
329 for (i = 0;i < 6;i++)
331 if (gl_filter_min == modes[i].minification)
333 Con_Printf("%s\n", modes[i].name);
337 Con_Print("current filter is unknown???\n");
341 for (i = 0;i < (int)(sizeof(modes)/sizeof(*modes));i++)
342 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
346 Con_Print("bad filter name\n");
350 gl_filter_min = modes[i].minification;
351 gl_filter_mag = modes[i].magnification;
352 gl_filter_force = ((Cmd_Argc() > 2) && !strcasecmp(Cmd_Argv(2), "force"));
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 && (gl_filter_force || !(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);
526 Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
528 // Disable JPEG screenshots if the DLL isn't loaded
529 if (! JPEG_OpenLibrary ())
530 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
531 if (! PNG_OpenLibrary ())
532 Cvar_SetValueQuick (&scr_screenshot_png, 0);
535 static void r_textures_shutdown(void)
537 rtexturepool_t *temp;
539 JPEG_CloseLibrary ();
541 while(gltexturepoolchain)
543 temp = (rtexturepool_t *) gltexturepoolchain;
544 R_FreeTexturePool(&temp);
547 resizebuffersize = 0;
549 colorconvertbuffer = NULL;
550 texturebuffer = NULL;
551 Mem_ExpandableArray_FreeArray(&texturearray);
552 Mem_FreePool(&texturemempool);
555 static void r_textures_newmap(void)
559 void R_Textures_Init (void)
561 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");
562 Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
563 Cvar_RegisterVariable (&gl_max_size);
564 Cvar_RegisterVariable (&gl_picmip);
565 Cvar_RegisterVariable (&gl_max_lightmapsize);
566 Cvar_RegisterVariable (&r_lerpimages);
567 Cvar_RegisterVariable (&gl_texture_anisotropy);
568 Cvar_RegisterVariable (&gl_texturecompression);
569 Cvar_RegisterVariable (&gl_texturecompression_color);
570 Cvar_RegisterVariable (&gl_texturecompression_normal);
571 Cvar_RegisterVariable (&gl_texturecompression_gloss);
572 Cvar_RegisterVariable (&gl_texturecompression_glow);
573 Cvar_RegisterVariable (&gl_texturecompression_2d);
574 Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
575 Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
576 Cvar_RegisterVariable (&gl_texturecompression_sky);
577 Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
578 Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
579 Cvar_RegisterVariable (&gl_nopartialtextureupdates);
581 R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
584 void R_Textures_Frame (void)
586 static int old_aniso = 0;
588 // could do procedural texture animation here, if we keep track of which
589 // textures were accessed this frame...
591 // free the resize buffers
592 resizebuffersize = 0;
595 Mem_Free(resizebuffer);
598 if (colorconvertbuffer)
600 Mem_Free(colorconvertbuffer);
601 colorconvertbuffer = NULL;
604 if (old_aniso != gl_texture_anisotropy.integer)
607 gltexturepool_t *pool;
610 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
612 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
616 for (pool = gltexturepoolchain;pool;pool = pool->next)
618 for (glt = pool->gltchain;glt;glt = glt->chain)
620 // only update already uploaded images
621 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
623 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
625 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
626 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
628 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
635 void R_MakeResizeBufferBigger(int size)
637 if (resizebuffersize < size)
639 resizebuffersize = size;
641 Mem_Free(resizebuffer);
642 if (colorconvertbuffer)
643 Mem_Free(colorconvertbuffer);
644 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
645 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
646 if (!resizebuffer || !colorconvertbuffer)
647 Host_Error("R_Upload: out of memory");
651 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
653 int textureenum = gltexturetypeenums[texturetype];
654 int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
658 if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
660 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
661 if (gl_texture_anisotropy.integer != aniso)
662 Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
663 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
665 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
666 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
667 if (gltexturetypedimensions[texturetype] >= 3)
669 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
673 if (!gl_filter_force && flags & TEXF_FORCENEAREST)
675 if (flags & TEXF_MIPMAP)
677 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
681 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
683 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
685 else if (!gl_filter_force && flags & TEXF_FORCELINEAR)
687 if (flags & TEXF_MIPMAP)
689 if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
691 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
695 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
700 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
702 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
706 if (flags & TEXF_MIPMAP)
708 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
712 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
714 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
717 if (textype == TEXTYPE_SHADOWMAP)
719 if (vid.support.arb_shadow)
721 if (flags & TEXF_COMPARE)
723 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
727 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
729 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
731 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
737 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
739 int i, mip, width, height, depth;
741 const unsigned char *prevbuffer;
746 // we need to restore the texture binding after finishing the upload
748 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
749 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
751 // these are rounded up versions of the size to do better resampling
752 if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
754 width = glt->inputwidth;
755 height = glt->inputheight;
756 depth = glt->inputdepth;
760 for (width = 1;width < glt->inputwidth ;width <<= 1);
761 for (height = 1;height < glt->inputheight;height <<= 1);
762 for (depth = 1;depth < glt->inputdepth ;depth <<= 1);
765 R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
766 R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
768 if (prevbuffer == NULL)
770 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
771 prevbuffer = resizebuffer;
773 else if (glt->textype->textype == TEXTYPE_PALETTE)
775 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
776 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
777 prevbuffer = colorconvertbuffer;
780 // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
782 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))
784 // update a portion of the image
785 switch(glt->texturetype)
787 case GLTEXTURETYPE_2D:
788 qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
790 case GLTEXTURETYPE_3D:
791 qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
794 Host_Error("R_Upload: partial update of type other than 2D");
800 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
801 Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
803 // cubemaps contain multiple images and thus get processed a bit differently
804 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
806 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
808 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
809 prevbuffer = resizebuffer;
812 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
814 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
815 prevbuffer = resizebuffer;
819 if (qglGetCompressedTexImageARB)
821 if (gl_texturecompression.integer >= 2)
822 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
824 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
827 switch(glt->texturetype)
829 case GLTEXTURETYPE_2D:
830 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
831 if (glt->flags & TEXF_MIPMAP)
833 while (width > 1 || height > 1 || depth > 1)
835 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
836 prevbuffer = resizebuffer;
837 qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
841 case GLTEXTURETYPE_3D:
842 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 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 qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
853 case GLTEXTURETYPE_CUBEMAP:
854 // convert and upload each side in turn,
855 // from a continuous block of input texels
856 texturebuffer = (unsigned char *)prevbuffer;
857 for (i = 0;i < 6;i++)
859 prevbuffer = texturebuffer;
860 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
861 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
863 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
864 prevbuffer = resizebuffer;
867 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
869 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
870 prevbuffer = resizebuffer;
873 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
874 if (glt->flags & TEXF_MIPMAP)
876 while (width > 1 || height > 1 || depth > 1)
878 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
879 prevbuffer = resizebuffer;
880 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
885 case GLTEXTURETYPE_RECTANGLE:
886 qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
889 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
891 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
894 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)
898 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
899 textypeinfo_t *texinfo, *texinfo2;
901 if (cls.state == ca_dedicated)
904 if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
906 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
909 if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
911 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
914 if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
916 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
920 texinfo = R_GetTexTypeInfo(textype, flags);
921 size = width * height * depth * sides * texinfo->inputbytesperpixel;
924 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
928 // clear the alpha flag if the texture has no transparent pixels
931 case TEXTYPE_PALETTE:
932 if (flags & TEXF_ALPHA)
934 flags &= ~TEXF_ALPHA;
937 for (i = 0;i < size;i++)
939 if (((unsigned char *)&palette[data[i]])[3] < 255)
950 if (flags & TEXF_ALPHA)
952 flags &= ~TEXF_ALPHA;
955 for (i = 3;i < size;i += 4)
966 case TEXTYPE_SHADOWMAP:
978 case TEXTYPE_COLORBUFFER:
982 Host_Error("R_LoadTexture: unknown texture type");
985 texinfo2 = R_GetTexTypeInfo(textype, flags);
986 if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
989 Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
991 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
993 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
995 glt->chain = pool->gltchain;
996 pool->gltchain = glt;
997 glt->inputwidth = width;
998 glt->inputheight = height;
999 glt->inputdepth = depth;
1001 glt->textype = texinfo;
1002 glt->texturetype = texturetype;
1003 glt->inputdatasize = size;
1004 glt->palette = palette;
1005 glt->glinternalformat = texinfo->glinternalformat;
1006 glt->glformat = texinfo->glformat;
1007 glt->gltype = texinfo->gltype;
1008 glt->bytesperpixel = texinfo->internalbytesperpixel;
1009 glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1012 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1013 // init the dynamic texture attributes, too [11/22/2007 Black]
1014 glt->updatecallback = NULL;
1015 glt->updatacallback_data = NULL;
1017 GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1019 // upload the texture
1020 // data may be NULL (blank texture for dynamic rendering)
1022 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1023 R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1024 if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1025 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1027 // texture converting and uploading can take a while, so make sure we're sending keepalives
1028 CL_KeepaliveMessage(false);
1030 return (rtexture_t *)glt;
1033 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)
1035 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1038 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)
1040 return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1043 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)
1045 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1048 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)
1050 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1053 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1055 int flags = TEXF_CLAMP;
1057 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1059 flags |= TEXF_FORCENEAREST;
1060 if (precision <= 16)
1061 flags |= TEXF_LOWPRECISION;
1065 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1067 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1070 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1072 return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1075 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1077 return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1080 int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
1082 gltexture_t *glt = (gltexture_t *)rt;
1085 int bytesperpixel = 0;
1086 int bytesperblock = 0;
1088 int dds_format_flags;
1096 GLint internalformat;
1097 const char *ddsfourcc;
1099 return -1; // NULL pointer
1100 if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
1101 return -2; // broken driver - crashes on reading internal format
1102 if (!qglGetTexLevelParameteriv)
1104 GL_ActiveTexture(0);
1105 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1106 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1107 qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
1108 switch(internalformat)
1110 default: ddsfourcc = NULL;bytesperpixel = 4;break;
1111 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1112 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
1113 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
1114 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
1116 if (!bytesperblock && skipuncompressed)
1117 return -3; // skipped
1118 memset(mipinfo, 0, sizeof(mipinfo));
1119 mipinfo[0][0] = glt->tilewidth;
1120 mipinfo[0][1] = glt->tileheight;
1122 if (glt->flags & TEXF_MIPMAP)
1124 for (mip = 1;mip < 16;mip++)
1126 mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
1127 mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
1128 if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
1136 for (mip = 0;mip < mipmaps;mip++)
1138 mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
1139 mipinfo[mip][3] = ddssize;
1140 ddssize += mipinfo[mip][2];
1142 dds = Mem_Alloc(tempmempool, ddssize);
1145 dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
1149 dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
1150 dds_format_flags = 0x4; // DDPF_FOURCC
1154 dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
1155 dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
1159 dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
1160 dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
1162 memcpy(dds, "DDS ", 4);
1163 StoreLittleLong(dds+4, ddssize);
1164 StoreLittleLong(dds+8, dds_flags);
1165 StoreLittleLong(dds+12, mipinfo[0][1]); // height
1166 StoreLittleLong(dds+16, mipinfo[0][0]); // width
1167 StoreLittleLong(dds+24, 1); // depth
1168 StoreLittleLong(dds+28, mipmaps); // mipmaps
1169 StoreLittleLong(dds+76, 32); // format size
1170 StoreLittleLong(dds+80, dds_format_flags);
1171 StoreLittleLong(dds+108, dds_caps1);
1172 StoreLittleLong(dds+112, dds_caps2);
1175 StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
1176 memcpy(dds+84, ddsfourcc, 4);
1177 for (mip = 0;mip < mipmaps;mip++)
1179 qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
1184 StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
1185 StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
1186 dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
1187 for (mip = 0;mip < mipmaps;mip++)
1189 qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
1192 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1193 ret = FS_WriteFile(filename, dds, ddssize);
1195 return ret ? ddssize : -5;
1198 rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
1200 int i, size, dds_format_flags, dds_miplevels, dds_width, dds_height;
1203 int bytesperblock, bytesperpixel;
1206 gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
1207 textypeinfo_t *texinfo;
1208 int mip, mipwidth, mipheight, mipsize;
1210 GLint oldbindtexnum;
1211 const unsigned char *mippixels, *ddspixels;
1213 fs_offset_t ddsfilesize;
1214 unsigned int ddssize;
1216 if (cls.state == ca_dedicated)
1219 dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
1220 ddssize = ddsfilesize;
1224 Log_Printf("ddstexturefailures.log", "%s\n", filename);
1225 return NULL; // not found
1228 if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
1231 Con_Printf("^1%s: not a DDS image\n", filename);
1235 //dds_flags = BuffLittleLong(dds+8);
1236 dds_format_flags = BuffLittleLong(dds+80);
1237 dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
1238 dds_width = BuffLittleLong(dds+16);
1239 dds_height = BuffLittleLong(dds+12);
1240 ddspixels = dds + 128;
1242 flags &= ~TEXF_ALPHA;
1243 if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
1245 // very sloppy BGRA 32bit identification
1246 textype = TEXTYPE_BGRA;
1249 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
1250 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1253 Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
1257 for (i = 3;i < size;i += 4)
1258 if (ddspixels[i] < 255)
1261 flags &= ~TEXF_ALPHA;
1263 else if (!memcmp(dds+84, "DXT1", 4))
1265 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1266 // LordHavoc: it is my belief that this does not infringe on the
1267 // patent because it is not decoding pixels...
1268 textype = TEXTYPE_DXT1;
1271 //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1272 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1273 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1276 Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
1279 for (i = 0;i < size;i += bytesperblock)
1280 if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1283 textype = TEXTYPE_DXT1A;
1285 flags &= ~TEXF_ALPHA;
1287 else if (!memcmp(dds+84, "DXT3", 4))
1289 textype = TEXTYPE_DXT3;
1292 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1293 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1296 Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
1300 else if (!memcmp(dds+84, "DXT5", 4))
1302 textype = TEXTYPE_DXT5;
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 DXT5 DDS image\n", filename);
1316 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1320 // return whether this texture is transparent
1322 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1324 // calculate average color if requested
1328 Vector4Clear(avgcolor);
1331 for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1333 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1334 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1335 avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
1336 avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
1338 f = (float)bytesperblock / size;
1339 avgcolor[0] *= (0.5f / 31.0f) * f;
1340 avgcolor[1] *= (0.5f / 63.0f) * f;
1341 avgcolor[2] *= (0.5f / 31.0f) * f;
1342 avgcolor[3] = 1; // too hard to calculate
1346 for (i = 0;i < size;i += 4)
1348 avgcolor[0] += ddspixels[i+2];
1349 avgcolor[1] += ddspixels[i+1];
1350 avgcolor[2] += ddspixels[i];
1351 avgcolor[3] += ddspixels[i+3];
1353 f = (1.0f / 255.0f) * bytesperpixel / size;
1361 if (dds_miplevels > 1)
1362 flags |= TEXF_MIPMAP;
1364 flags &= ~TEXF_MIPMAP;
1366 // if S3TC is not supported, there's very little we can do about it
1367 if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1370 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1374 texinfo = R_GetTexTypeInfo(textype, flags);
1376 glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
1377 strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1379 glt->chain = pool->gltchain;
1380 pool->gltchain = glt;
1381 glt->inputwidth = dds_width;
1382 glt->inputheight = dds_height;
1383 glt->inputdepth = 1;
1385 glt->textype = texinfo;
1386 glt->texturetype = GLTEXTURETYPE_2D;
1387 glt->inputdatasize = ddssize;
1388 glt->glinternalformat = texinfo->glinternalformat;
1389 glt->glformat = texinfo->glformat;
1390 glt->gltype = texinfo->gltype;
1391 glt->bytesperpixel = texinfo->internalbytesperpixel;
1393 glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1394 glt->tilewidth = dds_width;
1395 glt->tileheight = dds_height;
1398 // texture uploading can take a while, so make sure we're sending keepalives
1399 CL_KeepaliveMessage(false);
1401 // upload the texture
1402 // we need to restore the texture binding after finishing the upload
1404 GL_ActiveTexture(0);
1405 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1406 qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1407 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1408 mippixels = ddspixels;
1409 mipwidth = dds_width;
1410 mipheight = dds_height;
1411 mipcomplete = false;
1412 for (mip = 0;mip < dds_miplevels+1;mip++)
1414 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1415 if (mippixels + mipsize > dds + ddssize)
1419 qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1423 qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1425 mippixels += mipsize;
1426 if (mipwidth <= 1 && mipheight <= 1)
1436 if (dds_miplevels > 1 && !mipcomplete)
1438 // need to set GL_TEXTURE_MAX_LEVEL
1439 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1441 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1442 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1445 return (rtexture_t *)glt;
1448 int R_TextureWidth(rtexture_t *rt)
1450 return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1453 int R_TextureHeight(rtexture_t *rt)
1455 return rt ? ((gltexture_t *)rt)->inputheight : 0;
1458 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1460 gltexture_t *glt = (gltexture_t *)rt;
1462 Host_Error("R_UpdateTexture: no data supplied");
1464 Host_Error("R_UpdateTexture: no texture supplied");
1466 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1467 // update part of the texture
1468 if (glt->bufferpixels)
1471 int bpp = glt->bytesperpixel;
1472 int inputskip = width*bpp;
1473 int outputskip = glt->tilewidth*bpp;
1474 const unsigned char *input = data;
1475 unsigned char *output = glt->bufferpixels;
1485 input -= y*inputskip;
1488 if (width > glt->tilewidth - x)
1489 width = glt->tilewidth - x;
1490 if (height > glt->tileheight - y)
1491 height = glt->tileheight - y;
1492 if (width < 1 || height < 1)
1495 glt->buffermodified = true;
1496 output += y*outputskip + x*bpp;
1497 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1498 memcpy(output, input, width*bpp);
1501 R_Upload(glt, data, x, y, 0, width, height, 1);
1504 int R_RealGetTexture(rtexture_t *rt)
1509 glt = (gltexture_t *)rt;
1510 if (glt->flags & GLTEXF_DYNAMIC)
1511 R_UpdateDynamicTexture(glt);
1512 if (glt->buffermodified && glt->bufferpixels)
1514 glt->buffermodified = false;
1515 R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1524 void R_ClearTexture (rtexture_t *rt)
1526 gltexture_t *glt = (gltexture_t *)rt;
1528 R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );