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