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