25074722fc49283cd7acde7dccd37b5021cd0dbc
[divverent/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, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
8 cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
9 cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0", "reduces resolution of textures by powers of 2, for example 1 will halve width/height, reducing texture memory usage by 75%"};
10 cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1", "bilinear filters images when scaling them up to power of 2 size (mode 1), looks better than glquake (mode 0)"};
11 cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
12 cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
13 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
14 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
15 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
16 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
17 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
18 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
19 cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
20 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
21 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
22 cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "0", "use alternate path for dynamic lightmap updates"};
23
24 int             gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
25 int             gl_filter_mag = GL_LINEAR;
26
27
28 static mempool_t *texturemempool;
29
30 // note: this must not conflict with TEXF_ flags in r_textures.h
31 // cleared when a texture is uploaded
32 #define GLTEXF_UPLOAD           0x00010000
33 // bitmask for mismatch checking
34 #define GLTEXF_IMPORTANTBITS (0)
35 // set when image is uploaded and freed
36 #define GLTEXF_DESTROYED        0x00040000
37 // dynamic texture (treat texnum == 0 differently)
38 #define GLTEXF_DYNAMIC          0x00080000
39
40 typedef struct textypeinfo_s
41 {
42         textype_t textype;
43         int inputbytesperpixel;
44         int internalbytesperpixel;
45         float glinternalbytesperpixel;
46         int glformat;
47         int glinternalformat;
48         int gltype;
49 }
50 textypeinfo_t;
51
52 static textypeinfo_t textype_palette                = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA   , 3, GL_UNSIGNED_BYTE};
53 static textypeinfo_t textype_palette_alpha          = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA   , 4, GL_UNSIGNED_BYTE};
54 static textypeinfo_t textype_palette_compress       = {TEXTYPE_PALETTE, 1, 4, 0.5f, GL_BGRA   , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
55 static textypeinfo_t textype_palette_alpha_compress = {TEXTYPE_PALETTE, 1, 4, 1.0f, GL_BGRA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
56 static textypeinfo_t textype_rgba                   = {TEXTYPE_RGBA   , 4, 4, 4.0f, GL_RGBA   , 3, GL_UNSIGNED_BYTE};
57 static textypeinfo_t textype_rgba_alpha             = {TEXTYPE_RGBA   , 4, 4, 4.0f, GL_RGBA   , 4, GL_UNSIGNED_BYTE};
58 static textypeinfo_t textype_rgba_compress          = {TEXTYPE_RGBA   , 4, 4, 0.5f, GL_RGBA   , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
59 static textypeinfo_t textype_rgba_alpha_compress    = {TEXTYPE_RGBA   , 4, 4, 1.0f, GL_RGBA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
60 static textypeinfo_t textype_bgra                   = {TEXTYPE_BGRA   , 4, 4, 4.0f, GL_BGRA   , 3, GL_UNSIGNED_BYTE};
61 static textypeinfo_t textype_bgra_alpha             = {TEXTYPE_BGRA   , 4, 4, 4.0f, GL_BGRA   , 4, GL_UNSIGNED_BYTE};
62 static textypeinfo_t textype_bgra_compress          = {TEXTYPE_BGRA   , 4, 4, 0.5f, GL_BGRA   , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
63 static textypeinfo_t textype_bgra_alpha_compress    = {TEXTYPE_BGRA   , 4, 4, 1.0f, GL_BGRA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
64 static textypeinfo_t textype_shadowmap16            = {TEXTYPE_SHADOWMAP,2,2, 2.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16_ARB, GL_UNSIGNED_SHORT};
65 static textypeinfo_t textype_shadowmap24            = {TEXTYPE_SHADOWMAP,4,4, 4.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24_ARB, GL_UNSIGNED_INT};
66
67 typedef enum gltexturetype_e
68 {
69         GLTEXTURETYPE_2D,
70         GLTEXTURETYPE_3D,
71         GLTEXTURETYPE_CUBEMAP,
72         GLTEXTURETYPE_RECTANGLE,
73         GLTEXTURETYPE_TOTAL
74 }
75 gltexturetype_t;
76
77 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
78 static int gltexturetypebindingenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB, GL_TEXTURE_BINDING_RECTANGLE_ARB};
79 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
80 static int cubemapside[6] =
81 {
82         GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
83         GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
84         GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
85         GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
86         GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
87         GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
88 };
89
90 typedef struct gltexture_s
91 {
92         // this field is exposed to the R_GetTexture macro, for speed reasons
93         // (must be identical in rtexture_t)
94         int texnum; // GL texture slot number
95
96         // dynamic texture stuff [11/22/2007 Black]
97         // used to hold the texture number of dirty textures   
98         int dirtytexnum;
99         updatecallback_t updatecallback;
100         void *updatacallback_data;
101         // --- [11/22/2007 Black]
102
103         // stores backup copy of texture for deferred texture updates (r_nopartialtextureupdates cvar)
104         unsigned char *bufferpixels;
105         qboolean buffermodified;
106
107         // pointer to texturepool (check this to see if the texture is allocated)
108         struct gltexturepool_s *pool;
109         // pointer to next texture in texturepool chain
110         struct gltexture_s *chain;
111         // name of the texture (this might be removed someday), no duplicates
112         char identifier[MAX_QPATH + 32];
113         // original data size in *inputtexels
114         int inputwidth, inputheight, inputdepth;
115         // copy of the original texture(s) supplied to the upload function, for
116         // delayed uploads (non-precached)
117         unsigned char *inputtexels;
118         // original data size in *inputtexels
119         int inputdatasize;
120         // flags supplied to the LoadTexture function
121         // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
122         int flags;
123         // pointer to one of the textype_ structs
124         textypeinfo_t *textype;
125         // one of the GLTEXTURETYPE_ values
126         int texturetype;
127         // palette if the texture is TEXTYPE_PALETTE
128         const unsigned int *palette;
129         // actual stored texture size after gl_picmip and gl_max_size are applied
130         // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
131         int tilewidth, tileheight, tiledepth;
132         // 1 or 6 depending on texturetype
133         int sides;
134         // bytes per pixel
135         int bytesperpixel;
136         // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
137         int glformat;
138         // 3 or 4
139         int glinternalformat;
140         // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
141         int gltype;
142 }
143 gltexture_t;
144
145 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
146
147 typedef struct gltexturepool_s
148 {
149         unsigned int sentinel;
150         struct gltexture_s *gltchain;
151         struct gltexturepool_s *next;
152 }
153 gltexturepool_t;
154
155 static gltexturepool_t *gltexturepoolchain = NULL;
156
157 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
158 static int resizebuffersize = 0;
159 static const unsigned char *texturebuffer;
160 static int texturebuffersize = 0;
161
162 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
163 {
164         if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
165         {
166                 if (flags & TEXF_ALPHA)
167                 {
168                         switch(textype)
169                         {
170                         case TEXTYPE_PALETTE:
171                                 return &textype_palette_alpha_compress;
172                         case TEXTYPE_RGBA:
173                                 return &textype_rgba_alpha_compress;
174                         case TEXTYPE_BGRA:
175                                 return &textype_bgra_alpha_compress;
176                         default:
177                                 Host_Error("R_GetTexTypeInfo: unknown texture format");
178                                 return NULL;
179                         }
180                 }
181                 else
182                 {
183                         switch(textype)
184                         {
185                         case TEXTYPE_PALETTE:
186                                 return &textype_palette_compress;
187                         case TEXTYPE_RGBA:
188                                 return &textype_rgba_compress;
189                         case TEXTYPE_BGRA:
190                                 return &textype_bgra_compress;
191                         default:
192                                 Host_Error("R_GetTexTypeInfo: unknown texture format");
193                                 return NULL;
194                         }
195                 }
196         }
197         else
198         {
199                 if (flags & TEXF_ALPHA)
200                 {
201                         switch(textype)
202                         {
203                         case TEXTYPE_PALETTE:
204                                 return &textype_palette_alpha;
205                         case TEXTYPE_RGBA:
206                                 return &textype_rgba_alpha;
207                         case TEXTYPE_BGRA:
208                                 return &textype_bgra_alpha;
209                         default:
210                                 Host_Error("R_GetTexTypeInfo: unknown texture format");
211                                 return NULL;
212                         }
213                 }
214                 else
215                 {
216                         switch(textype)
217                         {
218                         case TEXTYPE_PALETTE:
219                                 return &textype_palette;
220                         case TEXTYPE_RGBA:
221                                 return &textype_rgba;
222                         case TEXTYPE_BGRA:
223                                 return &textype_bgra;
224                         case TEXTYPE_SHADOWMAP:
225                                 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
226                         default:
227                                 Host_Error("R_GetTexTypeInfo: unknown texture format");
228                                 return NULL;
229                         }
230                 }
231         }
232         return NULL; // this line only to hush compiler warnings
233 }
234
235 // dynamic texture code [11/22/2007 Black]
236 void R_MarkDirtyTexture(rtexture_t *rt) {
237         gltexture_t *glt = (gltexture_t*) rt;
238         if( !glt ) {
239                 return;
240         }
241
242         // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
243         if( !glt->dirtytexnum && glt->flags & GLTEXF_DYNAMIC ) {
244                 glt->dirtytexnum = glt->texnum;
245                 // mark it as dirty, so R_RealGetTexture gets called
246                 glt->texnum = 0;
247         }
248 }
249
250 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
251         gltexture_t *glt = (gltexture_t*) rt;
252         if( !glt ) {
253                 return;
254         }
255
256         glt->flags |= GLTEXF_DYNAMIC;
257         glt->updatecallback = updatecallback;
258         glt->updatacallback_data = data;
259         glt->dirtytexnum = 0;
260 }
261
262 static void R_UpdateDynamicTexture(gltexture_t *glt) {
263         glt->texnum = glt->dirtytexnum;
264         // reset dirtytexnum again (not dirty anymore)
265         glt->dirtytexnum = 0;
266         // TODO: now assert that t->texnum != 0 ?
267         if( glt->updatecallback ) {
268                 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
269         }
270 }
271
272 void R_PurgeTexture(rtexture_t *rt)
273 {
274         if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
275                 R_FreeTexture(rt);
276         }
277 }
278
279 void R_FreeTexture(rtexture_t *rt)
280 {
281         gltexture_t *glt, **gltpointer;
282
283         glt = (gltexture_t *)rt;
284         if (glt == NULL)
285                 Host_Error("R_FreeTexture: texture == NULL");
286
287         for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
288         if (*gltpointer == glt)
289                 *gltpointer = glt->chain;
290         else
291                 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
292
293         if (!(glt->flags & GLTEXF_UPLOAD))
294         {
295                 CHECKGLERROR
296                 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
297         }
298
299         if (glt->inputtexels)
300                 Mem_Free(glt->inputtexels);
301         Mem_Free(glt);
302 }
303
304 rtexturepool_t *R_AllocTexturePool(void)
305 {
306         gltexturepool_t *pool;
307         if (texturemempool == NULL)
308                 return NULL;
309         pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
310         if (pool == NULL)
311                 return NULL;
312         pool->next = gltexturepoolchain;
313         gltexturepoolchain = pool;
314         pool->sentinel = TEXTUREPOOL_SENTINEL;
315         return (rtexturepool_t *)pool;
316 }
317
318 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
319 {
320         gltexturepool_t *pool, **poolpointer;
321         if (rtexturepool == NULL)
322                 return;
323         if (*rtexturepool == NULL)
324                 return;
325         pool = (gltexturepool_t *)(*rtexturepool);
326         *rtexturepool = NULL;
327         if (pool->sentinel != TEXTUREPOOL_SENTINEL)
328                 Host_Error("R_FreeTexturePool: pool already freed");
329         for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
330         if (*poolpointer == pool)
331                 *poolpointer = pool->next;
332         else
333                 Host_Error("R_FreeTexturePool: pool not linked");
334         while (pool->gltchain)
335                 R_FreeTexture((rtexture_t *)pool->gltchain);
336         Mem_Free(pool);
337 }
338
339
340 typedef struct glmode_s
341 {
342         char *name;
343         int minification, magnification;
344 }
345 glmode_t;
346
347 static glmode_t modes[6] =
348 {
349         {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
350         {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
351         {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
352         {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
353         {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
354         {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
355 };
356
357 static void GL_TextureMode_f (void)
358 {
359         int i;
360         GLint oldbindtexnum;
361         gltexture_t *glt;
362         gltexturepool_t *pool;
363
364         if (Cmd_Argc() == 1)
365         {
366                 for (i = 0;i < 6;i++)
367                 {
368                         if (gl_filter_min == modes[i].minification)
369                         {
370                                 Con_Printf("%s\n", modes[i].name);
371                                 return;
372                         }
373                 }
374                 Con_Print("current filter is unknown???\n");
375                 return;
376         }
377
378         for (i = 0;i < 6;i++)
379                 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
380                         break;
381         if (i == 6)
382         {
383                 Con_Print("bad filter name\n");
384                 return;
385         }
386
387         gl_filter_min = modes[i].minification;
388         gl_filter_mag = modes[i].magnification;
389
390         // change all the existing mipmap texture objects
391         // FIXME: force renderer(/client/something?) restart instead?
392         CHECKGLERROR
393         GL_ActiveTexture(0);
394         for (pool = gltexturepoolchain;pool;pool = pool->next)
395         {
396                 for (glt = pool->gltchain;glt;glt = glt->chain)
397                 {
398                         // only update already uploaded images
399                         if (!(glt->flags & (GLTEXF_UPLOAD | TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
400                         {
401                                 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
402                                 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
403                                 if (glt->flags & TEXF_MIPMAP)
404                                 {
405                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
406                                 }
407                                 else
408                                 {
409                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
410                                 }
411                                 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
412                                 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
413                         }
414                 }
415         }
416 }
417
418 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
419 {
420         int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
421
422         switch (texturetype)
423         {
424         default:
425         case GLTEXTURETYPE_2D:
426                 maxsize = vid.maxtexturesize_2d;
427                 if (flags & TEXF_PICMIP)
428                 {
429                         maxsize = bound(1, gl_max_size.integer, maxsize);
430                         picmip = gl_picmip.integer;
431                 }
432                 break;
433         case GLTEXTURETYPE_3D:
434                 maxsize = vid.maxtexturesize_3d;
435                 break;
436         case GLTEXTURETYPE_CUBEMAP:
437                 maxsize = vid.maxtexturesize_cubemap;
438                 break;
439         }
440
441         if (outwidth)
442         {
443                 if (vid.support.arb_texture_non_power_of_two)
444                         width2 = min(inwidth >> picmip, maxsize);
445                 else
446                 {
447                         for (width2 = 1;width2 < inwidth;width2 <<= 1);
448                         for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
449                 }
450                 *outwidth = max(1, width2);
451         }
452         if (outheight)
453         {
454                 if (vid.support.arb_texture_non_power_of_two)
455                         height2 = min(inheight >> picmip, maxsize);
456                 else
457                 {
458                         for (height2 = 1;height2 < inheight;height2 <<= 1);
459                         for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
460                 }
461                 *outheight = max(1, height2);
462         }
463         if (outdepth)
464         {
465                 if (vid.support.arb_texture_non_power_of_two)
466                         depth2 = min(indepth >> picmip, maxsize);
467                 else
468                 {
469                         for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
470                         for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
471                 }
472                 *outdepth = max(1, depth2);
473         }
474 }
475
476
477 static int R_CalcTexelDataSize (gltexture_t *glt)
478 {
479         int width2, height2, depth2, size;
480
481         GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
482
483         size = width2 * height2 * depth2;
484
485         if (glt->flags & TEXF_MIPMAP)
486         {
487                 while (width2 > 1 || height2 > 1 || depth2 > 1)
488                 {
489                         if (width2 > 1)
490                                 width2 >>= 1;
491                         if (height2 > 1)
492                                 height2 >>= 1;
493                         if (depth2 > 1)
494                                 depth2 >>= 1;
495                         size += width2 * height2 * depth2;
496                 }
497         }
498
499         return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
500 }
501
502 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
503 {
504         int glsize;
505         int isloaded;
506         int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
507         int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
508         gltexture_t *glt;
509         gltexturepool_t *pool;
510         if (printeach)
511                 Con_Print("glsize input loaded mip alpha name\n");
512         for (pool = gltexturepoolchain;pool;pool = pool->next)
513         {
514                 pooltotal = 0;
515                 pooltotalt = 0;
516                 pooltotalp = 0;
517                 poolloaded = 0;
518                 poolloadedt = 0;
519                 poolloadedp = 0;
520                 for (glt = pool->gltchain;glt;glt = glt->chain)
521                 {
522                         glsize = R_CalcTexelDataSize(glt);
523                         isloaded = !(glt->flags & GLTEXF_UPLOAD);
524                         pooltotal++;
525                         pooltotalt += glsize;
526                         pooltotalp += glt->inputdatasize;
527                         if (isloaded)
528                         {
529                                 poolloaded++;
530                                 poolloadedt += glsize;
531                                 poolloadedp += glt->inputdatasize;
532                         }
533                         if (printeach)
534                                 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);
535                 }
536                 if (printpool)
537                         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);
538                 sumtotal += pooltotal;
539                 sumtotalt += pooltotalt;
540                 sumtotalp += pooltotalp;
541                 sumloaded += poolloaded;
542                 sumloadedt += poolloadedt;
543                 sumloadedp += poolloadedp;
544         }
545         if (printtotal)
546                 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);
547 }
548
549 static void R_TextureStats_f(void)
550 {
551         R_TextureStats_Print(true, true, true);
552 }
553
554 static void r_textures_start(void)
555 {
556         // LordHavoc: allow any alignment
557         CHECKGLERROR
558         qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
559         qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
560
561         texturemempool = Mem_AllocPool("texture management", 0, NULL);
562
563         // Disable JPEG screenshots if the DLL isn't loaded
564         if (! JPEG_OpenLibrary ())
565                 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
566         // TODO: support png screenshots?
567         PNG_OpenLibrary ();
568 }
569
570 static void r_textures_shutdown(void)
571 {
572         rtexturepool_t *temp;
573
574         JPEG_CloseLibrary ();
575
576         while(gltexturepoolchain)
577         {
578                 temp = (rtexturepool_t *) gltexturepoolchain;
579                 R_FreeTexturePool(&temp);
580         }
581
582         resizebuffersize = 0;
583         texturebuffersize = 0;
584         resizebuffer = NULL;
585         colorconvertbuffer = NULL;
586         texturebuffer = NULL;
587         Mem_FreePool(&texturemempool);
588 }
589
590 static void r_textures_newmap(void)
591 {
592 }
593
594 void R_Textures_Init (void)
595 {
596         Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
597         Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
598         Cvar_RegisterVariable (&gl_max_size);
599         Cvar_RegisterVariable (&gl_picmip);
600         Cvar_RegisterVariable (&gl_max_lightmapsize);
601         Cvar_RegisterVariable (&r_lerpimages);
602         Cvar_RegisterVariable (&gl_texture_anisotropy);
603         Cvar_RegisterVariable (&gl_texturecompression);
604         Cvar_RegisterVariable (&gl_texturecompression_color);
605         Cvar_RegisterVariable (&gl_texturecompression_normal);
606         Cvar_RegisterVariable (&gl_texturecompression_gloss);
607         Cvar_RegisterVariable (&gl_texturecompression_glow);
608         Cvar_RegisterVariable (&gl_texturecompression_2d);
609         Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
610         Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
611         Cvar_RegisterVariable (&gl_texturecompression_sky);
612         Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
613         Cvar_RegisterVariable (&gl_nopartialtextureupdates);
614
615         R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
616 }
617
618 void R_Textures_Frame (void)
619 {
620         static int old_aniso = 0;
621
622         // could do procedural texture animation here, if we keep track of which
623         // textures were accessed this frame...
624
625         // free the resize buffers
626         resizebuffersize = 0;
627         if (resizebuffer)
628         {
629                 Mem_Free(resizebuffer);
630                 resizebuffer = NULL;
631         }
632         if (colorconvertbuffer)
633         {
634                 Mem_Free(colorconvertbuffer);
635                 colorconvertbuffer = NULL;
636         }
637
638         if (old_aniso != gl_texture_anisotropy.integer)
639         {
640                 gltexture_t *glt;
641                 gltexturepool_t *pool;
642                 GLint oldbindtexnum;
643
644                 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
645
646                 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
647
648                 CHECKGLERROR
649                 GL_ActiveTexture(0);
650                 for (pool = gltexturepoolchain;pool;pool = pool->next)
651                 {
652                         for (glt = pool->gltchain;glt;glt = glt->chain)
653                         {
654                                 // only update already uploaded images
655                                 if ((glt->flags & (GLTEXF_UPLOAD | TEXF_MIPMAP)) == TEXF_MIPMAP)
656                                 {
657                                         oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
658
659                                         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
660                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
661
662                                         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
663                                 }
664                         }
665                 }
666         }
667 }
668
669 void R_MakeResizeBufferBigger(int size)
670 {
671         if (resizebuffersize < size)
672         {
673                 resizebuffersize = size;
674                 if (resizebuffer)
675                         Mem_Free(resizebuffer);
676                 if (colorconvertbuffer)
677                         Mem_Free(colorconvertbuffer);
678                 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
679                 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
680                 if (!resizebuffer || !colorconvertbuffer)
681                         Host_Error("R_Upload: out of memory");
682         }
683 }
684
685 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
686 {
687         int textureenum = gltexturetypeenums[texturetype];
688         int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
689
690         CHECKGLERROR
691
692         if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
693         {
694                 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
695                 if (gl_texture_anisotropy.integer != aniso)
696                         Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
697                 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
698         }
699         qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
700         qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
701         if (gltexturetypedimensions[texturetype] >= 3)
702         {
703                 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
704         }
705
706         CHECKGLERROR
707         if (flags & TEXF_FORCENEAREST)
708         {
709                 if (flags & TEXF_MIPMAP)
710                 {
711                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
712                 }
713                 else
714                 {
715                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
716                 }
717                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
718         }
719         else if (flags & TEXF_FORCELINEAR)
720         {
721                 if (flags & TEXF_MIPMAP)
722                 {
723                         if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
724                         {
725                                 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
726                         }
727                         else
728                         {
729                                 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
730                         }
731                 }
732                 else
733                 {
734                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
735                 }
736                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
737         }
738         else
739         {
740                 if (flags & TEXF_MIPMAP)
741                 {
742                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
743                 }
744                 else
745                 {
746                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
747                 }
748                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
749         }
750
751         if (textype == TEXTYPE_SHADOWMAP)
752         {
753                 if (vid.support.arb_shadow)
754                 {
755                         if (flags & TEXF_COMPARE)
756                         {
757                                 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
758                         }
759                         else
760                         {
761                                 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
762                         }
763                         qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
764                 }
765                 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
766         }
767
768         CHECKGLERROR
769 }
770
771 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
772 {
773         int i, mip, width, height, depth;
774         GLint oldbindtexnum;
775         const unsigned char *prevbuffer;
776         prevbuffer = data;
777
778         CHECKGLERROR
779
780         // we need to restore the texture binding after finishing the upload
781         GL_ActiveTexture(0);
782         oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
783         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
784
785         // these are rounded up versions of the size to do better resampling
786         if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
787         {
788                 width = glt->inputwidth;
789                 height = glt->inputheight;
790                 depth = glt->inputdepth;
791         }
792         else
793         {
794                 for (width  = 1;width  < glt->inputwidth ;width  <<= 1);
795                 for (height = 1;height < glt->inputheight;height <<= 1);
796                 for (depth  = 1;depth  < glt->inputdepth ;depth  <<= 1);
797         }
798
799         R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
800         R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
801
802         if (prevbuffer == NULL)
803         {
804                 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
805                 prevbuffer = resizebuffer;
806         }
807         else if (glt->textype->textype == TEXTYPE_PALETTE)
808         {
809                 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
810                 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
811                 prevbuffer = colorconvertbuffer;
812         }
813
814         if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP | GLTEXF_UPLOAD)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth && (fragx != 0 || fragy != 0 || fragwidth != glt->tilewidth || fragheight != glt->tileheight))
815         {
816                 // update a portion of the image
817                 switch(glt->texturetype)
818                 {
819                 case GLTEXTURETYPE_2D:
820                         qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
821                         break;
822                 case GLTEXTURETYPE_3D:
823                         qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
824                         break;
825                 default:
826                         Host_Error("R_Upload: partial update of type other than 2D");
827                         break;
828                 }
829         }
830         else
831         {
832                 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
833                         Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
834
835                 // upload the image for the first time
836                 glt->flags &= ~GLTEXF_UPLOAD;
837
838                 // cubemaps contain multiple images and thus get processed a bit differently
839                 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
840                 {
841                         if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
842                         {
843                                 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
844                                 prevbuffer = resizebuffer;
845                         }
846                         // picmip/max_size
847                         while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
848                         {
849                                 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
850                                 prevbuffer = resizebuffer;
851                         }
852                 }
853                 mip = 0;
854                 if (qglGetCompressedTexImageARB)
855                 {
856                         if (gl_texturecompression.integer >= 2)
857                                 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
858                         else
859                                 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
860                         CHECKGLERROR
861                 }
862                 switch(glt->texturetype)
863                 {
864                 case GLTEXTURETYPE_2D:
865                         qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
866                         if (glt->flags & TEXF_MIPMAP)
867                         {
868                                 while (width > 1 || height > 1 || depth > 1)
869                                 {
870                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
871                                         prevbuffer = resizebuffer;
872                                         qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
873                                 }
874                         }
875                         break;
876                 case GLTEXTURETYPE_3D:
877                         qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
878                         if (glt->flags & TEXF_MIPMAP)
879                         {
880                                 while (width > 1 || height > 1 || depth > 1)
881                                 {
882                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
883                                         prevbuffer = resizebuffer;
884                                         qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
885                                 }
886                         }
887                         break;
888                 case GLTEXTURETYPE_CUBEMAP:
889                         // convert and upload each side in turn,
890                         // from a continuous block of input texels
891                         texturebuffer = (unsigned char *)prevbuffer;
892                         for (i = 0;i < 6;i++)
893                         {
894                                 prevbuffer = texturebuffer;
895                                 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
896                                 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
897                                 {
898                                         Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
899                                         prevbuffer = resizebuffer;
900                                 }
901                                 // picmip/max_size
902                                 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
903                                 {
904                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
905                                         prevbuffer = resizebuffer;
906                                 }
907                                 mip = 0;
908                                 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
909                                 if (glt->flags & TEXF_MIPMAP)
910                                 {
911                                         while (width > 1 || height > 1 || depth > 1)
912                                         {
913                                                 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
914                                                 prevbuffer = resizebuffer;
915                                                 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
916                                         }
917                                 }
918                         }
919                         break;
920                 case GLTEXTURETYPE_RECTANGLE:
921                         qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
922                         break;
923                 }
924                 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
925         }
926         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
927 }
928
929 int R_RealGetTexture(rtexture_t *rt)
930 {
931         if (rt)
932         {
933                 gltexture_t *glt;
934                 glt = (gltexture_t *)rt;
935                 if (glt->flags & GLTEXF_DYNAMIC)
936                         R_UpdateDynamicTexture(glt);
937                 if (glt->flags & GLTEXF_UPLOAD)
938                 {
939                         CHECKGLERROR
940                         qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
941                         R_Upload(glt, glt->inputtexels, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
942                         if (glt->inputtexels)
943                         {
944                                 Mem_Free(glt->inputtexels);
945                                 glt->inputtexels = NULL;
946                                 glt->flags |= GLTEXF_DESTROYED;
947                         }
948                         else if (glt->flags & GLTEXF_DESTROYED)
949                                 Con_Printf("R_GetTexture: Texture %s already uploaded and destroyed.  Can not upload original image again.  Uploaded blank texture.\n", glt->identifier);
950                 }
951
952                 return glt->texnum;
953         }
954         else
955                 return 0;
956 }
957
958 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)
959 {
960         int i, size;
961         gltexture_t *glt;
962         gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
963         textypeinfo_t *texinfo;
964
965         if (cls.state == ca_dedicated)
966                 return NULL;
967
968         if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
969         {
970                 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
971                 return NULL;
972         }
973         if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
974         {
975                 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
976                 return NULL;
977         }
978         if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
979         {
980                 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
981                 return NULL;
982         }
983
984         texinfo = R_GetTexTypeInfo(textype, flags);
985         size = width * height * depth * sides * texinfo->inputbytesperpixel;
986         if (size < 1)
987         {
988                 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
989                 return NULL;
990         }
991
992         // clear the alpha flag if the texture has no transparent pixels
993         switch(textype)
994         {
995         case TEXTYPE_PALETTE:
996                 if (flags & TEXF_ALPHA)
997                 {
998                         flags &= ~TEXF_ALPHA;
999                         if (data)
1000                         {
1001                                 for (i = 0;i < size;i++)
1002                                 {
1003                                         if (((unsigned char *)&palette[data[i]])[3] < 255)
1004                                         {
1005                                                 flags |= TEXF_ALPHA;
1006                                                 break;
1007                                         }
1008                                 }
1009                         }
1010                 }
1011                 break;
1012         case TEXTYPE_RGBA:
1013         case TEXTYPE_BGRA:
1014                 if (flags & TEXF_ALPHA)
1015                 {
1016                         flags &= ~TEXF_ALPHA;
1017                         if (data)
1018                         {
1019                                 for (i = 3;i < size;i += 4)
1020                                 {
1021                                         if (data[i] < 255)
1022                                         {
1023                                                 flags |= TEXF_ALPHA;
1024                                                 break;
1025                                         }
1026                                 }
1027                         }
1028                 }
1029                 break;
1030         case TEXTYPE_SHADOWMAP:
1031                 break;
1032         default:
1033                 Host_Error("R_LoadTexture: unknown texture type");
1034         }
1035
1036         glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1037         if (identifier)
1038                 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1039         glt->pool = pool;
1040         glt->chain = pool->gltchain;
1041         pool->gltchain = glt;
1042         glt->inputwidth = width;
1043         glt->inputheight = height;
1044         glt->inputdepth = depth;
1045         glt->flags = flags | GLTEXF_UPLOAD;
1046         glt->textype = texinfo;
1047         glt->texturetype = texturetype;
1048         glt->inputdatasize = size;
1049         glt->palette = palette;
1050         glt->glinternalformat = texinfo->glinternalformat;
1051         glt->glformat = texinfo->glformat;
1052         glt->gltype = texinfo->gltype;
1053         glt->bytesperpixel = texinfo->internalbytesperpixel;
1054         glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1055         glt->texnum = 0;
1056         // init the dynamic texture attributes, too [11/22/2007 Black]
1057         glt->dirtytexnum = 0;
1058         glt->updatecallback = NULL;
1059         glt->updatacallback_data = NULL;
1060
1061         GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1062
1063         // upload the texture
1064         // data may be NULL (blank texture for dynamic rendering)
1065         CHECKGLERROR
1066         qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1067         R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1068         if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1069                 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1070
1071         // texture converting and uploading can take a while, so make sure we're sending keepalives
1072         CL_KeepaliveMessage(false);
1073
1074         return (rtexture_t *)glt;
1075 }
1076
1077 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)
1078 {
1079         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1080 }
1081
1082 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)
1083 {
1084         return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1085 }
1086
1087 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)
1088 {
1089         return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1090 }
1091
1092 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)
1093 {
1094         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1095 }
1096
1097 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1098 {
1099         int flags = TEXF_CLAMP;
1100         if (filter)
1101                 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1102         else
1103                 flags |= TEXF_FORCENEAREST;
1104         if (precision <= 16)
1105                 flags |= TEXF_LOWPRECISION;
1106         return flags;
1107 }
1108
1109 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1110 {
1111         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1112 }
1113
1114 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1115 {
1116         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1117 }
1118
1119 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1120 {
1121     return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1122 }
1123
1124 int R_TextureWidth(rtexture_t *rt)
1125 {
1126         return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1127 }
1128
1129 int R_TextureHeight(rtexture_t *rt)
1130 {
1131         return rt ? ((gltexture_t *)rt)->inputheight : 0;
1132 }
1133
1134 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1135 {
1136         gltexture_t *glt = (gltexture_t *)rt;
1137         if (data == NULL)
1138                 Host_Error("R_UpdateTexture: no data supplied");
1139         if (glt == NULL)
1140                 Host_Error("R_UpdateTexture: no texture supplied");
1141         if (!glt->texnum)
1142                 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1143         // update part of the texture
1144         if (glt->bufferpixels)
1145         {
1146                 int j;
1147                 int bpp = glt->bytesperpixel;
1148                 int inputskip = width*bpp;
1149                 int outputskip = glt->tilewidth*bpp;
1150                 const unsigned char *input = data;
1151                 unsigned char *output = glt->bufferpixels;
1152                 if (x < 0)
1153                 {
1154                         width += x;
1155                         input -= x*bpp;
1156                         x = 0;
1157                 }
1158                 if (y < 0)
1159                 {
1160                         height += y;
1161                         input -= y*inputskip;
1162                         y = 0;
1163                 }
1164                 if (width > glt->tilewidth - x)
1165                         width = glt->tilewidth - x;
1166                 if (height > glt->tileheight - y)
1167                         height = glt->tileheight - y;
1168                 if (width < 1 || height < 1)
1169                         return;
1170                 glt->buffermodified = true;
1171                 output += y*outputskip + x*bpp;
1172                 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1173                         memcpy(output, input, width*bpp);
1174                 if (!(glt->flags & TEXF_MANUALFLUSHUPDATES))
1175                         R_FlushTexture(rt);
1176         }
1177         else
1178                 R_Upload(glt, data, x, y, 0, width, height, 1);
1179 }
1180
1181 void R_FlushTexture(rtexture_t *rt)
1182 {
1183         gltexture_t *glt;
1184         if (rt == NULL)
1185                 Host_Error("R_FlushTexture: no texture supplied");
1186
1187         // update part of the texture
1188         glt = (gltexture_t *)rt;
1189
1190         if (!glt->buffermodified || !glt->bufferpixels)
1191                 return;
1192         glt->buffermodified = false;
1193         R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1194 }
1195
1196 void R_ClearTexture (rtexture_t *rt)
1197 {
1198         gltexture_t *glt = (gltexture_t *)rt;
1199
1200         R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );
1201 }