]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_textures.c
implemented support for GL_ARB_texture_compression - this is controlled
[xonotic/darkplaces.git] / gl_textures.c
1
2 #include "quakedef.h"
3 #include "image.h"
4 #include "jpeg.h"
5 #include "image_png.h"
6
7 cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, note: this is automatically reduced to match video card capabilities (such as 256 on 3Dfx cards before Voodoo4/5)"};
8 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%"};
9 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)"};
10 cvar_t r_precachetextures = {CVAR_SAVE, "r_precachetextures", "1", "0 = never upload textures until used, 1 = upload most textures before use (exceptions: rarely used skin colormap layers), 2 = upload all textures before use (can increase texture memory usage significantly)"};
11 cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
12 cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
13 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
14 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "1", "whether to compress normalmap (normalmap) textures"};
15 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
16 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
17 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "1", "whether to compress 2d (hud/menu) textures other than the font"};
18 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "1", "whether to compress lightmaps in q3bsp format levels"};
19 cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "1", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
20 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
21 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
22
23 int             gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
24 int             gl_filter_mag = GL_LINEAR;
25
26
27 static mempool_t *texturemempool;
28
29 // note: this must not conflict with TEXF_ flags in r_textures.h
30 // cleared when a texture is uploaded
31 #define GLTEXF_UPLOAD 0x00010000
32 // bitmask for mismatch checking
33 #define GLTEXF_IMPORTANTBITS (0)
34 // set when image is uploaded and freed
35 #define GLTEXF_DESTROYED 0x00040000
36
37 typedef struct textypeinfo_s
38 {
39         int textype;
40         int inputbytesperpixel;
41         int internalbytesperpixel;
42         int glformat;
43         int glinternalformat;
44         int glcompressedinternalformat;
45 }
46 textypeinfo_t;
47
48 static textypeinfo_t textype_palette       = {TEXTYPE_PALETTE, 1, 4, GL_RGBA   , 3, GL_COMPRESSED_RGB_ARB};
49 static textypeinfo_t textype_rgb           = {TEXTYPE_RGB    , 3, 3, GL_RGB    , 3, GL_COMPRESSED_RGB_ARB};
50 static textypeinfo_t textype_rgba          = {TEXTYPE_RGBA   , 4, 4, GL_RGBA   , 3, GL_COMPRESSED_RGB_ARB};
51 static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE, 1, 4, GL_RGBA   , 4, GL_COMPRESSED_RGBA_ARB};
52 static textypeinfo_t textype_rgba_alpha    = {TEXTYPE_RGBA   , 4, 4, GL_RGBA   , 4, GL_COMPRESSED_RGBA_ARB};
53
54 #define GLTEXTURETYPE_1D 0
55 #define GLTEXTURETYPE_2D 1
56 #define GLTEXTURETYPE_3D 2
57 #define GLTEXTURETYPE_CUBEMAP 3
58
59 static int gltexturetypeenums[4] = {GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB};
60 static int gltexturetypebindingenums[4] = {GL_TEXTURE_BINDING_1D, GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB};
61 static int gltexturetypedimensions[4] = {1, 2, 3, 2};
62 static int cubemapside[6] =
63 {
64         GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
65         GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
66         GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
67         GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
68         GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
69         GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
70 };
71
72 typedef struct gltexture_s
73 {
74         // this field is exposed to the R_GetTexture macro, for speed reasons
75         // (must be identical in rtexture_t)
76         int texnum; // GL texture slot number
77
78         // pointer to texturepool (check this to see if the texture is allocated)
79         struct gltexturepool_s *pool;
80         // pointer to next texture in texturepool chain
81         struct gltexture_s *chain;
82         // name of the texture (this might be removed someday), no duplicates
83         char identifier[MAX_QPATH + 32];
84         // original data size in *inputtexels
85         int inputwidth, inputheight, inputdepth;
86         // copy of the original texture(s) supplied to the upload function, for
87         // delayed uploads (non-precached)
88         unsigned char *inputtexels;
89         // original data size in *inputtexels
90         int inputdatasize;
91         // flags supplied to the LoadTexture function
92         // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
93         int flags;
94         // pointer to one of the textype_ structs
95         textypeinfo_t *textype;
96         // one of the GLTEXTURETYPE_ values
97         int texturetype;
98         // palette if the texture is TEXTYPE_PALETTE
99         const unsigned int *palette;
100         // actual stored texture size after gl_picmip and gl_max_size are applied
101         // (power of 2 if gl_support_arb_texture_non_power_of_two is not supported)
102         int tilewidth, tileheight, tiledepth;
103         // 1 or 6 depending on texturetype
104         int sides;
105         // bytes per pixel
106         int bytesperpixel;
107         // GL_RGB or GL_RGBA
108         int glformat;
109         // 3 or 4
110         int glinternalformat;
111 }
112 gltexture_t;
113
114 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
115
116 typedef struct gltexturepool_s
117 {
118         unsigned int sentinel;
119         struct gltexture_s *gltchain;
120         struct gltexturepool_s *next;
121 }
122 gltexturepool_t;
123
124 static gltexturepool_t *gltexturepoolchain = NULL;
125
126 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
127 static int resizebuffersize = 0;
128 static unsigned char *texturebuffer;
129 static int texturebuffersize = 0;
130
131 static textypeinfo_t *R_GetTexTypeInfo(int textype, int flags)
132 {
133         if (flags & TEXF_ALPHA)
134         {
135                 switch(textype)
136                 {
137                 case TEXTYPE_PALETTE:
138                         return &textype_palette_alpha;
139                 case TEXTYPE_RGB:
140                         Host_Error("R_GetTexTypeInfo: RGB format has no alpha, TEXF_ALPHA not allowed");
141                         return NULL;
142                 case TEXTYPE_RGBA:
143                         return &textype_rgba_alpha;
144                 default:
145                         Host_Error("R_GetTexTypeInfo: unknown texture format");
146                         return NULL;
147                 }
148         }
149         else
150         {
151                 switch(textype)
152                 {
153                 case TEXTYPE_PALETTE:
154                         return &textype_palette;
155                 case TEXTYPE_RGB:
156                         return &textype_rgb;
157                 case TEXTYPE_RGBA:
158                         return &textype_rgba;
159                 default:
160                         Host_Error("R_GetTexTypeInfo: unknown texture format");
161                         return NULL;
162                 }
163         }
164 }
165
166 static void R_UploadTexture(gltexture_t *t);
167
168 static void R_PrecacheTexture(gltexture_t *glt)
169 {
170         int precache;
171         precache = false;
172         if (glt->flags & TEXF_ALWAYSPRECACHE)
173                 precache = true;
174         else if (r_precachetextures.integer >= 2)
175                 precache = true;
176         else if (r_precachetextures.integer >= 1)
177                 if (glt->flags & TEXF_PRECACHE)
178                         precache = true;
179
180         if (precache)
181                 R_UploadTexture(glt);
182 }
183
184 int R_RealGetTexture(rtexture_t *rt)
185 {
186         if (rt)
187         {
188                 gltexture_t *glt;
189                 glt = (gltexture_t *)rt;
190                 if (glt->flags & GLTEXF_UPLOAD)
191                         R_UploadTexture(glt);
192                 return glt->texnum;
193         }
194         else
195                 return 0;
196 }
197
198 void R_FreeTexture(rtexture_t *rt)
199 {
200         gltexture_t *glt, **gltpointer;
201
202         glt = (gltexture_t *)rt;
203         if (glt == NULL)
204                 Host_Error("R_FreeTexture: texture == NULL");
205
206         for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
207         if (*gltpointer == glt)
208                 *gltpointer = glt->chain;
209         else
210                 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
211
212         if (glt->texnum)
213         {
214                 CHECKGLERROR
215                 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
216         }
217
218         if (glt->inputtexels)
219                 Mem_Free(glt->inputtexels);
220         Mem_Free(glt);
221 }
222
223 rtexturepool_t *R_AllocTexturePool(void)
224 {
225         gltexturepool_t *pool;
226         if (texturemempool == NULL)
227                 return NULL;
228         pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
229         if (pool == NULL)
230                 return NULL;
231         pool->next = gltexturepoolchain;
232         gltexturepoolchain = pool;
233         pool->sentinel = TEXTUREPOOL_SENTINEL;
234         return (rtexturepool_t *)pool;
235 }
236
237 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
238 {
239         gltexturepool_t *pool, **poolpointer;
240         if (rtexturepool == NULL)
241                 return;
242         if (*rtexturepool == NULL)
243                 return;
244         pool = (gltexturepool_t *)(*rtexturepool);
245         *rtexturepool = NULL;
246         if (pool->sentinel != TEXTUREPOOL_SENTINEL)
247                 Host_Error("R_FreeTexturePool: pool already freed");
248         for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
249         if (*poolpointer == pool)
250                 *poolpointer = pool->next;
251         else
252                 Host_Error("R_FreeTexturePool: pool not linked");
253         while (pool->gltchain)
254                 R_FreeTexture((rtexture_t *)pool->gltchain);
255         Mem_Free(pool);
256 }
257
258
259 typedef struct glmode_s
260 {
261         char *name;
262         int minification, magnification;
263 }
264 glmode_t;
265
266 static glmode_t modes[6] =
267 {
268         {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
269         {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
270         {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
271         {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
272         {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
273         {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
274 };
275
276 static void GL_TextureMode_f (void)
277 {
278         int i;
279         GLint oldbindtexnum;
280         gltexture_t *glt;
281         gltexturepool_t *pool;
282
283         if (Cmd_Argc() == 1)
284         {
285                 for (i = 0;i < 6;i++)
286                 {
287                         if (gl_filter_min == modes[i].minification)
288                         {
289                                 Con_Printf("%s\n", modes[i].name);
290                                 return;
291                         }
292                 }
293                 Con_Print("current filter is unknown???\n");
294                 return;
295         }
296
297         for (i = 0;i < 6;i++)
298                 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
299                         break;
300         if (i == 6)
301         {
302                 Con_Print("bad filter name\n");
303                 return;
304         }
305
306         gl_filter_min = modes[i].minification;
307         gl_filter_mag = modes[i].magnification;
308
309         // change all the existing mipmap texture objects
310         // FIXME: force renderer(/client/something?) restart instead?
311         CHECKGLERROR
312         for (pool = gltexturepoolchain;pool;pool = pool->next)
313         {
314                 for (glt = pool->gltchain;glt;glt = glt->chain)
315                 {
316                         // only update already uploaded images
317                         if (!(glt->flags & (GLTEXF_UPLOAD | TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
318                         {
319                                 qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
320                                 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
321                                 if (glt->flags & TEXF_MIPMAP)
322                                 {
323                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
324                                 }
325                                 else
326                                 {
327                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
328                                 }
329                                 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
330                                 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
331                         }
332                 }
333         }
334 }
335
336 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
337 {
338         int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
339
340         if (gl_max_size.integer > gl_max_texture_size)
341                 Cvar_SetValue("gl_max_size", gl_max_texture_size);
342
343         switch (texturetype)
344         {
345         default:
346         case GLTEXTURETYPE_1D:
347         case GLTEXTURETYPE_2D:
348                 maxsize = gl_max_texture_size;
349                 break;
350         case GLTEXTURETYPE_3D:
351                 maxsize = gl_max_3d_texture_size;
352                 break;
353         case GLTEXTURETYPE_CUBEMAP:
354                 maxsize = gl_max_cube_map_texture_size;
355                 break;
356         }
357
358         if (flags & TEXF_PICMIP)
359         {
360                 maxsize = min(maxsize, gl_max_size.integer);
361                 picmip = gl_picmip.integer;
362         }
363
364         if (outwidth)
365         {
366                 if (gl_support_arb_texture_non_power_of_two)
367                         width2 = min(inwidth >> picmip, maxsize);
368                 else
369                 {
370                         for (width2 = 1;width2 < inwidth;width2 <<= 1);
371                         for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
372                 }
373                 *outwidth = max(1, width2);
374         }
375         if (outheight)
376         {
377                 if (gl_support_arb_texture_non_power_of_two)
378                         height2 = min(inheight >> picmip, maxsize);
379                 else
380                 {
381                         for (height2 = 1;height2 < inheight;height2 <<= 1);
382                         for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
383                 }
384                 *outheight = max(1, height2);
385         }
386         if (outdepth)
387         {
388                 if (gl_support_arb_texture_non_power_of_two)
389                         depth2 = min(indepth >> picmip, maxsize);
390                 else
391                 {
392                         for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
393                         for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
394                 }
395                 *outdepth = max(1, depth2);
396         }
397 }
398
399
400 static int R_CalcTexelDataSize (gltexture_t *glt)
401 {
402         int width2, height2, depth2, size;
403
404         GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
405
406         size = width2 * height2 * depth2;
407
408         if (glt->flags & TEXF_MIPMAP)
409         {
410                 while (width2 > 1 || height2 > 1 || depth2 > 1)
411                 {
412                         if (width2 > 1)
413                                 width2 >>= 1;
414                         if (height2 > 1)
415                                 height2 >>= 1;
416                         if (depth2 > 1)
417                                 depth2 >>= 1;
418                         size += width2 * height2 * depth2;
419                 }
420         }
421
422         return size * glt->textype->internalbytesperpixel * glt->sides;
423 }
424
425 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
426 {
427         int glsize;
428         int isloaded;
429         int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
430         int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
431         gltexture_t *glt;
432         gltexturepool_t *pool;
433         if (printeach)
434                 Con_Print("glsize input loaded mip alpha name\n");
435         for (pool = gltexturepoolchain;pool;pool = pool->next)
436         {
437                 pooltotal = 0;
438                 pooltotalt = 0;
439                 pooltotalp = 0;
440                 poolloaded = 0;
441                 poolloadedt = 0;
442                 poolloadedp = 0;
443                 for (glt = pool->gltchain;glt;glt = glt->chain)
444                 {
445                         glsize = R_CalcTexelDataSize(glt);
446                         isloaded = !(glt->flags & GLTEXF_UPLOAD);
447                         pooltotal++;
448                         pooltotalt += glsize;
449                         pooltotalp += glt->inputdatasize;
450                         if (isloaded)
451                         {
452                                 poolloaded++;
453                                 poolloadedt += glsize;
454                                 poolloadedp += glt->inputdatasize;
455                         }
456                         if (printeach)
457                                 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);
458                 }
459                 if (printpool)
460                         Con_Printf("texturepool %10p total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", 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);
461                 sumtotal += pooltotal;
462                 sumtotalt += pooltotalt;
463                 sumtotalp += pooltotalp;
464                 sumloaded += poolloaded;
465                 sumloadedt += poolloadedt;
466                 sumloadedp += poolloadedp;
467         }
468         if (printtotal)
469                 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);
470 }
471
472 static void R_TextureStats_f(void)
473 {
474         R_TextureStats_Print(true, true, true);
475 }
476
477 static void r_textures_start(void)
478 {
479         // LordHavoc: allow any alignment
480         CHECKGLERROR
481         qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
482         qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
483
484         texturemempool = Mem_AllocPool("texture management", 0, NULL);
485
486         // Disable JPEG screenshots if the DLL isn't loaded
487         if (! JPEG_OpenLibrary ())
488                 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
489         // TODO: support png screenshots?
490         PNG_OpenLibrary ();
491 }
492
493 static void r_textures_shutdown(void)
494 {
495         rtexturepool_t *temp;
496
497         JPEG_CloseLibrary ();
498
499         while(gltexturepoolchain)
500         {
501                 temp = (rtexturepool_t *) gltexturepoolchain;
502                 R_FreeTexturePool(&temp);
503         }
504
505         resizebuffersize = 0;
506         texturebuffersize = 0;
507         resizebuffer = NULL;
508         colorconvertbuffer = NULL;
509         texturebuffer = NULL;
510         Mem_FreePool(&texturemempool);
511 }
512
513 static void r_textures_newmap(void)
514 {
515 }
516
517 void R_Textures_Init (void)
518 {
519         Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
520         Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
521         Cvar_RegisterVariable (&gl_max_size);
522         Cvar_RegisterVariable (&gl_picmip);
523         Cvar_RegisterVariable (&r_lerpimages);
524         Cvar_RegisterVariable (&r_precachetextures);
525         Cvar_RegisterVariable (&gl_texture_anisotropy);
526         Cvar_RegisterVariable (&gl_texturecompression);
527         Cvar_RegisterVariable (&gl_texturecompression_color);
528         Cvar_RegisterVariable (&gl_texturecompression_normal);
529         Cvar_RegisterVariable (&gl_texturecompression_gloss);
530         Cvar_RegisterVariable (&gl_texturecompression_glow);
531         Cvar_RegisterVariable (&gl_texturecompression_2d);
532         Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
533         Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
534         Cvar_RegisterVariable (&gl_texturecompression_sky);
535         Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
536
537         R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
538 }
539
540 void R_Textures_Frame (void)
541 {
542         static int old_aniso = 0;
543
544         // could do procedural texture animation here, if we keep track of which
545         // textures were accessed this frame...
546
547         // free the resize buffers
548         resizebuffersize = 0;
549         if (resizebuffer)
550         {
551                 Mem_Free(resizebuffer);
552                 resizebuffer = NULL;
553         }
554         if (colorconvertbuffer)
555         {
556                 Mem_Free(colorconvertbuffer);
557                 colorconvertbuffer = NULL;
558         }
559
560         if (old_aniso != gl_texture_anisotropy.integer)
561         {
562                 gltexture_t *glt;
563                 gltexturepool_t *pool;
564                 GLint oldbindtexnum;
565
566                 old_aniso = bound(1, gl_texture_anisotropy.integer, gl_max_anisotropy);
567
568                 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
569
570                 CHECKGLERROR
571                 for (pool = gltexturepoolchain;pool;pool = pool->next)
572                 {
573                         for (glt = pool->gltchain;glt;glt = glt->chain)
574                         {
575                                 // only update already uploaded images
576                                 if ((glt->flags & (GLTEXF_UPLOAD | TEXF_MIPMAP)) == TEXF_MIPMAP)
577                                 {
578                                         qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
579
580                                         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
581                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
582
583                                         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
584                                 }
585                         }
586                 }
587         }
588 }
589
590 void R_MakeResizeBufferBigger(int size)
591 {
592         if (resizebuffersize < size)
593         {
594                 resizebuffersize = size;
595                 if (resizebuffer)
596                         Mem_Free(resizebuffer);
597                 if (colorconvertbuffer)
598                         Mem_Free(colorconvertbuffer);
599                 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
600                 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
601                 if (!resizebuffer || !colorconvertbuffer)
602                         Host_Error("R_Upload: out of memory");
603         }
604 }
605
606 static void GL_SetupTextureParameters(int flags, int texturetype)
607 {
608         int textureenum = gltexturetypeenums[texturetype];
609         int wrapmode = ((flags & TEXF_CLAMP) && gl_support_clamptoedge) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
610
611         CHECKGLERROR
612
613         if (gl_support_anisotropy && (flags & TEXF_MIPMAP))
614         {
615                 int aniso = bound(1, gl_texture_anisotropy.integer, gl_max_anisotropy);
616                 if (gl_texture_anisotropy.integer != aniso)
617                         Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
618                 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
619         }
620         qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
621         qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
622         if (gltexturetypedimensions[texturetype] >= 3)
623         {
624                 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
625         }
626
627         CHECKGLERROR
628         if (flags & TEXF_FORCENEAREST)
629         {
630                 if (flags & TEXF_MIPMAP)
631                 {
632                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
633                 }
634                 else
635                 {
636                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
637                 }
638                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
639         }
640         else if (flags & TEXF_FORCELINEAR)
641         {
642                 if (flags & TEXF_MIPMAP)
643                 {
644                         if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
645                         {
646                                 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
647                         }
648                         else
649                         {
650                                 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
651                         }
652                 }
653                 else
654                 {
655                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
656                 }
657                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
658         }
659         else
660         {
661                 if (flags & TEXF_MIPMAP)
662                 {
663                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
664                 }
665                 else
666                 {
667                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
668                 }
669                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
670         }
671
672         CHECKGLERROR
673 }
674
675 static void R_Upload(gltexture_t *glt, unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
676 {
677         int i, mip, width, height, depth;
678         GLint oldbindtexnum;
679         unsigned char *prevbuffer;
680         prevbuffer = data;
681
682         CHECKGLERROR
683
684         // we need to restore the texture binding after finishing the upload
685         qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
686         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
687
688         // these are rounded up versions of the size to do better resampling
689         if (gl_support_arb_texture_non_power_of_two)
690         {
691                 width = glt->inputwidth;
692                 height = glt->inputheight;
693                 depth = glt->inputdepth;
694         }
695         else
696         {
697                 for (width  = 1;width  < glt->inputwidth ;width  <<= 1);
698                 for (height = 1;height < glt->inputheight;height <<= 1);
699                 for (depth  = 1;depth  < glt->inputdepth ;depth  <<= 1);
700         }
701
702         R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
703         R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
704
705         if (prevbuffer == NULL)
706         {
707                 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
708                 prevbuffer = resizebuffer;
709         }
710         else if (glt->textype->textype == TEXTYPE_PALETTE)
711         {
712                 // promote paletted to RGBA, so we only have to worry about RGB and
713                 // RGBA in the rest of this code
714                 Image_Copy8bitRGBA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
715                 prevbuffer = colorconvertbuffer;
716         }
717
718         if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP | GLTEXF_UPLOAD)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth)
719         {
720                 // update a portion of the image
721                 switch(glt->texturetype)
722                 {
723                 case GLTEXTURETYPE_1D:
724                         qglTexSubImage1D(GL_TEXTURE_1D, 0, fragx, fragwidth, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
725                         break;
726                 case GLTEXTURETYPE_2D:
727                         qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
728                         break;
729                 case GLTEXTURETYPE_3D:
730                         qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
731                         break;
732                 default:
733                         Host_Error("R_Upload: partial update of type other than 1D, 2D, or 3D");
734                         break;
735                 }
736         }
737         else
738         {
739                 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
740                         Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
741
742                 // upload the image for the first time
743                 glt->flags &= ~GLTEXF_UPLOAD;
744
745                 // cubemaps contain multiple images and thus get processed a bit differently
746                 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
747                 {
748                         if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
749                         {
750                                 Image_Resample(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, glt->bytesperpixel, r_lerpimages.integer);
751                                 prevbuffer = resizebuffer;
752                         }
753                         // picmip/max_size
754                         while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
755                         {
756                                 Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth, glt->bytesperpixel);
757                                 prevbuffer = resizebuffer;
758                         }
759                 }
760                 mip = 0;
761                 if (gl_support_texture_compression)
762                 {
763                         if (gl_texturecompression.integer >= 2)
764                                 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
765                         else
766                                 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
767                         CHECKGLERROR
768                 }
769                 switch(glt->texturetype)
770                 {
771                 case GLTEXTURETYPE_1D:
772                         qglTexImage1D(GL_TEXTURE_1D, mip++, glt->glinternalformat, width, 0, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
773                         if (glt->flags & TEXF_MIPMAP)
774                         {
775                                 while (width > 1 || height > 1 || depth > 1)
776                                 {
777                                         Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1, glt->bytesperpixel);
778                                         prevbuffer = resizebuffer;
779                                         qglTexImage1D(GL_TEXTURE_1D, mip++, glt->glinternalformat, width, 0, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
780                                 }
781                         }
782                         break;
783                 case GLTEXTURETYPE_2D:
784                         qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
785                         if (glt->flags & TEXF_MIPMAP)
786                         {
787                                 while (width > 1 || height > 1 || depth > 1)
788                                 {
789                                         Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1, glt->bytesperpixel);
790                                         prevbuffer = resizebuffer;
791                                         qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
792                                 }
793                         }
794                         break;
795                 case GLTEXTURETYPE_3D:
796                         qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
797                         if (glt->flags & TEXF_MIPMAP)
798                         {
799                                 while (width > 1 || height > 1 || depth > 1)
800                                 {
801                                         Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1, glt->bytesperpixel);
802                                         prevbuffer = resizebuffer;
803                                         qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
804                                 }
805                         }
806                         break;
807                 case GLTEXTURETYPE_CUBEMAP:
808                         // convert and upload each side in turn,
809                         // from a continuous block of input texels
810                         texturebuffer = prevbuffer;
811                         for (i = 0;i < 6;i++)
812                         {
813                                 prevbuffer = texturebuffer;
814                                 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
815                                 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
816                                 {
817                                         Image_Resample(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, glt->bytesperpixel, r_lerpimages.integer);
818                                         prevbuffer = resizebuffer;
819                                 }
820                                 // picmip/max_size
821                                 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
822                                 {
823                                         Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth, glt->bytesperpixel);
824                                         prevbuffer = resizebuffer;
825                                 }
826                                 mip = 0;
827                                 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
828                                 if (glt->flags & TEXF_MIPMAP)
829                                 {
830                                         while (width > 1 || height > 1 || depth > 1)
831                                         {
832                                                 Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1, glt->bytesperpixel);
833                                                 prevbuffer = resizebuffer;
834                                                 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
835                                         }
836                                 }
837                         }
838                         break;
839                 }
840                 GL_SetupTextureParameters(glt->flags, glt->texturetype);
841         }
842         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
843 }
844
845 static void R_UploadTexture (gltexture_t *glt)
846 {
847         if (!(glt->flags & GLTEXF_UPLOAD))
848                 return;
849
850         CHECKGLERROR
851         qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
852         R_Upload(glt, glt->inputtexels, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
853         if (glt->inputtexels)
854         {
855                 Mem_Free(glt->inputtexels);
856                 glt->inputtexels = NULL;
857                 glt->flags |= GLTEXF_DESTROYED;
858         }
859         else if (glt->flags & GLTEXF_DESTROYED)
860                 Con_Printf("R_UploadTexture: Texture %s already uploaded and destroyed.  Can not upload original image again.  Uploaded blank texture.\n", glt->identifier);
861 }
862
863 static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, int sides, int flags, int textype, int texturetype, const unsigned char *data, const unsigned int *palette)
864 {
865         int i, size;
866         gltexture_t *glt;
867         gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
868         textypeinfo_t *texinfo;
869
870         if (cls.state == ca_dedicated)
871                 return NULL;
872
873         if (texturetype == GLTEXTURETYPE_CUBEMAP && !gl_texturecubemap)
874         {
875                 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
876                 return NULL;
877         }
878         if (texturetype == GLTEXTURETYPE_3D && !gl_texture3d)
879         {
880                 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
881                 return NULL;
882         }
883
884         texinfo = R_GetTexTypeInfo(textype, flags);
885         size = width * height * depth * sides * texinfo->inputbytesperpixel;
886         if (size < 1)
887         {
888                 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
889                 return NULL;
890         }
891
892         // clear the alpha flag if the texture has no transparent pixels
893         switch(textype)
894         {
895         case TEXTYPE_PALETTE:
896                 if (flags & TEXF_ALPHA)
897                 {
898                         flags &= ~TEXF_ALPHA;
899                         if (data)
900                         {
901                                 for (i = 0;i < size;i++)
902                                 {
903                                         if (((unsigned char *)&palette[data[i]])[3] < 255)
904                                         {
905                                                 flags |= TEXF_ALPHA;
906                                                 break;
907                                         }
908                                 }
909                         }
910                 }
911                 break;
912         case TEXTYPE_RGB:
913                 if (flags & TEXF_ALPHA)
914                         Host_Error("R_LoadTexture: RGB has no alpha, don't specify TEXF_ALPHA");
915                 break;
916         case TEXTYPE_RGBA:
917                 if (flags & TEXF_ALPHA)
918                 {
919                         flags &= ~TEXF_ALPHA;
920                         if (data)
921                         {
922                                 for (i = 3;i < size;i += 4)
923                                 {
924                                         if (data[i] < 255)
925                                         {
926                                                 flags |= TEXF_ALPHA;
927                                                 break;
928                                         }
929                                 }
930                         }
931                 }
932                 break;
933         default:
934                 Host_Error("R_LoadTexture: unknown texture type");
935         }
936
937         glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
938         if (identifier)
939                 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
940         glt->pool = pool;
941         glt->chain = pool->gltchain;
942         pool->gltchain = glt;
943         glt->inputwidth = width;
944         glt->inputheight = height;
945         glt->inputdepth = depth;
946         glt->flags = flags | GLTEXF_UPLOAD;
947         glt->textype = texinfo;
948         glt->texturetype = texturetype;
949         glt->inputdatasize = size;
950         glt->palette = palette;
951         glt->glinternalformat = ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && gl_support_texture_compression) ? texinfo->glcompressedinternalformat : texinfo->glinternalformat;
952         glt->glformat = texinfo->glformat;
953         glt->bytesperpixel = texinfo->internalbytesperpixel;
954         glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
955         glt->texnum = -1;
956
957         if (data)
958         {
959                 glt->inputtexels = (unsigned char *)Mem_Alloc(texturemempool, size);
960                 memcpy(glt->inputtexels, data, size);
961         }
962         else
963                 glt->inputtexels = NULL;
964
965         GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
966         R_PrecacheTexture(glt);
967
968         return (rtexture_t *)glt;
969 }
970
971 rtexture_t *R_LoadTexture1D(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, int textype, int flags, const unsigned int *palette)
972 {
973         return R_SetupTexture(rtexturepool, identifier, width, 1, 1, 1, flags, textype, GLTEXTURETYPE_1D, data, palette);
974 }
975
976 rtexture_t *R_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, int textype, int flags, const unsigned int *palette)
977 {
978         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
979 }
980
981 rtexture_t *R_LoadTexture3D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, const unsigned char *data, int textype, int flags, const unsigned int *palette)
982 {
983         return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
984 }
985
986 rtexture_t *R_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, int textype, int flags, const unsigned int *palette)
987 {
988         return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
989 }
990
991 int R_TextureHasAlpha(rtexture_t *rt)
992 {
993         return rt ? (((gltexture_t *)rt)->flags & TEXF_ALPHA) != 0 : false;
994 }
995
996 int R_TextureWidth(rtexture_t *rt)
997 {
998         return rt ? ((gltexture_t *)rt)->inputwidth : 0;
999 }
1000
1001 int R_TextureHeight(rtexture_t *rt)
1002 {
1003         return rt ? ((gltexture_t *)rt)->inputheight : 0;
1004 }
1005
1006 void R_UpdateTexture(rtexture_t *rt, unsigned char *data, int x, int y, int width, int height)
1007 {
1008         gltexture_t *glt;
1009         if (rt == NULL)
1010                 Host_Error("R_UpdateTexture: no texture supplied");
1011         if (data == NULL)
1012                 Host_Error("R_UpdateTexture: no data supplied");
1013         glt = (gltexture_t *)rt;
1014
1015         // we need it to be uploaded before we can update a part of it
1016         if (glt->flags & GLTEXF_UPLOAD)
1017                 R_UploadTexture(glt);
1018
1019         // update part of the texture
1020         R_Upload(glt, data, x, y, 0, width, height, 1);
1021 }
1022