]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_textures.c
store blend weights as unsigned char data
[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 #include "intoverflow.h"
7
8 cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
9 cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
10 cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0", "reduces resolution of textures by powers of 2, for example 1 will halve width/height, reducing texture memory usage by 75%"};
11 cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1", "bilinear filters images when scaling them up to power of 2 size (mode 1), looks better than glquake (mode 0)"};
12 cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
13 cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
14 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
15 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
16 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
17 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
18 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
19 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
20 cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
21 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
22 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
23 cvar_t gl_texturecompression_reflectmask = {CVAR_SAVE, "gl_texturecompression_reflectmask", "1", "whether to compress reflection cubemap masks (mask of which areas of the texture should reflect the generic shiny cubemap)"};
24 cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "1", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"};
25
26 int             gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
27 int             gl_filter_mag = GL_LINEAR;
28
29
30 static mempool_t *texturemempool;
31 static memexpandablearray_t texturearray;
32
33 // note: this must not conflict with TEXF_ flags in r_textures.h
34 // bitmask for mismatch checking
35 #define GLTEXF_IMPORTANTBITS (0)
36 // dynamic texture (treat texnum == 0 differently)
37 #define GLTEXF_DYNAMIC          0x00080000
38
39 typedef struct textypeinfo_s
40 {
41         textype_t textype;
42         int inputbytesperpixel;
43         int internalbytesperpixel;
44         float glinternalbytesperpixel;
45         int glinternalformat;
46         int glformat;
47         int gltype;
48 }
49 textypeinfo_t;
50
51
52 static textypeinfo_t textype_palette                = {TEXTYPE_PALETTE    , 1, 4, 4.0f, 3                               , GL_BGRA           , GL_UNSIGNED_BYTE };
53 static textypeinfo_t textype_palette_alpha          = {TEXTYPE_PALETTE    , 1, 4, 4.0f, 4                               , GL_BGRA           , GL_UNSIGNED_BYTE };
54 static textypeinfo_t textype_rgba                   = {TEXTYPE_RGBA       , 4, 4, 4.0f, 3                               , GL_RGBA           , GL_UNSIGNED_BYTE };
55 static textypeinfo_t textype_rgba_alpha             = {TEXTYPE_RGBA       , 4, 4, 4.0f, 4                               , GL_RGBA           , GL_UNSIGNED_BYTE };
56 static textypeinfo_t textype_rgba_compress          = {TEXTYPE_RGBA       , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGBA           , GL_UNSIGNED_BYTE };
57 static textypeinfo_t textype_rgba_alpha_compress    = {TEXTYPE_RGBA       , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA           , GL_UNSIGNED_BYTE };
58 static textypeinfo_t textype_bgra                   = {TEXTYPE_BGRA       , 4, 4, 4.0f, 3                               , GL_BGRA           , GL_UNSIGNED_BYTE };
59 static textypeinfo_t textype_bgra_alpha             = {TEXTYPE_BGRA       , 4, 4, 4.0f, 4                               , GL_BGRA           , GL_UNSIGNED_BYTE };
60 static textypeinfo_t textype_bgra_compress          = {TEXTYPE_BGRA       , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_BGRA           , GL_UNSIGNED_BYTE };
61 static textypeinfo_t textype_bgra_alpha_compress    = {TEXTYPE_BGRA       , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_BGRA           , GL_UNSIGNED_BYTE };
62 static textypeinfo_t textype_shadowmap16            = {TEXTYPE_SHADOWMAP  , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB        , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
63 static textypeinfo_t textype_shadowmap24            = {TEXTYPE_SHADOWMAP  , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB        , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT  };
64 static textypeinfo_t textype_alpha                  = {TEXTYPE_ALPHA      , 1, 4, 4.0f, GL_ALPHA                        , GL_ALPHA          , GL_UNSIGNED_BYTE };
65 static textypeinfo_t textype_dxt1                   = {TEXTYPE_DXT1       , 4, 0, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , 0                 , 0                };
66 static textypeinfo_t textype_dxt1a                  = {TEXTYPE_DXT1A      , 4, 0, 0.5f, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0                 , 0                };
67 static textypeinfo_t textype_dxt3                   = {TEXTYPE_DXT3       , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0                 , 0                };
68 static textypeinfo_t textype_dxt5                   = {TEXTYPE_DXT5       , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0                 , 0                };
69 static textypeinfo_t textype_colorbuffer            = {TEXTYPE_COLORBUFFER, 4, 4, 4.0f, 4                               , GL_BGRA           , GL_UNSIGNED_BYTE };
70
71
72 typedef enum gltexturetype_e
73 {
74         GLTEXTURETYPE_2D,
75         GLTEXTURETYPE_3D,
76         GLTEXTURETYPE_CUBEMAP,
77         GLTEXTURETYPE_RECTANGLE,
78         GLTEXTURETYPE_TOTAL
79 }
80 gltexturetype_t;
81
82 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
83 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
84 static int cubemapside[6] =
85 {
86         GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
87         GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
88         GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
89         GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
90         GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
91         GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
92 };
93
94 typedef struct gltexture_s
95 {
96         // this portion of the struct is exposed to the R_GetTexture macro for
97         // speed reasons, must be identical in rtexture_t!
98         int texnum; // GL texture slot number
99         qboolean dirty; // indicates that R_RealGetTexture should be called
100         int gltexturetypeenum; // used by R_Mesh_TexBind
101
102         // dynamic texture stuff [11/22/2007 Black]
103         updatecallback_t updatecallback;
104         void *updatacallback_data;
105         // --- [11/22/2007 Black]
106
107         // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
108         unsigned char *bufferpixels;
109         qboolean buffermodified;
110
111         // pointer to texturepool (check this to see if the texture is allocated)
112         struct gltexturepool_s *pool;
113         // pointer to next texture in texturepool chain
114         struct gltexture_s *chain;
115         // name of the texture (this might be removed someday), no duplicates
116         char identifier[MAX_QPATH + 32];
117         // original data size in *inputtexels
118         int inputwidth, inputheight, inputdepth;
119         // copy of the original texture(s) supplied to the upload function, for
120         // delayed uploads (non-precached)
121         unsigned char *inputtexels;
122         // original data size in *inputtexels
123         int inputdatasize;
124         // flags supplied to the LoadTexture function
125         // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
126         int flags;
127         // pointer to one of the textype_ structs
128         textypeinfo_t *textype;
129         // one of the GLTEXTURETYPE_ values
130         int texturetype;
131         // palette if the texture is TEXTYPE_PALETTE
132         const unsigned int *palette;
133         // actual stored texture size after gl_picmip and gl_max_size are applied
134         // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
135         int tilewidth, tileheight, tiledepth;
136         // 1 or 6 depending on texturetype
137         int sides;
138         // bytes per pixel
139         int bytesperpixel;
140         // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
141         int glformat;
142         // 3 or 4
143         int glinternalformat;
144         // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
145         int gltype;
146 }
147 gltexture_t;
148
149 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
150
151 typedef struct gltexturepool_s
152 {
153         unsigned int sentinel;
154         struct gltexture_s *gltchain;
155         struct gltexturepool_s *next;
156 }
157 gltexturepool_t;
158
159 static gltexturepool_t *gltexturepoolchain = NULL;
160
161 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
162 static int resizebuffersize = 0;
163 static const unsigned char *texturebuffer;
164
165 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
166 {
167         switch(textype)
168         {
169         case TEXTYPE_DXT1:
170                 return &textype_dxt1;
171         case TEXTYPE_DXT1A:
172                 return &textype_dxt1a;
173         case TEXTYPE_DXT3:
174                 return &textype_dxt3;
175         case TEXTYPE_DXT5:
176                 return &textype_dxt5;
177         case TEXTYPE_PALETTE:
178                 return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
179         case TEXTYPE_RGBA:
180                 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
181                         return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
182                 return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
183         case TEXTYPE_BGRA:
184                 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
185                         return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
186                 return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
187         case TEXTYPE_ALPHA:
188                 return &textype_alpha;
189         case TEXTYPE_SHADOWMAP:
190                 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
191         case TEXTYPE_COLORBUFFER:
192                 return &textype_colorbuffer;
193         default:
194                 Host_Error("R_GetTexTypeInfo: unknown texture format");
195                 break;
196         }
197         return NULL;
198 }
199
200 // dynamic texture code [11/22/2007 Black]
201 void R_MarkDirtyTexture(rtexture_t *rt) {
202         gltexture_t *glt = (gltexture_t*) rt;
203         if( !glt ) {
204                 return;
205         }
206
207         // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
208         if (glt->flags & GLTEXF_DYNAMIC)
209         {
210                 // mark it as dirty, so R_RealGetTexture gets called
211                 glt->dirty = true;
212         }
213 }
214
215 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
216         gltexture_t *glt = (gltexture_t*) rt;
217         if( !glt ) {
218                 return;
219         }
220
221         glt->flags |= GLTEXF_DYNAMIC;
222         glt->updatecallback = updatecallback;
223         glt->updatacallback_data = data;
224 }
225
226 static void R_UpdateDynamicTexture(gltexture_t *glt) {
227         glt->dirty = false;
228         if( glt->updatecallback ) {
229                 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
230         }
231 }
232
233 void R_PurgeTexture(rtexture_t *rt)
234 {
235         if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
236                 R_FreeTexture(rt);
237         }
238 }
239
240 void R_FreeTexture(rtexture_t *rt)
241 {
242         gltexture_t *glt, **gltpointer;
243
244         glt = (gltexture_t *)rt;
245         if (glt == NULL)
246                 Host_Error("R_FreeTexture: texture == NULL");
247
248         for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
249         if (*gltpointer == glt)
250                 *gltpointer = glt->chain;
251         else
252                 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
253
254         if (glt->texnum)
255         {
256                 CHECKGLERROR
257                 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
258         }
259
260         if (glt->inputtexels)
261                 Mem_Free(glt->inputtexels);
262         Mem_ExpandableArray_FreeRecord(&texturearray, glt);
263 }
264
265 rtexturepool_t *R_AllocTexturePool(void)
266 {
267         gltexturepool_t *pool;
268         if (texturemempool == NULL)
269                 return NULL;
270         pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
271         if (pool == NULL)
272                 return NULL;
273         pool->next = gltexturepoolchain;
274         gltexturepoolchain = pool;
275         pool->sentinel = TEXTUREPOOL_SENTINEL;
276         return (rtexturepool_t *)pool;
277 }
278
279 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
280 {
281         gltexturepool_t *pool, **poolpointer;
282         if (rtexturepool == NULL)
283                 return;
284         if (*rtexturepool == NULL)
285                 return;
286         pool = (gltexturepool_t *)(*rtexturepool);
287         *rtexturepool = NULL;
288         if (pool->sentinel != TEXTUREPOOL_SENTINEL)
289                 Host_Error("R_FreeTexturePool: pool already freed");
290         for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
291         if (*poolpointer == pool)
292                 *poolpointer = pool->next;
293         else
294                 Host_Error("R_FreeTexturePool: pool not linked");
295         while (pool->gltchain)
296                 R_FreeTexture((rtexture_t *)pool->gltchain);
297         Mem_Free(pool);
298 }
299
300
301 typedef struct glmode_s
302 {
303         char *name;
304         int minification, magnification;
305 }
306 glmode_t;
307
308 static glmode_t modes[6] =
309 {
310         {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
311         {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
312         {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
313         {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
314         {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
315         {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
316 };
317
318 static void GL_TextureMode_f (void)
319 {
320         int i;
321         GLint oldbindtexnum;
322         gltexture_t *glt;
323         gltexturepool_t *pool;
324
325         if (Cmd_Argc() == 1)
326         {
327                 for (i = 0;i < 6;i++)
328                 {
329                         if (gl_filter_min == modes[i].minification)
330                         {
331                                 Con_Printf("%s\n", modes[i].name);
332                                 return;
333                         }
334                 }
335                 Con_Print("current filter is unknown???\n");
336                 return;
337         }
338
339         for (i = 0;i < 6;i++)
340                 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
341                         break;
342         if (i == 6)
343         {
344                 Con_Print("bad filter name\n");
345                 return;
346         }
347
348         gl_filter_min = modes[i].minification;
349         gl_filter_mag = modes[i].magnification;
350
351         // change all the existing mipmap texture objects
352         // FIXME: force renderer(/client/something?) restart instead?
353         CHECKGLERROR
354         GL_ActiveTexture(0);
355         for (pool = gltexturepoolchain;pool;pool = pool->next)
356         {
357                 for (glt = pool->gltchain;glt;glt = glt->chain)
358                 {
359                         // only update already uploaded images
360                         if (glt->texnum && !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
361                         {
362                                 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
363                                 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
364                                 if (glt->flags & TEXF_MIPMAP)
365                                 {
366                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
367                                 }
368                                 else
369                                 {
370                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
371                                 }
372                                 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
373                                 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
374                         }
375                 }
376         }
377 }
378
379 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
380 {
381         int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
382
383         switch (texturetype)
384         {
385         default:
386         case GLTEXTURETYPE_2D:
387                 maxsize = vid.maxtexturesize_2d;
388                 if (flags & TEXF_PICMIP)
389                 {
390                         maxsize = bound(1, gl_max_size.integer, maxsize);
391                         picmip = gl_picmip.integer;
392                 }
393                 break;
394         case GLTEXTURETYPE_3D:
395                 maxsize = vid.maxtexturesize_3d;
396                 break;
397         case GLTEXTURETYPE_CUBEMAP:
398                 maxsize = vid.maxtexturesize_cubemap;
399                 break;
400         }
401
402         if (outwidth)
403         {
404                 if (vid.support.arb_texture_non_power_of_two)
405                         width2 = min(inwidth >> picmip, maxsize);
406                 else
407                 {
408                         for (width2 = 1;width2 < inwidth;width2 <<= 1);
409                         for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
410                 }
411                 *outwidth = max(1, width2);
412         }
413         if (outheight)
414         {
415                 if (vid.support.arb_texture_non_power_of_two)
416                         height2 = min(inheight >> picmip, maxsize);
417                 else
418                 {
419                         for (height2 = 1;height2 < inheight;height2 <<= 1);
420                         for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
421                 }
422                 *outheight = max(1, height2);
423         }
424         if (outdepth)
425         {
426                 if (vid.support.arb_texture_non_power_of_two)
427                         depth2 = min(indepth >> picmip, maxsize);
428                 else
429                 {
430                         for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
431                         for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
432                 }
433                 *outdepth = max(1, depth2);
434         }
435 }
436
437
438 static int R_CalcTexelDataSize (gltexture_t *glt)
439 {
440         int width2, height2, depth2, size;
441
442         GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
443
444         size = width2 * height2 * depth2;
445
446         if (glt->flags & TEXF_MIPMAP)
447         {
448                 while (width2 > 1 || height2 > 1 || depth2 > 1)
449                 {
450                         if (width2 > 1)
451                                 width2 >>= 1;
452                         if (height2 > 1)
453                                 height2 >>= 1;
454                         if (depth2 > 1)
455                                 depth2 >>= 1;
456                         size += width2 * height2 * depth2;
457                 }
458         }
459
460         return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
461 }
462
463 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
464 {
465         int glsize;
466         int isloaded;
467         int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
468         int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
469         gltexture_t *glt;
470         gltexturepool_t *pool;
471         if (printeach)
472                 Con_Print("glsize input loaded mip alpha name\n");
473         for (pool = gltexturepoolchain;pool;pool = pool->next)
474         {
475                 pooltotal = 0;
476                 pooltotalt = 0;
477                 pooltotalp = 0;
478                 poolloaded = 0;
479                 poolloadedt = 0;
480                 poolloadedp = 0;
481                 for (glt = pool->gltchain;glt;glt = glt->chain)
482                 {
483                         glsize = R_CalcTexelDataSize(glt);
484                         isloaded = glt->texnum != 0;
485                         pooltotal++;
486                         pooltotalt += glsize;
487                         pooltotalp += glt->inputdatasize;
488                         if (isloaded)
489                         {
490                                 poolloaded++;
491                                 poolloadedt += glsize;
492                                 poolloadedp += glt->inputdatasize;
493                         }
494                         if (printeach)
495                                 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);
496                 }
497                 if (printpool)
498                         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);
499                 sumtotal += pooltotal;
500                 sumtotalt += pooltotalt;
501                 sumtotalp += pooltotalp;
502                 sumloaded += poolloaded;
503                 sumloadedt += poolloadedt;
504                 sumloadedp += poolloadedp;
505         }
506         if (printtotal)
507                 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);
508 }
509
510 static void R_TextureStats_f(void)
511 {
512         R_TextureStats_Print(true, true, true);
513 }
514
515 static void r_textures_start(void)
516 {
517         // LordHavoc: allow any alignment
518         CHECKGLERROR
519         qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
520         qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
521
522         texturemempool = Mem_AllocPool("texture management", 0, NULL);
523         Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
524
525         // Disable JPEG screenshots if the DLL isn't loaded
526         if (! JPEG_OpenLibrary ())
527                 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
528         // TODO: support png screenshots?
529         PNG_OpenLibrary ();
530 }
531
532 static void r_textures_shutdown(void)
533 {
534         rtexturepool_t *temp;
535
536         JPEG_CloseLibrary ();
537
538         while(gltexturepoolchain)
539         {
540                 temp = (rtexturepool_t *) gltexturepoolchain;
541                 R_FreeTexturePool(&temp);
542         }
543
544         resizebuffersize = 0;
545         resizebuffer = NULL;
546         colorconvertbuffer = NULL;
547         texturebuffer = NULL;
548         Mem_ExpandableArray_FreeArray(&texturearray);
549         Mem_FreePool(&texturemempool);
550 }
551
552 static void r_textures_newmap(void)
553 {
554 }
555
556 void R_Textures_Init (void)
557 {
558         Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
559         Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
560         Cvar_RegisterVariable (&gl_max_size);
561         Cvar_RegisterVariable (&gl_picmip);
562         Cvar_RegisterVariable (&gl_max_lightmapsize);
563         Cvar_RegisterVariable (&r_lerpimages);
564         Cvar_RegisterVariable (&gl_texture_anisotropy);
565         Cvar_RegisterVariable (&gl_texturecompression);
566         Cvar_RegisterVariable (&gl_texturecompression_color);
567         Cvar_RegisterVariable (&gl_texturecompression_normal);
568         Cvar_RegisterVariable (&gl_texturecompression_gloss);
569         Cvar_RegisterVariable (&gl_texturecompression_glow);
570         Cvar_RegisterVariable (&gl_texturecompression_2d);
571         Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
572         Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
573         Cvar_RegisterVariable (&gl_texturecompression_sky);
574         Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
575         Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
576         Cvar_RegisterVariable (&gl_nopartialtextureupdates);
577
578         R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
579 }
580
581 void R_Textures_Frame (void)
582 {
583         static int old_aniso = 0;
584
585         // could do procedural texture animation here, if we keep track of which
586         // textures were accessed this frame...
587
588         // free the resize buffers
589         resizebuffersize = 0;
590         if (resizebuffer)
591         {
592                 Mem_Free(resizebuffer);
593                 resizebuffer = NULL;
594         }
595         if (colorconvertbuffer)
596         {
597                 Mem_Free(colorconvertbuffer);
598                 colorconvertbuffer = NULL;
599         }
600
601         if (old_aniso != gl_texture_anisotropy.integer)
602         {
603                 gltexture_t *glt;
604                 gltexturepool_t *pool;
605                 GLint oldbindtexnum;
606
607                 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
608
609                 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
610
611                 CHECKGLERROR
612                 GL_ActiveTexture(0);
613                 for (pool = gltexturepoolchain;pool;pool = pool->next)
614                 {
615                         for (glt = pool->gltchain;glt;glt = glt->chain)
616                         {
617                                 // only update already uploaded images
618                                 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
619                                 {
620                                         oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
621
622                                         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
623                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
624
625                                         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
626                                 }
627                         }
628                 }
629         }
630 }
631
632 void R_MakeResizeBufferBigger(int size)
633 {
634         if (resizebuffersize < size)
635         {
636                 resizebuffersize = size;
637                 if (resizebuffer)
638                         Mem_Free(resizebuffer);
639                 if (colorconvertbuffer)
640                         Mem_Free(colorconvertbuffer);
641                 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
642                 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
643                 if (!resizebuffer || !colorconvertbuffer)
644                         Host_Error("R_Upload: out of memory");
645         }
646 }
647
648 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
649 {
650         int textureenum = gltexturetypeenums[texturetype];
651         int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
652
653         CHECKGLERROR
654
655         if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
656         {
657                 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
658                 if (gl_texture_anisotropy.integer != aniso)
659                         Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
660                 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
661         }
662         qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
663         qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
664         if (gltexturetypedimensions[texturetype] >= 3)
665         {
666                 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
667         }
668
669         CHECKGLERROR
670         if (flags & TEXF_FORCENEAREST)
671         {
672                 if (flags & TEXF_MIPMAP)
673                 {
674                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
675                 }
676                 else
677                 {
678                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
679                 }
680                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
681         }
682         else if (flags & TEXF_FORCELINEAR)
683         {
684                 if (flags & TEXF_MIPMAP)
685                 {
686                         if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
687                         {
688                                 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
689                         }
690                         else
691                         {
692                                 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
693                         }
694                 }
695                 else
696                 {
697                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
698                 }
699                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
700         }
701         else
702         {
703                 if (flags & TEXF_MIPMAP)
704                 {
705                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
706                 }
707                 else
708                 {
709                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
710                 }
711                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
712         }
713
714         if (textype == TEXTYPE_SHADOWMAP)
715         {
716                 if (vid.support.arb_shadow)
717                 {
718                         if (flags & TEXF_COMPARE)
719                         {
720                                 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
721                         }
722                         else
723                         {
724                                 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
725                         }
726                         qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
727                 }
728                 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
729         }
730
731         CHECKGLERROR
732 }
733
734 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
735 {
736         int i, mip, width, height, depth;
737         GLint oldbindtexnum;
738         const unsigned char *prevbuffer;
739         prevbuffer = data;
740
741         CHECKGLERROR
742
743         // we need to restore the texture binding after finishing the upload
744         GL_ActiveTexture(0);
745         oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
746         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
747
748         // these are rounded up versions of the size to do better resampling
749         if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
750         {
751                 width = glt->inputwidth;
752                 height = glt->inputheight;
753                 depth = glt->inputdepth;
754         }
755         else
756         {
757                 for (width  = 1;width  < glt->inputwidth ;width  <<= 1);
758                 for (height = 1;height < glt->inputheight;height <<= 1);
759                 for (depth  = 1;depth  < glt->inputdepth ;depth  <<= 1);
760         }
761
762         R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
763         R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
764
765         if (prevbuffer == NULL)
766         {
767                 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
768                 prevbuffer = resizebuffer;
769         }
770         else if (glt->textype->textype == TEXTYPE_PALETTE)
771         {
772                 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
773                 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
774                 prevbuffer = colorconvertbuffer;
775         }
776
777         // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
778
779         if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth && (fragx != 0 || fragy != 0 || fragwidth != glt->tilewidth || fragheight != glt->tileheight))
780         {
781                 // update a portion of the image
782                 switch(glt->texturetype)
783                 {
784                 case GLTEXTURETYPE_2D:
785                         qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
786                         break;
787                 case GLTEXTURETYPE_3D:
788                         qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
789                         break;
790                 default:
791                         Host_Error("R_Upload: partial update of type other than 2D");
792                         break;
793                 }
794         }
795         else
796         {
797                 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
798                         Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
799
800                 // cubemaps contain multiple images and thus get processed a bit differently
801                 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
802                 {
803                         if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
804                         {
805                                 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
806                                 prevbuffer = resizebuffer;
807                         }
808                         // picmip/max_size
809                         while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
810                         {
811                                 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
812                                 prevbuffer = resizebuffer;
813                         }
814                 }
815                 mip = 0;
816                 if (qglGetCompressedTexImageARB)
817                 {
818                         if (gl_texturecompression.integer >= 2)
819                                 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
820                         else
821                                 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
822                         CHECKGLERROR
823                 }
824                 switch(glt->texturetype)
825                 {
826                 case GLTEXTURETYPE_2D:
827                         qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
828                         if (glt->flags & TEXF_MIPMAP)
829                         {
830                                 while (width > 1 || height > 1 || depth > 1)
831                                 {
832                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
833                                         prevbuffer = resizebuffer;
834                                         qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
835                                 }
836                         }
837                         break;
838                 case GLTEXTURETYPE_3D:
839                         qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
840                         if (glt->flags & TEXF_MIPMAP)
841                         {
842                                 while (width > 1 || height > 1 || depth > 1)
843                                 {
844                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
845                                         prevbuffer = resizebuffer;
846                                         qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
847                                 }
848                         }
849                         break;
850                 case GLTEXTURETYPE_CUBEMAP:
851                         // convert and upload each side in turn,
852                         // from a continuous block of input texels
853                         texturebuffer = (unsigned char *)prevbuffer;
854                         for (i = 0;i < 6;i++)
855                         {
856                                 prevbuffer = texturebuffer;
857                                 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
858                                 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
859                                 {
860                                         Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
861                                         prevbuffer = resizebuffer;
862                                 }
863                                 // picmip/max_size
864                                 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
865                                 {
866                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
867                                         prevbuffer = resizebuffer;
868                                 }
869                                 mip = 0;
870                                 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
871                                 if (glt->flags & TEXF_MIPMAP)
872                                 {
873                                         while (width > 1 || height > 1 || depth > 1)
874                                         {
875                                                 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
876                                                 prevbuffer = resizebuffer;
877                                                 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
878                                         }
879                                 }
880                         }
881                         break;
882                 case GLTEXTURETYPE_RECTANGLE:
883                         qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
884                         break;
885                 }
886                 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
887         }
888         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
889 }
890
891 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)
892 {
893         int i, size;
894         gltexture_t *glt;
895         gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
896         textypeinfo_t *texinfo, *texinfo2;
897
898         if (cls.state == ca_dedicated)
899                 return NULL;
900
901         if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
902         {
903                 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
904                 return NULL;
905         }
906         if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
907         {
908                 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
909                 return NULL;
910         }
911         if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
912         {
913                 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
914                 return NULL;
915         }
916
917         texinfo = R_GetTexTypeInfo(textype, flags);
918         size = width * height * depth * sides * texinfo->inputbytesperpixel;
919         if (size < 1)
920         {
921                 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
922                 return NULL;
923         }
924
925         // clear the alpha flag if the texture has no transparent pixels
926         switch(textype)
927         {
928         case TEXTYPE_PALETTE:
929                 if (flags & TEXF_ALPHA)
930                 {
931                         flags &= ~TEXF_ALPHA;
932                         if (data)
933                         {
934                                 for (i = 0;i < size;i++)
935                                 {
936                                         if (((unsigned char *)&palette[data[i]])[3] < 255)
937                                         {
938                                                 flags |= TEXF_ALPHA;
939                                                 break;
940                                         }
941                                 }
942                         }
943                 }
944                 break;
945         case TEXTYPE_RGBA:
946         case TEXTYPE_BGRA:
947                 if (flags & TEXF_ALPHA)
948                 {
949                         flags &= ~TEXF_ALPHA;
950                         if (data)
951                         {
952                                 for (i = 3;i < size;i += 4)
953                                 {
954                                         if (data[i] < 255)
955                                         {
956                                                 flags |= TEXF_ALPHA;
957                                                 break;
958                                         }
959                                 }
960                         }
961                 }
962                 break;
963         case TEXTYPE_SHADOWMAP:
964                 break;
965         case TEXTYPE_DXT1:
966                 break;
967         case TEXTYPE_DXT1A:
968         case TEXTYPE_DXT3:
969         case TEXTYPE_DXT5:
970                 flags |= TEXF_ALPHA;
971                 break;
972         case TEXTYPE_ALPHA:
973                 flags |= TEXF_ALPHA;
974                 break;
975         case TEXTYPE_COLORBUFFER:
976                 flags |= TEXF_ALPHA;
977                 break;
978         default:
979                 Host_Error("R_LoadTexture: unknown texture type");
980         }
981
982         texinfo2 = R_GetTexTypeInfo(textype, flags);
983         if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
984                 texinfo = texinfo2;
985         else
986                 Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
987
988         glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
989         if (identifier)
990                 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
991         glt->pool = pool;
992         glt->chain = pool->gltchain;
993         pool->gltchain = glt;
994         glt->inputwidth = width;
995         glt->inputheight = height;
996         glt->inputdepth = depth;
997         glt->flags = flags;
998         glt->textype = texinfo;
999         glt->texturetype = texturetype;
1000         glt->inputdatasize = size;
1001         glt->palette = palette;
1002         glt->glinternalformat = texinfo->glinternalformat;
1003         glt->glformat = texinfo->glformat;
1004         glt->gltype = texinfo->gltype;
1005         glt->bytesperpixel = texinfo->internalbytesperpixel;
1006         glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1007         glt->texnum = 0;
1008         glt->dirty = false;
1009         glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1010         // init the dynamic texture attributes, too [11/22/2007 Black]
1011         glt->updatecallback = NULL;
1012         glt->updatacallback_data = NULL;
1013
1014         GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1015
1016         // upload the texture
1017         // data may be NULL (blank texture for dynamic rendering)
1018         CHECKGLERROR
1019         qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1020         R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1021         if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1022                 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1023
1024         // texture converting and uploading can take a while, so make sure we're sending keepalives
1025         CL_KeepaliveMessage(false);
1026
1027         return (rtexture_t *)glt;
1028 }
1029
1030 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)
1031 {
1032         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1033 }
1034
1035 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)
1036 {
1037         return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1038 }
1039
1040 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)
1041 {
1042         return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1043 }
1044
1045 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)
1046 {
1047         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1048 }
1049
1050 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1051 {
1052         int flags = TEXF_CLAMP;
1053         if (filter)
1054                 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1055         else
1056                 flags |= TEXF_FORCENEAREST;
1057         if (precision <= 16)
1058                 flags |= TEXF_LOWPRECISION;
1059         return flags;
1060 }
1061
1062 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1063 {
1064         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1065 }
1066
1067 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1068 {
1069         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1070 }
1071
1072 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1073 {
1074     return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1075 }
1076
1077 int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
1078 {
1079         gltexture_t *glt = (gltexture_t *)rt;
1080         unsigned char *dds;
1081         int oldbindtexnum;
1082         int bytesperpixel = 0;
1083         int bytesperblock = 0;
1084         int dds_flags;
1085         int dds_format_flags;
1086         int dds_caps1;
1087         int dds_caps2;
1088         int ret;
1089         int mip;
1090         int mipmaps;
1091         int mipinfo[16][4];
1092         int ddssize = 128;
1093         GLint internalformat;
1094         const char *ddsfourcc;
1095         if (!rt)
1096                 return -1; // NULL pointer
1097         if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
1098                 return -2; // broken driver - crashes on reading internal format
1099         if (!qglGetTexLevelParameteriv)
1100                 return -2;
1101         GL_ActiveTexture(0);
1102         oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1103         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1104         qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
1105         switch(internalformat)
1106         {
1107         default: ddsfourcc = NULL;bytesperpixel = 4;break;
1108         case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1109         case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
1110         case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
1111         case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
1112         }
1113         if (!bytesperblock && skipuncompressed)
1114                 return -3; // skipped
1115         memset(mipinfo, 0, sizeof(mipinfo));
1116         mipinfo[0][0] = glt->tilewidth;
1117         mipinfo[0][1] = glt->tileheight;
1118         mipmaps = 1;
1119         if (glt->flags & TEXF_MIPMAP)
1120         {
1121                 for (mip = 1;mip < 16;mip++)
1122                 {
1123                         mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
1124                         mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
1125                         if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
1126                         {
1127                                 mip++;
1128                                 break;
1129                         }
1130                 }
1131                 mipmaps = mip;
1132         }
1133         for (mip = 0;mip < mipmaps;mip++)
1134         {
1135                 mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
1136                 mipinfo[mip][3] = ddssize;
1137                 ddssize += mipinfo[mip][2];
1138         }
1139         dds = Mem_Alloc(tempmempool, ddssize);
1140         if (!dds)
1141                 return -4;
1142         dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
1143         dds_caps2 = 0;
1144         if (bytesperblock)
1145         {
1146                 dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
1147                 dds_format_flags = 0x4; // DDPF_FOURCC
1148         }
1149         else
1150         {
1151                 dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
1152                 dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
1153         }
1154         if (mipmaps)
1155         {
1156                 dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
1157                 dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
1158         }
1159         memcpy(dds, "DDS ", 4);
1160         StoreLittleLong(dds+4, ddssize);
1161         StoreLittleLong(dds+8, dds_flags);
1162         StoreLittleLong(dds+12, mipinfo[0][1]); // height
1163         StoreLittleLong(dds+16, mipinfo[0][0]); // width
1164         StoreLittleLong(dds+24, 1); // depth
1165         StoreLittleLong(dds+28, mipmaps); // mipmaps
1166         StoreLittleLong(dds+76, 32); // format size
1167         StoreLittleLong(dds+80, dds_format_flags);
1168         StoreLittleLong(dds+108, dds_caps1);
1169         StoreLittleLong(dds+112, dds_caps2);
1170         if (bytesperblock)
1171         {
1172                 StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
1173                 memcpy(dds+84, ddsfourcc, 4);
1174                 for (mip = 0;mip < mipmaps;mip++)
1175                 {
1176                         qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
1177                 }
1178         }
1179         else
1180         {
1181                 StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
1182                 StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
1183                 dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
1184                 for (mip = 0;mip < mipmaps;mip++)
1185                 {
1186                         qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
1187                 }
1188         }
1189         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1190         ret = FS_WriteFile(filename, dds, ddssize);
1191         Mem_Free(dds);
1192         return ret ? ddssize : -5;
1193 }
1194
1195 rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
1196 {
1197         int i, size, dds_format_flags, dds_miplevels, dds_width, dds_height;
1198         //int dds_flags;
1199         textype_t textype;
1200         int bytesperblock, bytesperpixel;
1201         int mipcomplete;
1202         gltexture_t *glt;
1203         gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
1204         textypeinfo_t *texinfo;
1205         int mip, mipwidth, mipheight, mipsize;
1206         unsigned int c;
1207         GLint oldbindtexnum;
1208         const unsigned char *mippixels, *ddspixels;
1209         unsigned char *dds;
1210         fs_offset_t ddsfilesize;
1211         unsigned int ddssize;
1212
1213         if (cls.state == ca_dedicated)
1214                 return NULL;
1215
1216         dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
1217         ddssize = ddsfilesize;
1218
1219         if (!dds)
1220         {
1221                 Log_Printf("ddstexturefailures.log", "%s\n", filename);
1222                 return NULL; // not found
1223         }
1224
1225         if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
1226         {
1227                 Mem_Free(dds);
1228                 Con_Printf("^1%s: not a DDS image\n", filename);
1229                 return NULL;
1230         }
1231
1232         //dds_flags = BuffLittleLong(dds+8);
1233         dds_format_flags = BuffLittleLong(dds+80);
1234         dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
1235         dds_width = BuffLittleLong(dds+16);
1236         dds_height = BuffLittleLong(dds+12);
1237         ddspixels = dds + 128;
1238
1239         flags &= ~TEXF_ALPHA;
1240         if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
1241         {
1242                 // very sloppy BGRA 32bit identification
1243                 textype = TEXTYPE_BGRA;
1244                 bytesperblock = 0;
1245                 bytesperpixel = 4;
1246                 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
1247                 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1248                 {
1249                         Mem_Free(dds);
1250                         Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
1251                         return NULL;
1252                 }
1253                 // check alpha
1254                 for (i = 3;i < size;i += 4)
1255                         if (ddspixels[i] < 255)
1256                                 break;
1257                 if (i >= size)
1258                         flags &= ~TEXF_ALPHA;
1259         }
1260         else if (!memcmp(dds+84, "DXT1", 4))
1261         {
1262                 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1263                 // LordHavoc: it is my belief that this does not infringe on the
1264                 // patent because it is not decoding pixels...
1265                 textype = TEXTYPE_DXT1;
1266                 bytesperblock = 8;
1267                 bytesperpixel = 0;
1268                 //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1269                 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1270                 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1271                 {
1272                         Mem_Free(dds);
1273                         Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
1274                         return NULL;
1275                 }
1276                 for (i = 0;i < size;i += bytesperblock)
1277                         if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1278                                 break;
1279                 if (i < size)
1280                         textype = TEXTYPE_DXT1A;
1281                 else
1282                         flags &= ~TEXF_ALPHA;
1283         }
1284         else if (!memcmp(dds+84, "DXT3", 4))
1285         {
1286                 textype = TEXTYPE_DXT3;
1287                 bytesperblock = 16;
1288                 bytesperpixel = 0;
1289                 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1290                 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1291                 {
1292                         Mem_Free(dds);
1293                         Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
1294                         return NULL;
1295                 }
1296         }
1297         else if (!memcmp(dds+84, "DXT5", 4))
1298         {
1299                 textype = TEXTYPE_DXT5;
1300                 bytesperblock = 16;
1301                 bytesperpixel = 0;
1302                 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1303                 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1304                 {
1305                         Mem_Free(dds);
1306                         Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
1307                         return NULL;
1308                 }
1309         }
1310         else
1311         {
1312                 Mem_Free(dds);
1313                 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1314                 return NULL;
1315         }
1316
1317         // return whether this texture is transparent
1318         if (hasalphaflag)
1319                 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1320
1321         // calculate average color if requested
1322         if (avgcolor)
1323         {
1324                 float f;
1325                 Vector4Clear(avgcolor);
1326                 if (bytesperblock)
1327                 {
1328                         for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1329                         {
1330                                 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1331                                 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1332                                 avgcolor[1] += ((c >>  5) & 0x3F) + ((c >> 21) & 0x3F);
1333                                 avgcolor[2] += ((c      ) & 0x1F) + ((c >> 16) & 0x1F);
1334                         }
1335                         f = (float)bytesperblock / size;
1336                         avgcolor[0] *= (0.5f / 31.0f) * f;
1337                         avgcolor[1] *= (0.5f / 63.0f) * f;
1338                         avgcolor[2] *= (0.5f / 31.0f) * f;
1339                         avgcolor[3] = 1; // too hard to calculate
1340                 }
1341                 else
1342                 {
1343                         for (i = 0;i < size;i += 4)
1344                         {
1345                                 avgcolor[0] += ddspixels[i+2];
1346                                 avgcolor[1] += ddspixels[i+1];
1347                                 avgcolor[2] += ddspixels[i];
1348                                 avgcolor[3] += ddspixels[i+3];
1349                         }
1350                         f = (1.0f / 255.0f) * bytesperpixel / size;
1351                         avgcolor[0] *= f;
1352                         avgcolor[1] *= f;
1353                         avgcolor[2] *= f;
1354                         avgcolor[3] *= f;
1355                 }
1356         }
1357
1358         if (dds_miplevels > 1)
1359                 flags |= TEXF_MIPMAP;
1360         else
1361                 flags &= ~TEXF_MIPMAP;
1362
1363         // if S3TC is not supported, there's very little we can do about it
1364         if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1365         {
1366                 Mem_Free(dds);
1367                 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1368                 return NULL;
1369         }
1370
1371         texinfo = R_GetTexTypeInfo(textype, flags);
1372
1373         glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
1374         strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1375         glt->pool = pool;
1376         glt->chain = pool->gltchain;
1377         pool->gltchain = glt;
1378         glt->inputwidth = dds_width;
1379         glt->inputheight = dds_height;
1380         glt->inputdepth = 1;
1381         glt->flags = flags;
1382         glt->textype = texinfo;
1383         glt->texturetype = GLTEXTURETYPE_2D;
1384         glt->inputdatasize = ddssize;
1385         glt->glinternalformat = texinfo->glinternalformat;
1386         glt->glformat = texinfo->glformat;
1387         glt->gltype = texinfo->gltype;
1388         glt->bytesperpixel = texinfo->internalbytesperpixel;
1389         glt->sides = 1;
1390         glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1391         glt->tilewidth = dds_width;
1392         glt->tileheight = dds_height;
1393         glt->tiledepth = 1;
1394
1395         // texture uploading can take a while, so make sure we're sending keepalives
1396         CL_KeepaliveMessage(false);
1397
1398         // upload the texture
1399         // we need to restore the texture binding after finishing the upload
1400         CHECKGLERROR
1401         GL_ActiveTexture(0);
1402         oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1403         qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1404         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1405         mippixels = ddspixels;
1406         mipwidth = dds_width;
1407         mipheight = dds_height;
1408         mipcomplete = false;
1409         for (mip = 0;mip < dds_miplevels+1;mip++)
1410         {
1411                 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1412                 if (mippixels + mipsize > dds + ddssize)
1413                         break;
1414                 if (bytesperblock)
1415                 {
1416                         qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1417                 }
1418                 else
1419                 {
1420                         qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1421                 }
1422                 mippixels += mipsize;
1423                 if (mipwidth <= 1 && mipheight <= 1)
1424                 {
1425                         mipcomplete = true;
1426                         break;
1427                 }
1428                 if (mipwidth > 1)
1429                         mipwidth >>= 1;
1430                 if (mipheight > 1)
1431                         mipheight >>= 1;
1432         }
1433         if (dds_miplevels > 1 && !mipcomplete)
1434         {
1435                 // need to set GL_TEXTURE_MAX_LEVEL
1436                 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1437         }
1438         GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1439         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1440
1441         Mem_Free(dds);
1442         return (rtexture_t *)glt;
1443 }
1444
1445 int R_TextureWidth(rtexture_t *rt)
1446 {
1447         return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1448 }
1449
1450 int R_TextureHeight(rtexture_t *rt)
1451 {
1452         return rt ? ((gltexture_t *)rt)->inputheight : 0;
1453 }
1454
1455 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1456 {
1457         gltexture_t *glt = (gltexture_t *)rt;
1458         if (data == NULL)
1459                 Host_Error("R_UpdateTexture: no data supplied");
1460         if (glt == NULL)
1461                 Host_Error("R_UpdateTexture: no texture supplied");
1462         if (!glt->texnum)
1463                 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1464         // update part of the texture
1465         if (glt->bufferpixels)
1466         {
1467                 int j;
1468                 int bpp = glt->bytesperpixel;
1469                 int inputskip = width*bpp;
1470                 int outputskip = glt->tilewidth*bpp;
1471                 const unsigned char *input = data;
1472                 unsigned char *output = glt->bufferpixels;
1473                 if (x < 0)
1474                 {
1475                         width += x;
1476                         input -= x*bpp;
1477                         x = 0;
1478                 }
1479                 if (y < 0)
1480                 {
1481                         height += y;
1482                         input -= y*inputskip;
1483                         y = 0;
1484                 }
1485                 if (width > glt->tilewidth - x)
1486                         width = glt->tilewidth - x;
1487                 if (height > glt->tileheight - y)
1488                         height = glt->tileheight - y;
1489                 if (width < 1 || height < 1)
1490                         return;
1491                 glt->dirty = true;
1492                 glt->buffermodified = true;
1493                 output += y*outputskip + x*bpp;
1494                 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1495                         memcpy(output, input, width*bpp);
1496         }
1497         else
1498                 R_Upload(glt, data, x, y, 0, width, height, 1);
1499 }
1500
1501 int R_RealGetTexture(rtexture_t *rt)
1502 {
1503         if (rt)
1504         {
1505                 gltexture_t *glt;
1506                 glt = (gltexture_t *)rt;
1507                 if (glt->flags & GLTEXF_DYNAMIC)
1508                         R_UpdateDynamicTexture(glt);
1509                 if (glt->buffermodified && glt->bufferpixels)
1510                 {
1511                         glt->buffermodified = false;
1512                         R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1513                 }
1514                 glt->dirty = false;
1515                 return glt->texnum;
1516         }
1517         else
1518                 return 0;
1519 }
1520
1521 void R_ClearTexture (rtexture_t *rt)
1522 {
1523         gltexture_t *glt = (gltexture_t *)rt;
1524
1525         R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );
1526 }