improved TraceLine in chase.c to be more generally useful (should move it to another...
[divverent/darkplaces.git] / gl_textures.c
1 #include "quakedef.h"
2
3 cvar_t          gl_max_size = {"gl_max_size", "2048"};
4 cvar_t          gl_picmip = {"gl_picmip", "0"};
5 cvar_t          gl_lerpimages = {"gl_lerpimages", "1"};
6 cvar_t          r_upload = {"r_upload", "1"};
7
8 int             gl_filter_min = GL_LINEAR_MIPMAP_LINEAR; //NEAREST;
9 int             gl_filter_max = GL_LINEAR;
10
11
12 int             texels;
13
14 // 65536x65536
15 #define MAXMIPS 16
16
17 typedef struct
18 {
19         char    identifier[64];
20         int             texnum;
21         int             texeldatasize;
22         byte    *texels[MAXMIPS];
23         unsigned short texelsize[MAXMIPS][2];
24         unsigned short width, height;
25 // LordHavoc: CRC to identify cache mismatchs
26         unsigned short crc;
27         char    mipmap;
28         char    alpha;
29         char    bytesperpixel;
30         char    lerped; // whether this texture was uploaded with or without interpolation
31         char    inuse; // cleared during texture purge when loading new level
32         char    pad; // unused
33 } gltexture_t;
34
35 #define MAX_GLTEXTURES  4096
36 gltexture_t     *gltextures;
37 int                     numgltextures;
38
39 typedef struct
40 {
41         char *name;
42         int     minimize, maximize;
43 } glmode_t;
44
45 glmode_t modes[] = {
46         {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
47         {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
48         {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
49         {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
50         {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
51         {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
52 };
53
54 /*
55 ===============
56 Draw_TextureMode_f
57 ===============
58 */
59 void Draw_TextureMode_f (void)
60 {
61         int             i;
62         gltexture_t     *glt;
63
64         if (Cmd_Argc() == 1)
65         {
66                 for (i=0 ; i< 6 ; i++)
67                         if (gl_filter_min == modes[i].minimize)
68                         {
69                                 Con_Printf ("%s\n", modes[i].name);
70                                 return;
71                         }
72                 Con_Printf ("current filter is unknown???\n");
73                 return;
74         }
75
76         for (i=0 ; i< 6 ; i++)
77         {
78                 if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) )
79                         break;
80         }
81         if (i == 6)
82         {
83                 Con_Printf ("bad filter name\n");
84                 return;
85         }
86
87         gl_filter_min = modes[i].minimize;
88         gl_filter_max = modes[i].maximize;
89
90         if (!r_upload.value)
91                 return;
92         // change all the existing mipmap texture objects
93         for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
94         {
95                 if (glt->mipmap)
96                 {
97                         glBindTexture(GL_TEXTURE_2D, glt->texnum);
98                         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
99                         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
100                 }
101         }
102 }
103
104 void GL_TextureStats_Print(char *name, int total, int crc, int mip, int alpha)
105 {
106         char n[64];
107         int c = 0;
108         if (!name[0])
109                 name = "<unnamed>";
110         while (name[c] && c < 28)
111                 n[c++] = name[c];
112         // no need to pad since the name was moved to last
113 //      while (c < 28)
114 //              n[c++] = ' ';
115         n[c] = 0;
116         Con_Printf("%5i %04X %s %s %s\n", total, crc, mip ? "yes" : "no ", alpha ? "yes  " : "no   ", n);
117 }
118
119 void GL_TextureStats_f(void)
120 {
121         int i, s = 0, sc = 0, t = 0;
122         gltexture_t *glt;
123         Con_Printf("kbytes crc  mip alpha name\n");
124         for (i = 0, glt = gltextures;i < numgltextures;i++, glt++)
125         {
126                 GL_TextureStats_Print(glt->identifier, (glt->texeldatasize + 512) >> 10, glt->crc, glt->mipmap, glt->alpha);
127                 t += glt->texeldatasize;
128                 if (glt->identifier[0] == '&')
129                 {
130                         sc++;
131                         s += glt->texeldatasize;
132                 }
133         }
134         Con_Printf("%i textures, totalling %.3fMB, %i are (usually) unnecessary model skins totalling %.3fMB\n", numgltextures, t / 1048576.0, sc, s / 1048576.0);
135 }
136
137 void GL_TextureStats_PrintTotal(void)
138 {
139         int i, s = 0, sc = 0, t = 0;
140         gltexture_t *glt;
141         for (i = 0, glt = gltextures;i < numgltextures;i++, glt++)
142         {
143                 t += glt->texeldatasize;
144                 if (glt->identifier[0] == '&')
145                 {
146                         sc++;
147                         s += glt->texeldatasize;
148                 }
149         }
150         Con_Printf("%i textures, totalling %.3fMB, %i are (usually) unnecessary model skins totalling %.3fMB\n", numgltextures, t / 1048576.0, sc, s / 1048576.0);
151 }
152
153 char engineversion[40];
154
155 //void GL_UploadTexture (gltexture_t *glt);
156 void gl_textures_start()
157 {
158 //      int i;
159 //      gltexture_t *glt;
160 //      for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
161 //              GL_UploadTexture(glt);
162 }
163
164 void gl_textures_shutdown()
165 {
166 }
167
168 void GL_Textures_Init (void)
169 {
170         Cmd_AddCommand("r_texturestats", GL_TextureStats_f);
171         Cvar_RegisterVariable (&gl_max_size);
172         Cvar_RegisterVariable (&gl_picmip);
173         Cvar_RegisterVariable (&gl_lerpimages);
174         Cvar_RegisterVariable (&r_upload);
175 #ifdef NORENDER
176         r_upload.value = 0;
177 #endif
178
179         // 3dfx can only handle 256 wide textures
180         if (!Q_strncasecmp ((char *)gl_renderer, "3dfx",4) || strstr((char *)gl_renderer, "Glide"))
181                 Cvar_Set ("gl_max_size", "256");
182
183         gltextures = qmalloc(sizeof(gltexture_t) * MAX_GLTEXTURES);
184         memset(gltextures, 0, sizeof(gltexture_t) * MAX_GLTEXTURES);
185         Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f);
186
187         R_RegisterModule("GL_Textures", gl_textures_start, gl_textures_shutdown);
188 }
189
190 /*
191 ================
192 GL_FindTexture
193 ================
194 */
195 int GL_FindTexture (char *identifier)
196 {
197         int             i;
198         gltexture_t     *glt;
199
200         for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
201         {
202                 if (!strcmp (identifier, glt->identifier))
203                         return gltextures[i].texnum;
204         }
205
206         return -1;
207 }
208
209 void GL_ResampleTextureLerpLine (byte *in, byte *out, int inwidth, int outwidth)
210 {
211         int             j, xi, oldx = 0, f, fstep, l1, l2, endx;
212         fstep = (int) (inwidth*65536.0f/outwidth);
213         endx = (inwidth-1);
214         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
215         {
216                 xi = (int) f >> 16;
217                 if (xi != oldx)
218                 {
219                         in += (xi - oldx) * 4;
220                         oldx = xi;
221                 }
222                 if (xi < endx)
223                 {
224                         l2 = f & 0xFFFF;
225                         l1 = 0x10000 - l2;
226                         *out++ = (byte) ((in[0] * l1 + in[4] * l2) >> 16);
227                         *out++ = (byte) ((in[1] * l1 + in[5] * l2) >> 16);
228                         *out++ = (byte) ((in[2] * l1 + in[6] * l2) >> 16);
229                         *out++ = (byte) ((in[3] * l1 + in[7] * l2) >> 16);
230                 }
231                 else // last pixel of the line has no pixel to lerp to
232                 {
233                         *out++ = in[0];
234                         *out++ = in[1];
235                         *out++ = in[2];
236                         *out++ = in[3];
237                 }
238         }
239 }
240
241 /*
242 ================
243 GL_ResampleTexture
244 ================
245 */
246 void GL_ResampleTexture (void *indata, int inwidth, int inheight, void *outdata,  int outwidth, int outheight)
247 {
248         if (gl_lerpimages.value)
249         {
250                 int             i, j, yi, oldy, f, fstep, l1, l2, endy = (inheight-1);
251                 byte    *inrow, *out, *row1, *row2;
252                 out = outdata;
253                 fstep = (int) (inheight*65536.0f/outheight);
254
255                 row1 = qmalloc(outwidth*4);
256                 row2 = qmalloc(outwidth*4);
257                 inrow = indata;
258                 oldy = 0;
259                 GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
260                 GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
261                 for (i = 0, f = 0;i < outheight;i++,f += fstep)
262                 {
263                         yi = f >> 16;
264                         if (yi != oldy)
265                         {
266                                 inrow = (byte *)indata + inwidth*4*yi;
267                                 if (yi == oldy+1)
268                                         memcpy(row1, row2, outwidth*4);
269                                 else
270                                         GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
271                                 if (yi < endy)
272                                         GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
273                                 else
274                                         memcpy(row2, row1, outwidth*4);
275                                 oldy = yi;
276                         }
277                         if (yi < endy)
278                         {
279                                 l2 = f & 0xFFFF;
280                                 l1 = 0x10000 - l2;
281                                 for (j = 0;j < outwidth;j++)
282                                 {
283                                         *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
284                                         *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
285                                         *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
286                                         *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
287                                 }
288                                 row1 -= outwidth*4;
289                                 row2 -= outwidth*4;
290                         }
291                         else // last line has no pixels to lerp to
292                         {
293                                 for (j = 0;j < outwidth;j++)
294                                 {
295                                         *out++ = *row1++;
296                                         *out++ = *row1++;
297                                         *out++ = *row1++;
298                                         *out++ = *row1++;
299                                 }
300                                 row1 -= outwidth*4;
301                         }
302                 }
303                 qfree(row1);
304                 qfree(row2);
305         }
306         else
307         {
308                 int             i, j;
309                 unsigned        frac, fracstep;
310                 byte    *inrow, *out, *inpix;
311                 out = outdata;
312
313                 fracstep = inwidth*0x10000/outwidth;
314                 for (i=0 ; i<outheight ; i++)
315                 {
316                         inrow = (byte *)indata + inwidth*(i*inheight/outheight)*4;
317                         frac = fracstep >> 1;
318                         for (j=0 ; j<outwidth ; j+=4)
319                         {
320                                 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ =       *inpix++ ;frac += fracstep;
321                                 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ =       *inpix++ ;frac += fracstep;
322                                 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ =       *inpix++ ;frac += fracstep;
323                                 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ =       *inpix++ ;frac += fracstep;
324                         }
325                 }
326         }
327 }
328
329 void GL_FreeTexels(gltexture_t *glt)
330 {
331         if (glt->texels[0])
332                 qfree(glt->texels[0]);
333         glt->texels[0] = 0;
334 }
335
336 void GL_AllocTexels(gltexture_t *glt, int width, int height, int mipmapped)
337 {
338         int i, w, h, size;
339         if (glt->texels[0])
340                 GL_FreeTexels(glt);
341         glt->texelsize[0][0] = width;
342         glt->texelsize[0][1] = height;
343         if (mipmapped)
344         {
345                 size = 0;
346                 w = width;h = height;
347                 i = 0;
348                 while (i < MAXMIPS)
349                 {
350                         glt->texelsize[i][0] = w;
351                         glt->texelsize[i][1] = h;
352                         glt->texels[i++] = (void *)size;
353                         size += w*h*4;
354                         if (w > 1)
355                         {
356                                 w >>= 1;
357                                 if (h > 1)
358                                         h >>= 1;
359                         }
360                         else if (h > 1)
361                                 h >>= 1;
362                         else
363                                 break;
364                 }
365                 glt->texeldatasize = size;
366                 while (i < MAXMIPS)
367                         glt->texels[i++] = NULL;
368                 glt->texels[0] = qmalloc(size);
369                 for (i = 1;i < MAXMIPS && glt->texels[i];i++)
370                         glt->texels[i] += (int) glt->texels[0];
371         }
372         else
373         {
374                 size = width*height*4;
375                 glt->texeldatasize = size;
376                 glt->texels[0] = qmalloc(size);
377                 for (i = 1;i < MAXMIPS;i++)
378                         glt->texels[i] = NULL;
379         }
380         if (!glt->texels[0])
381                 Sys_Error("GL_AllocTexels: out of memory\n");
382 }
383
384 // in can be the same as out
385 void GL_MipReduce(byte *in, byte *out, int width, int height, int destwidth, int destheight)
386 {
387         int x, y, width2, height2, nextrow;
388         if (width > destwidth)
389         {
390                 if (height > destheight)
391                 {
392                         // reduce both
393                         width2 = width >> 1;
394                         height2 = height >> 1;
395                         nextrow = width << 2;
396                         for (y = 0;y < height2;y++)
397                         {
398                                 for (x = 0;x < width2;x++)
399                                 {
400                                         out[0] = (byte) ((in[0] + in[4] + in[nextrow  ] + in[nextrow+4]) >> 2);
401                                         out[1] = (byte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
402                                         out[2] = (byte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
403                                         out[3] = (byte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
404                                         out += 4;
405                                         in += 8;
406                                 }
407                                 in += nextrow; // skip a line
408                         }
409                 }
410                 else
411                 {
412                         // reduce width
413                         width2 = width >> 1;
414                         for (y = 0;y < height;y++)
415                         {
416                                 for (x = 0;x < width2;x++)
417                                 {
418                                         out[0] = (byte) ((in[0] + in[4]) >> 1);
419                                         out[1] = (byte) ((in[1] + in[5]) >> 1);
420                                         out[2] = (byte) ((in[2] + in[6]) >> 1);
421                                         out[3] = (byte) ((in[3] + in[7]) >> 1);
422                                         out += 4;
423                                         in += 8;
424                                 }
425                         }
426                 }
427         }
428         else
429         {
430                 if (height > destheight)
431                 {
432                         // reduce height
433                         height2 = height >> 1;
434                         nextrow = width << 2;
435                         for (y = 0;y < height2;y++)
436                         {
437                                 for (x = 0;x < width;x++)
438                                 {
439                                         out[0] = (byte) ((in[0] + in[nextrow  ]) >> 1);
440                                         out[1] = (byte) ((in[1] + in[nextrow+1]) >> 1);
441                                         out[2] = (byte) ((in[2] + in[nextrow+2]) >> 1);
442                                         out[3] = (byte) ((in[3] + in[nextrow+3]) >> 1);
443                                         out += 4;
444                                         in += 4;
445                                 }
446                                 in += nextrow; // skip a line
447                         }
448                 }
449                 else
450                         Sys_Error("GL_MipReduce: desired size already achieved\n");
451         }
452 }
453
454 void GL_UploadTexture (gltexture_t *glt)
455 {
456         int mip, width, height;
457         if (!r_upload.value)
458                 return;
459         glBindTexture(GL_TEXTURE_2D, glt->texnum);
460         width = glt->width;
461         height = glt->height;
462         for (mip = 0;mip < MAXMIPS && glt->texels[mip];mip++)
463                 glTexImage2D(GL_TEXTURE_2D, mip, glt->alpha ? 4 : 3, glt->texelsize[mip][0], glt->texelsize[mip][1], 0, GL_RGBA, GL_UNSIGNED_BYTE, glt->texels[mip]);
464         if (glt->mipmap)
465         {
466                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
467                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
468         }
469         else
470         {
471                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
472                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
473         }
474         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
475 }
476
477 /*
478 ================
479 GL_LoadTexture
480 ================
481 */
482 int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel)
483 {
484         unsigned short  crc;
485         int                             i, width2, height2, width3, height3, w, h, mip;
486         gltexture_t             *glt, *freeglt;
487         // LordHavoc: texture caching, turned out to be a waste of time (and immense waste of diskspace)
488         //char                  cachefilename[1024], *cachefile;
489
490         if (isDedicated)
491                 return 1;
492
493         freeglt = NULL;
494
495         // LordHavoc: do a CRC to confirm the data really is the same as previous occurances.
496         crc = CRC_Block(data, width*height*bytesperpixel);
497         // see if the texture is already present
498         if (identifier[0])
499         {
500                 for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
501                 {
502                         if (glt->inuse)
503                         {
504                                 if (!strcmp (identifier, glt->identifier))
505                                 {
506                                         // LordHavoc: everyone hates cache mismatchs, so I fixed it
507                                         if (crc != glt->crc || width != glt->width || height != glt->height)
508                                         {
509                                                 Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n");
510                                                 goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
511                                         }
512                                         if ((gl_lerpimages.value != 0) != glt->lerped)
513                                                 goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
514                                         return glt->texnum;
515                                 }
516                         }
517                         else
518                                 freeglt = glt;
519                 }
520         }
521         else
522                 i = 0;
523         // LordHavoc: although this could be an else condition as it was in the original id code,
524         //            it is more clear this way
525         if (freeglt)
526         {
527                 glt = freeglt;
528                 strcpy (glt->identifier, identifier);
529         }
530         else
531         {
532                 // LordHavoc: check if there are still slots available
533                 if (numgltextures >= MAX_GLTEXTURES)
534                         Sys_Error ("GL_LoadTexture: ran out of texture slots (%d)\n", MAX_GLTEXTURES);
535                 glt = &gltextures[numgltextures++];
536                 glt->texnum = texture_extension_number;
537                 texture_extension_number++;
538                 strcpy (glt->identifier, identifier);
539         }
540
541 // LordHavoc: label to drop out of the loop into the setup code
542 GL_LoadTexture_setup:
543         // calculate power of 2 size
544         width2 = 1;while (width2 < width) width2 <<= 1;
545         height2 = 1;while (height2 < height) height2 <<= 1;
546         // calculate final size (mipmapped downward to this)
547         width3 = width2 >> (int) gl_picmip.value;
548         height3 = height2 >> (int) gl_picmip.value;
549         while (width3 > (int) gl_max_size.value) width3 >>= 1;
550         while (height3 > (int) gl_max_size.value) height3 >>= 1;
551         if (width3 < 1) width3 = 1;
552         if (height3 < 1) height3 = 1;
553
554         // final storage
555         GL_AllocTexels(glt, width3, height3, mipmap);
556         glt->crc = crc; // LordHavoc: used to verify textures are identical
557         glt->width = width;
558         glt->height = height;
559         glt->mipmap = mipmap;
560         glt->bytesperpixel = bytesperpixel;
561         glt->lerped = gl_lerpimages.value != 0;
562         glt->alpha = false; // updated later
563         glt->inuse = true;
564         /*
565         // LordHavoc: texture caching, turned out to be a waste of time (and immense waste of diskspace)
566         sprintf(cachefilename, "%s%x%x%x.texels", identifier, width3, height3, crc);
567         for (i = 0;cachefilename[i];i++)
568         {
569                 if (cachefilename[i] <= ' ' || cachefilename[i] >= 127 || cachefilename[i] == '/' || cachefilename[i] == '\\' || cachefilename[i] == ':' || cachefilename[i] == '*' || cachefilename[i] == '?')
570                         cachefilename[i] = '@';
571                 if (cachefilename[i] >= 'A' && cachefilename[i] <= 'Z')
572                         cachefilename[i] += 'a' - 'A';
573         }
574         cachefile = COM_LoadMallocFile(cachefilename, true);
575         if (cachefile)
576         {
577                 if (cachefile[0] == 'D' && cachefile[1] == 'P' && cachefile[2] == 'C' && cachefile[3] == 'T')
578                 {
579                         memcpy(glt->texels[0], cachefile + 4, width3*height3*4);
580                         qfree(cachefile);
581 //                      Con_Printf("loaded cache texture %s\n", cachefilename);
582                         goto cacheloaded;
583                 }
584                 else
585                         qfree(cachefile);
586         }
587         */
588         if (width == width3 && height == height3) // perfect match
589         {
590                 if (bytesperpixel == 1) // 8bit
591                         Image_Copy8bitRGBA(data, glt->texels[0], width*height, d_8to24table);
592                 else
593                         Image_CopyRGBAGamma(data, glt->texels[0], width*height);
594         }
595         else if (width == width2 && height == height2) // perfect match for top level, but needs to be reduced
596         {
597                 byte *temptexels2;
598                 temptexels2 = qmalloc(width2*height2*4); // scaleup buffer
599                 if (bytesperpixel == 1) // 8bit
600                         Image_Copy8bitRGBA(data, temptexels2, width*height, d_8to24table);
601                 else
602                         Image_CopyRGBAGamma(data, temptexels2, width*height);
603                 while (width2 > width3 || height2 > height3)
604                 {
605                         w = width2;h = height2;
606                         if (width2 > width3) width2 >>= 1;
607                         if (height2 > height3) height2 >>= 1;
608                         if (width2 <= width3 && height2 <= height3) // size achieved
609                                 GL_MipReduce(temptexels2, glt->texels[0], w, h, width3, height3);
610                         else
611                                 GL_MipReduce(temptexels2, temptexels2, w, h, width3, height3);
612                 }
613                 qfree(temptexels2);
614         }
615         else // scaling...
616         {
617                 byte *temptexels;
618                 // pre-scaleup buffer
619                 temptexels = qmalloc(width*height*4);
620                 if (bytesperpixel == 1) // 8bit
621                         Image_Copy8bitRGBA(data, temptexels, width*height, d_8to24table);
622                 else
623                         Image_CopyRGBAGamma(data, temptexels, width*height);
624                 if (width2 != width3 || height2 != height3) // reduced by gl_pic_mip or gl_max_size
625                 {
626                         byte *temptexels2;
627                         temptexels2 = qmalloc(width2*height2*4); // scaleup buffer
628                         GL_ResampleTexture(temptexels, width, height, temptexels2, width2, height2);
629                         while (width2 > width3 || height2 > height3)
630                         {
631                                 w = width2;h = height2;
632                                 if (width2 > width3) width2 >>= 1;
633                                 if (height2 > height3) height2 >>= 1;
634                                 if (width2 <= width3 && height2 <= height3) // size achieved
635                                         GL_MipReduce(temptexels2, glt->texels[0], w, h, width3, height3);
636                                 else
637                                         GL_MipReduce(temptexels2, temptexels2, w, h, width3, height3);
638                         }
639                         qfree(temptexels2);
640                 }
641                 else // copy directly
642                         GL_ResampleTexture(temptexels, width, height, glt->texels[0], width2, height2);
643                 qfree(temptexels);
644         }
645         /*
646         // LordHavoc: texture caching, turned out to be a waste of time (and immense waste of diskspace)
647         Con_Printf("writing cache texture %s\n", cachefilename);
648         cachefile = qmalloc(width3*height3*4 + 4);
649         cachefile[0] = 'D';
650         cachefile[1] = 'P';
651         cachefile[2] = 'C';
652         cachefile[3] = 'T';
653         memcpy(cachefile + 4, glt->texels[0], width3*height3*4);
654         COM_WriteFile(cachefilename, cachefile, width3*height3*4 + 4);
655         qfree(cachefile);
656 cacheloaded:
657         */
658         if (alpha)
659         {
660                 byte    *in = glt->texels[0] + 3;
661                 for (i = 0;i < width*height;i++, in += 4)
662                         if (*in < 255)
663                         {
664                                 glt->alpha = true;
665                                 break;
666                         }
667         }
668         // this loop is skipped if there are no mipmaps to generate
669         for (mip = 1;mip < MAXMIPS && glt->texels[mip];mip++)
670                 GL_MipReduce(glt->texels[mip-1], glt->texels[mip], glt->texelsize[mip-1][0], glt->texelsize[mip-1][1], 1, 1);
671         GL_UploadTexture(glt);
672         GL_FreeTexels(glt);
673
674 //      if (bytesperpixel == 1) // 8bit
675 //              GL_Upload8 (data, width, height, mipmap, alpha);
676 //      else // 32bit
677 //              GL_Upload32 (data, width, height, mipmap, true);
678
679         return glt->texnum;
680 }