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