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