1 // texture.cpp: texture management
2 
3 #include "cube.h"
4 
5 #define FUNCNAME(name) name##1
6 #define DEFPIXEL uint OP(r, 0);
7 #define PIXELOP OP(r, 0);
8 #define BPP 1
9 #include "scale.h"
10 
11 #define FUNCNAME(name) name##2
12 #define DEFPIXEL uint OP(r, 0), OP(g, 1);
13 #define PIXELOP OP(r, 0); OP(g, 1);
14 #define BPP 2
15 #include "scale.h"
16 
17 #define FUNCNAME(name) name##3
18 #define DEFPIXEL uint OP(r, 0), OP(g, 1), OP(b, 2);
19 #define PIXELOP OP(r, 0); OP(g, 1); OP(b, 2);
20 #define BPP 3
21 #include "scale.h"
22 
23 #define FUNCNAME(name) name##4
24 #define DEFPIXEL uint OP(r, 0), OP(g, 1), OP(b, 2), OP(a, 3);
25 #define PIXELOP OP(r, 0); OP(g, 1); OP(b, 2); OP(a, 3);
26 #define BPP 4
27 #include "scale.h"
28 
scaletexture(uchar * src,uint sw,uint sh,uint bpp,uchar * dst,uint dw,uint dh)29 void scaletexture(uchar *src, uint sw, uint sh, uint bpp, uchar *dst, uint dw, uint dh)
30 {
31     if(sw == dw*2 && sh == dh*2)
32     {
33         switch(bpp)
34         {
35             case 1: return halvetexture1(src, sw, sh, dst);
36             case 2: return halvetexture2(src, sw, sh, dst);
37             case 3: return halvetexture3(src, sw, sh, dst);
38             case 4: return halvetexture4(src, sw, sh, dst);
39         }
40     }
41     else if(sw < dw || sh < dh || sw&(sw-1) || sh&(sh-1))
42     {
43         switch(bpp)
44         {
45             case 1: return scaletexture1(src, sw, sh, dst, dw, dh);
46             case 2: return scaletexture2(src, sw, sh, dst, dw, dh);
47             case 3: return scaletexture3(src, sw, sh, dst, dw, dh);
48             case 4: return scaletexture4(src, sw, sh, dst, dw, dh);
49         }
50     }
51     else
52     {
53         switch(bpp)
54         {
55             case 1: return shifttexture1(src, sw, sh, dst, dw, dh);
56             case 2: return shifttexture2(src, sw, sh, dst, dw, dh);
57             case 3: return shifttexture3(src, sw, sh, dst, dw, dh);
58             case 4: return shifttexture4(src, sw, sh, dst, dw, dh);
59         }
60     }
61 }
62 
63 Texture *notexture = NULL, *noworldtexture = NULL;
64 
65 hashtable<char *, Texture> textures;
66 
67 VAR(hwtexsize, 1, 0, 0);
68 VAR(hwmaxaniso, 1, 0, 0);
69 VARFP(maxtexsize, 0, 0, 1<<12, initwarning("texture quality", INIT_LOAD));
70 VARFP(texreduce, -1, 0, 3, initwarning("texture quality", INIT_LOAD));
71 VARFP(trilinear, 0, 1, 1, initwarning("texture filtering", INIT_LOAD));
72 VARFP(bilinear, 0, 1, 1, initwarning("texture filtering", INIT_LOAD));
73 VARFP(aniso, 0, 0, 16, initwarning("texture filtering", INIT_LOAD));
74 
formatsize(GLenum format)75 int formatsize(GLenum format)
76 {
77     switch(format)
78     {
79         case GL_LUMINANCE:
80         case GL_ALPHA: return 1;
81         case GL_LUMINANCE_ALPHA: return 2;
82         case GL_RGB: return 3;
83         case GL_RGBA: return 4;
84         default: return 4;
85     }
86 }
87 
resizetexture(int w,int h,bool mipmap,bool canreduce,GLenum target,int & tw,int & th)88 void resizetexture(int w, int h, bool mipmap, bool canreduce, GLenum target, int &tw, int &th)
89 {
90     int hwlimit = hwtexsize,
91         sizelimit = mipmap && maxtexsize ? min(maxtexsize, hwlimit) : hwlimit;
92     if(canreduce && texreduce)
93     {
94         if(texreduce==-1)
95         {
96             w = 2;
97             h = 2;
98         }
99         else
100         {
101             w = max(w>>texreduce, 2); // 1);
102             h = max(h>>texreduce, 2); // 1);
103         }
104     }
105     w = min(w, sizelimit);
106     h = min(h, sizelimit);
107     if(mipmap || w&(w-1) || h&(h-1))
108     {
109         tw = th = 1;
110         while(tw < w) tw *= 2;
111         while(th < h) th *= 2;
112         if(w < tw - tw/4) tw /= 2;
113         if(h < th - th/4) th /= 2;
114     }
115     else
116     {
117         tw = w;
118         th = h;
119     }
120 }
121 
uploadtexture(GLenum target,GLenum internal,int tw,int th,GLenum format,GLenum type,void * pixels,int pw,int ph,bool mipmap)122 void uploadtexture(GLenum target, GLenum internal, int tw, int th, GLenum format, GLenum type, void *pixels, int pw, int ph, bool mipmap)
123 {
124     int bpp = formatsize(format);
125     uchar *buf = NULL;
126     if(pw!=tw || ph!=th)
127     {
128         buf = new uchar[tw*th*bpp];
129         scaletexture((uchar *)pixels, pw, ph, bpp, buf, tw, th);
130     }
131     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
132     for(int level = 0;; level++)
133     {
134         uchar *src = buf ? buf : (uchar *)pixels;
135         glTexImage2D(target, level, internal, tw, th, 0, format, type, src);
136         if(!mipmap || max(tw, th) <= 1) break;
137         int srcw = tw, srch = th;
138         if(tw > 1) tw /= 2;
139         if(th > 1) th /= 2;
140         if(!buf) buf = new uchar[tw*th*bpp];
141         scaletexture(src, srcw, srch, bpp, buf, tw, th);
142     }
143     if(buf) delete[] buf;
144 }
145 
createtexture(int tnum,int w,int h,void * pixels,int clamp,bool mipmap,bool canreduce,GLenum format)146 void createtexture(int tnum, int w, int h, void *pixels, int clamp, bool mipmap, bool canreduce, GLenum format)
147 {
148     glBindTexture(GL_TEXTURE_2D, tnum);
149     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp&1 ? GL_CLAMP_TO_EDGE : GL_REPEAT);
150     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp&2 ? GL_CLAMP_TO_EDGE : GL_REPEAT);
151     if(hasAF && min(aniso, hwmaxaniso) > 0 && mipmap) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, min(aniso, hwmaxaniso));
152     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, bilinear ? GL_LINEAR : GL_NEAREST);
153     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
154         mipmap ?
155             (trilinear ?
156                 (bilinear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR) :
157                 (bilinear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST)) :
158             (bilinear ? GL_LINEAR : GL_NEAREST));
159 
160     int tw = w, th = h;
161     if(pixels) resizetexture(w, h, mipmap, canreduce, GL_TEXTURE_2D, tw, th);
162     uploadtexture(GL_TEXTURE_2D, format, tw, th, format, GL_UNSIGNED_BYTE, pixels, w, h, mipmap);
163 }
164 
165 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
166 #define RGBAMASKS 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff
167 #define RGBMASKS  0xff0000, 0x00ff00, 0x0000ff, 0
168 #else
169 #define RGBAMASKS 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
170 #define RGBMASKS  0x0000ff, 0x00ff00, 0xff0000, 0
171 #endif
172 
wrapsurface(void * data,int width,int height,int bpp)173 SDL_Surface *wrapsurface(void *data, int width, int height, int bpp)
174 {
175     switch(bpp)
176     {
177         case 3: return SDL_CreateRGBSurfaceFrom(data, width, height, 8*bpp, bpp*width, RGBMASKS);
178         case 4: return SDL_CreateRGBSurfaceFrom(data, width, height, 8*bpp, bpp*width, RGBAMASKS);
179     }
180     return NULL;
181 }
182 
creatergbsurface(int width,int height)183 SDL_Surface *creatergbsurface(int width, int height)
184 {
185     return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, RGBMASKS);
186 }
187 
creatergbasurface(int width,int height)188 SDL_Surface *creatergbasurface(int width, int height)
189 {
190     return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, RGBAMASKS);
191 }
192 
forcergbsurface(SDL_Surface * os)193 SDL_Surface *forcergbsurface(SDL_Surface *os)
194 {
195     SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 24, RGBMASKS);
196     if(ns) SDL_BlitSurface(os, NULL, ns, NULL);
197     SDL_FreeSurface(os);
198     return ns;
199 }
200 
forcergbasurface(SDL_Surface * os)201 SDL_Surface *forcergbasurface(SDL_Surface *os)
202 {
203     SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 32, RGBAMASKS);
204     if(ns)
205     {
206         SDL_SetAlpha(os, 0, 0);
207         SDL_BlitSurface(os, NULL, ns, NULL);
208     }
209     SDL_FreeSurface(os);
210     return ns;
211 }
212 
checkgrayscale(SDL_Surface * s)213 bool checkgrayscale(SDL_Surface *s)
214 {
215     // gray scale images have 256 levels, no colorkey, and the palette is a ramp
216     if(s->format->palette)
217     {
218         if(s->format->palette->ncolors != 256 || s->format->colorkey) return false;
219         const SDL_Color *colors = s->format->palette->colors;
220         loopi(256) if(colors[i].r != i || colors[i].g != i || colors[i].b != i) return false;
221     }
222     return true;
223 }
224 
fixcl(SDL_Surface * s,bool check=true,Uint8 value=0,Uint8 mlimit=255)225 int fixcl(SDL_Surface *s, bool check = true, Uint8 value = 0, Uint8 mlimit = 255)
226 {
227     Uint32 pixel = 0;
228     int bpp = s->format->BytesPerPixel;
229     int F = 0, N = 0, t = 0;
230     while ( value > mlimit ) {t++; value >>= 1;}
231     int tmp = s->w * bpp;
232     for (int i = 0; i < tmp; i+=bpp)
233     {
234         for (int j = 0; j < s->h; j++)
235         {
236             Uint8 *p = (Uint8 *)s->pixels + j * s->w * bpp + i;
237             switch (bpp)
238             {
239                 case 1:
240                 {
241                     if (check) pixel = *p;
242                     else *p >>= t;
243                     break;
244                 }
245                 case 2:
246                 {
247                     if (check) pixel = *(Uint16 *)p;
248                     else { p[0] >>= t; p[1] >>= t; }
249                     break;
250                 }
251                 case 3:
252                 {
253                     if (check)
254                     {
255                         if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
256                             pixel = p[0] << 16 | p[1] << 8 | p[2];
257                         else
258                             pixel = p[0] | p[1] << 8 | p[2] << 16;
259                     }
260                     else { loopk(3) p[k] >>= t; }
261                     break;
262                 }
263                case 4:
264                {
265                    if (check) pixel = *(Uint32 *)p;
266                    else {
267                        Uint8 r = 0, g = 0, b = 0, a = 0;
268                        SDL_GetRGBA(pixel, s->format, &r, &g, &b, &a);
269                        r >>= t; g >>= t; b >>= t;
270                        Uint32 *q = (Uint32 *)p;
271                        *q = SDL_MapRGBA(s->format, r,g,b,a);
272                    }
273                    break;
274                }
275                 default: break;
276             }
277             if (check) {
278                 Uint8 r = 0, g = 0, b = 0, a = 0;
279                 SDL_GetRGBA(pixel, s->format, &r, &g, &b, &a);
280                 F += r > g ? ( r > b ? r : b ) : ( g > b ? g : b ); N++;
281             }
282         }
283     }
284     if (!N) return 0;
285     return F/N;
286 }
287 
fixsurfaceformat(SDL_Surface * s)288 SDL_Surface *fixsurfaceformat(SDL_Surface *s)
289 {
290     if(!s) return NULL;
291     if(!s->pixels || min(s->w, s->h) <= 0 || s->format->BytesPerPixel <= 0)
292     {
293         SDL_FreeSurface(s);
294         return NULL;
295     }
296     static const uint rgbmasks[] = { RGBMASKS }, rgbamasks[] = { RGBAMASKS };
297     switch(s->format->BytesPerPixel)
298     {
299         case 1:
300             if(!checkgrayscale(s)) return s->format->colorkey ? forcergbasurface(s) : forcergbsurface(s);
301             break;
302         case 3:
303             if(s->format->Rmask != rgbmasks[0] || s->format->Gmask != rgbmasks[1] || s->format->Bmask != rgbmasks[2])
304                 return forcergbsurface(s);
305             break;
306         case 4:
307             if(s->format->Rmask != rgbamasks[0] || s->format->Gmask != rgbamasks[1] || s->format->Bmask != rgbamasks[2] || s->format->Amask != rgbamasks[3])
308                 return s->format->Amask ? forcergbasurface(s) : forcergbsurface(s);
309             break;
310     }
311     return s;
312 }
313 
texformat(int bpp)314 GLenum texformat(int bpp)
315 {
316     switch(bpp)
317     {
318         case 8: return GL_LUMINANCE;
319         case 16: return GL_LUMINANCE_ALPHA;
320         case 24: return GL_RGB;
321         case 32: return GL_RGBA;
322         default: return 0;
323     }
324 }
325 
texdecal(SDL_Surface * s)326 SDL_Surface *texdecal(SDL_Surface *s)
327 {
328     SDL_Surface *m = SDL_CreateRGBSurface(SDL_SWSURFACE, s->w, s->h, 16, 0, 0, 0, 0);
329     if(!m) fatal("create surface");
330     uchar *dst = (uchar *)m->pixels, *src = (uchar *)s->pixels;
331     loopi(s->h*s->w)
332     {
333         *dst++ = *src;
334         *dst++ = 255 - *src;
335         src += s->format->BytesPerPixel;
336     }
337     SDL_FreeSurface(s);
338     return m;
339 }
340 
scalesurface(SDL_Surface * s,float scale)341 void scalesurface(SDL_Surface *s, float scale)
342 {
343     uint dw = s->w*scale, dh = s->h*scale;
344     uchar *buf = new uchar[dw*dh*s->format->BytesPerPixel];
345     scaletexture((uchar *)s->pixels, s->w, s->h, s->format->BytesPerPixel, buf, dw, dh);
346     delete[] (uchar *)s->pixels;
347     s->w = dw;
348     s->h = dh;
349     s->pixels = buf;
350 }
351 
352 bool silent_texture_load = false;
353 
354 VARFP(hirestextures, 0, 1, 1, initwarning("texture resolution", INIT_LOAD));
355 bool uniformtexres = !hirestextures;
356 
loadsurface(const char * texname,int & xs,int & ys,int & bpp,int clamp=0,bool mipmap=true,bool canreduce=false,float scale=1.0f,bool trydl=false)357 GLuint loadsurface(const char *texname, int &xs, int &ys, int &bpp, int clamp = 0, bool mipmap = true, bool canreduce = false, float scale = 1.0f, bool trydl = false)
358 {
359     const char *file = texname;
360     if(texname[0]=='<')
361     {
362         file = strchr(texname, '>');
363         if(!file) { if(!silent_texture_load) conoutf("could not load texture %s", texname); return 0; }
364         file++;
365     }
366 
367     SDL_Surface *s = NULL;
368     stream *z = openzipfile(file, "rb");
369     if(z)
370     {
371         SDL_RWops *rw = z->rwops();
372         if(rw)
373         {
374             s = IMG_Load_RW(rw, 0);
375             SDL_FreeRW(rw);
376         }
377         delete z;
378     }
379     if(!s) s = IMG_Load(findfile(file, "rb"));
380     if(!s)
381     {
382         if(trydl)
383             requirepackage(PCK_TEXTURE, file);
384         else if(!silent_texture_load) conoutf("couldn't load texture %s", texname);
385         return 0;
386     }
387     s = fixsurfaceformat(s);
388     Uint8 x = 0;
389     if(strstr(texname,"playermodel") && (x = fixcl(s)) > 35) { fixcl(s,false,x,35); }
390     else if(strstr(texname,"skin") && strstr(texname,"weapon") && (x = fixcl(s)) > 40 ) { fixcl(s,false,x,40); }
391 
392     GLenum format = texformat(s->format->BitsPerPixel);
393     if(!format)
394     {
395         SDL_FreeSurface(s);
396         conoutf("texture must be 8, 16, 24, or 32 bpp: %s", texname);
397         return 0;
398     }
399     if(max(s->w, s->h) > (1<<12))
400     {
401         SDL_FreeSurface(s);
402         conoutf("texture size exceeded %dx%d pixels: %s", 1<<12, 1<<12, texname);
403         return 0;
404     }
405 
406     if(texname[0]=='<')
407     {
408         const char *cmd = &texname[1], *arg1 = strchr(cmd, ':');//, *arg2 = arg1 ? strchr(arg1, ',') : NULL;
409         if(!arg1) arg1 = strchr(cmd, '>');
410         if(!strncmp(cmd, "decal", arg1-cmd)) { s = texdecal(s); format = texformat(s->format->BitsPerPixel); }
411     }
412 
413     if(uniformtexres && scale > 1.0f) scalesurface(s, 1.0f/scale);
414 
415     GLuint tnum;
416     glGenTextures(1, &tnum);
417     createtexture(tnum, s->w, s->h, s->pixels, clamp, mipmap, canreduce, format);
418     xs = s->w;
419     ys = s->h;
420     bpp = s->format->BitsPerPixel;
421     SDL_FreeSurface(s);
422     return tnum;
423 }
424 
425 // management of texture slots
426 // each texture slot can have multiple texture frames, of which currently only the first is used
427 // additional frames can be used for various shaders
428 
textureload(const char * name,int clamp,bool mipmap,bool canreduce,float scale,bool trydl)429 Texture *textureload(const char *name, int clamp, bool mipmap, bool canreduce, float scale, bool trydl)
430 {
431     string pname;
432     copystring(pname, name);
433     path(pname);
434     Texture *t = textures.access(pname);
435     if(t) return t;
436     int xs, ys, bpp;
437     GLuint id = loadsurface(pname, xs, ys, bpp, clamp, mipmap, canreduce, scale, trydl);
438     if(!id) return notexture;
439     char *key = newstring(pname);
440     t = &textures[key];
441     t->name = key;
442     t->xs = xs;
443     t->ys = ys;
444     t->bpp = bpp;
445     t->clamp = clamp;
446     t->mipmap = mipmap;
447     t->canreduce = canreduce;
448     t->id = id;
449     t->scale = scale;
450     return t;
451 }
452 
createtexturefromsurface(const char * name,SDL_Surface * s)453 Texture *createtexturefromsurface(const char *name, SDL_Surface *s)
454 {
455     string pname;
456     copystring(pname, name);
457     path(pname);
458     Texture *t = textures.access(pname);
459     if(!t)
460     {
461         char *key = newstring(pname);
462         t = &textures[key];
463         t->name = key;
464     }
465 
466     GLuint tnum;
467     glGenTextures(1, &tnum);
468     GLenum format = texformat(s->format->BitsPerPixel);
469     createtexture(tnum, s->w, s->h, s->pixels, 0, true, false, format);
470 
471     t->xs = s->w;
472     t->ys = s->h;
473     t->bpp = s->format->BitsPerPixel;
474     t->clamp = 0;
475     t->mipmap = true;
476     t->canreduce = false;
477     t->id = tnum;
478     return t;
479 }
480 
481 struct Slot
482 {
483     string name;
484     float scale;
485     Texture *tex;
486     bool loaded;
487 };
488 
489 vector<Slot> slots;
490 
texturereset()491 void texturereset() { if(execcontext==IEXC_MAPCFG) slots.setsize(0); }
492 
texture(float * scale,char * name)493 void texture(float *scale, char *name)
494 {
495     Slot &s = slots.add();
496     copystring(s.name, name);
497     path(s.name);
498     s.tex = NULL;
499     s.loaded = false;
500     s.scale = (*scale > 0 && *scale <= 2.0f) ? *scale : 1.0f;
501 }
502 
503 COMMAND(texturereset, "");
504 COMMAND(texture, "fs");
505 
lookuptexture(int tex,Texture * failtex,bool trydl)506 Texture *lookuptexture(int tex, Texture *failtex, bool trydl)
507 {
508     Texture *t = failtex;
509     if(slots.inrange(tex))
510     {
511         Slot &s = slots[tex];
512         if(!s.loaded)
513         {
514             defformatstring(pname)("packages/textures/%s", s.name);
515             s.tex = textureload(pname, 0, true, true, s.scale, trydl);
516             if(!trydl)
517             {
518             if(s.tex==notexture) s.tex = failtex;
519             s.loaded = true;
520         }
521         }
522         if(s.tex) t = s.tex;
523     }
524     return t;
525 }
526 
cleanuptextures()527 void cleanuptextures()
528 {
529     enumerate(textures, Texture, t,
530         if(t.id) { glDeleteTextures(1, &t.id); t.id = 0; }
531     );
532 }
533 
reloadtexture(Texture & t)534 bool reloadtexture(Texture &t)
535 {
536     if(t.id) return true;
537     int xs = 1, ys = 1, bpp = 0;
538     t.id = loadsurface(t.name, xs, ys, bpp, t.clamp, t.mipmap, t.canreduce, t.scale);
539     t.xs = xs;
540     t.ys = ys;
541     t.bpp = bpp;
542     return t.id!=0;
543 }
544 
reloadtexture(const char * name)545 bool reloadtexture(const char *name)
546 {
547     Texture *t = textures.access(path(name, true));
548     if(t) return reloadtexture(*t);
549     return false;
550 }
551 
reloadtextures()552 void reloadtextures()
553 {
554     enumerate(textures, Texture, t, reloadtexture(t));
555 }
556 
557 Texture *sky[6] = {NULL, NULL, NULL, NULL, NULL, NULL};
558 static string skybox;
559 
loadsky(char * basename,bool reload)560 void loadsky(char *basename, bool reload)
561 {
562     const char *side[] = { "lf", "rt", "ft", "bk", "dn", "up" };
563     if(reload) basename = skybox;
564     else copystring(skybox, basename);
565     loopi(6)
566     {
567         defformatstring(name)("packages/%s_%s.jpg", basename, side[i]);
568         sky[i] = textureload(name, 3);
569         if(sky[i] == notexture && !reload && autodownload)
570         {
571             defformatstring(dl)("packages/%s", basename);
572             requirepackage(PCK_SKYBOX, dl);
573             break;
574         }
575     }
576 }
577 
578 COMMANDF(loadsky, "s", (char *name) { loadsky(name, false); intret(0); });
loadnotexture(char * c)579 void loadnotexture(char *c)
580 {
581     noworldtexture = notexture; // reset to default
582     if(c[0])
583     {
584         defformatstring(p)("packages/textures/%s", c);
585         noworldtexture = textureload(p);
586         if(noworldtexture==notexture) conoutf("could not load alternative texture '%s'.", p);
587     }
588 }
589 COMMAND(loadnotexture, "s");
590 
draw_envbox_face(float s0,float t0,float x0,float y0,float z0,float s1,float t1,float x1,float y1,float z1,float s2,float t2,float x2,float y2,float z2,float s3,float t3,float x3,float y3,float z3,Texture * tex)591 void draw_envbox_face(float s0, float t0, float x0, float y0, float z0,
592                       float s1, float t1, float x1, float y1, float z1,
593                       float s2, float t2, float x2, float y2, float z2,
594                       float s3, float t3, float x3, float y3, float z3,
595                       Texture *tex)
596 {
597     glBindTexture(GL_TEXTURE_2D, tex->id);
598     glBegin(GL_TRIANGLE_STRIP);
599     glTexCoord2f(s3, t3); glVertex3f(x3, y3, z3);
600     glTexCoord2f(s2, t2); glVertex3f(x2, y2, z2);
601     glTexCoord2f(s0, t0); glVertex3f(x0, y0, z0);
602     glTexCoord2f(s1, t1); glVertex3f(x1, y1, z1);
603     glEnd();
604     xtraverts += 4;
605 }
606 
607 VAR(skyclip, 0, 1, 1);
608 
609 float skyfloor = 1e16f;
610 
draw_envbox(int w)611 void draw_envbox(int w)
612 {
613     extern float skyfloor;
614 
615     float zclip = skyclip && skyfloor >= camera1->o.z ? 0.5f + float(skyfloor-camera1->o.z)/w : 0.0f,
616           vclip = 1-zclip,
617           z = 2*w*(vclip-0.5f);
618 
619     if(vclip < 0) return;
620 
621     glDepthMask(GL_FALSE);
622 
623     draw_envbox_face(0.0f, 0.0f, -w, -w, -w,
624                      1.0f, 0.0f, -w,  w, -w,
625                      1.0f, vclip, -w,  w,  z,
626                      0.0f, vclip, -w, -w,  z, sky[0] ? sky[0] : notexture);
627 
628     draw_envbox_face(1.0f, vclip, +w, -w,  z,
629                      0.0f, vclip, +w,  w,  z,
630                      0.0f, 0.0f, +w,  w, -w,
631                      1.0f, 0.0f, +w, -w, -w, sky[1] ? sky[1] : notexture);
632 
633     draw_envbox_face(1.0f, vclip, -w, -w,  z,
634                      0.0f, vclip,  w, -w,  z,
635                      0.0f, 0.0f,  w, -w, -w,
636                      1.0f, 0.0f, -w, -w, -w, sky[2] ? sky[2] : notexture);
637 
638     draw_envbox_face(1.0f, vclip, +w,  w,  z,
639                      0.0f, vclip, -w,  w,  z,
640                      0.0f, 0.0f, -w,  w, -w,
641                      1.0f, 0.0f, +w,  w, -w, sky[3] ? sky[3] : notexture);
642 
643     if(!zclip)
644         draw_envbox_face(0.0f, 1.0f, -w,  w,  w,
645                          0.0f, 0.0f, +w,  w,  w,
646                          1.0f, 0.0f, +w, -w,  w,
647                          1.0f, 1.0f, -w, -w,  w, sky[4] ? sky[4] : notexture);
648 
649     draw_envbox_face(0.0f, 1.0f, +w,  w, -w,
650                      0.0f, 0.0f, -w,  w, -w,
651                      1.0f, 0.0f, -w, -w, -w,
652                      1.0f, 1.0f, +w, -w, -w, sky[5] ? sky[5] : notexture);
653 
654     glDepthMask(GL_TRUE);
655 
656     skyfloor = 1e16f;
657 }
658 
659 struct tmufunc
660 {
661     GLenum combine, sources[3], ops[3];
662     int scale;
663 };
664 
665 struct tmu
666 {
667     GLenum mode;
668     GLfloat color[4];
669     tmufunc rgb, alpha;
670 };
671 
672 #define INVALIDTMU \
673 { \
674     0, \
675     { -1, -1, -1, -1 }, \
676     { 0, { 0, 0, 0 }, { 0, 0, 0 }, 0 }, \
677     { 0, { 0, 0, 0 }, { 0, 0, 0 }, 0 } \
678 }
679 
680 #define INITTMU \
681 { \
682     GL_MODULATE, \
683     { 0, 0, 0, 0 }, \
684     { GL_MODULATE, { GL_TEXTURE, GL_PREVIOUS_ARB, GL_CONSTANT_ARB }, { GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_ALPHA }, 1 }, \
685     { GL_MODULATE, { GL_TEXTURE, GL_PREVIOUS_ARB, GL_CONSTANT_ARB }, { GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA }, 1 } \
686 }
687 
688 #define MAXTMUS 4
689 
690 tmu tmus[MAXTMUS] =
691 {
692     INVALIDTMU,
693     INVALIDTMU,
694     INVALIDTMU,
695     INVALIDTMU
696 };
697 
698 VAR(maxtmus, 1, 0, 0);
699 
parsetmufunc(tmufunc & f,const char * s)700 void parsetmufunc(tmufunc &f, const char *s)
701 {
702     int arg = -1;
703     while(*s) switch(tolower(*s++))
704     {
705         case 't': f.sources[++arg] = GL_TEXTURE; f.ops[arg] = GL_SRC_COLOR; break;
706         case 'p': f.sources[++arg] = GL_PREVIOUS_ARB; f.ops[arg] = GL_SRC_COLOR; break;
707         case 'k': f.sources[++arg] = GL_CONSTANT_ARB; f.ops[arg] = GL_SRC_COLOR; break;
708         case 'c': f.sources[++arg] = GL_PRIMARY_COLOR_ARB; f.ops[arg] = GL_SRC_COLOR; break;
709         case '~': f.ops[arg] = GL_ONE_MINUS_SRC_COLOR; break;
710         case 'a': f.ops[arg] = f.ops[arg]==GL_ONE_MINUS_SRC_COLOR ? GL_ONE_MINUS_SRC_ALPHA : GL_SRC_ALPHA; break;
711         case '=': f.combine = GL_REPLACE; break;
712         case '*': f.combine = GL_MODULATE; break;
713         case '+': f.combine = GL_ADD; break;
714         case '-': f.combine = GL_SUBTRACT_ARB; break;
715         case ',':
716         case '@': f.combine = GL_INTERPOLATE_ARB; break;
717         case '.': f.combine = GL_DOT3_RGB_ARB; break;
718         case 'x': while(!isdigit(*s)) s++; f.scale = *s++-'0'; break;
719     }
720 }
721 
committmufunc(bool rgb,tmufunc & dst,tmufunc & src)722 void committmufunc(bool rgb, tmufunc &dst, tmufunc &src)
723 {
724     if(dst.combine!=src.combine) glTexEnvi(GL_TEXTURE_ENV, rgb ? GL_COMBINE_RGB_ARB : GL_COMBINE_ALPHA_ARB, src.combine);
725     loopi(3)
726     {
727         if(dst.sources[i]!=src.sources[i]) glTexEnvi(GL_TEXTURE_ENV, (rgb ? GL_SOURCE0_RGB_ARB : GL_SOURCE0_ALPHA_ARB)+i, src.sources[i]);
728         if(dst.ops[i]!=src.ops[i]) glTexEnvi(GL_TEXTURE_ENV, (rgb ? GL_OPERAND0_RGB_ARB : GL_OPERAND0_ALPHA_ARB)+i, src.ops[i]);
729     }
730     if(dst.scale!=src.scale) glTexEnvi(GL_TEXTURE_ENV, rgb ? GL_RGB_SCALE_ARB : GL_ALPHA_SCALE, src.scale);
731 }
732 
committmu(int n,tmu & f)733 void committmu(int n, tmu &f)
734 {
735     if(n>=maxtmus) return;
736     if(tmus[n].mode!=f.mode) glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, f.mode);
737     if(memcmp(tmus[n].color, f.color, sizeof(f.color))) glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, f.color);
738     committmufunc(true, tmus[n].rgb, f.rgb);
739     committmufunc(false, tmus[n].alpha, f.alpha);
740     tmus[n] = f;
741 }
742 
resettmu(int n)743 void resettmu(int n)
744 {
745     tmu f = tmus[n];
746     f.mode = GL_MODULATE;
747     f.rgb.scale = 1;
748     f.alpha.scale = 1;
749     committmu(n, f);
750 }
751 
scaletmu(int n,int rgbscale,int alphascale)752 void scaletmu(int n, int rgbscale, int alphascale)
753 {
754     tmu f = tmus[n];
755     if(rgbscale) f.rgb.scale = rgbscale;
756     if(alphascale) f.alpha.scale = alphascale;
757     committmu(n, f);
758 }
759 
colortmu(int n,float r,float g,float b,float a)760 void colortmu(int n, float r, float g, float b, float a)
761 {
762     tmu f = tmus[n];
763     f.color[0] = r;
764     f.color[1] = g;
765     f.color[2] = b;
766     f.color[3] = a;
767     committmu(n, f);
768 }
769 
setuptmu(int n,const char * rgbfunc,const char * alphafunc)770 void setuptmu(int n, const char *rgbfunc, const char *alphafunc)
771 {
772     static tmu init = INITTMU;
773     tmu f = tmus[n];
774 
775     f.mode = GL_COMBINE_ARB;
776     if(rgbfunc) parsetmufunc(f.rgb, rgbfunc);
777     else f.rgb = init.rgb;
778     if(alphafunc) parsetmufunc(f.alpha, alphafunc);
779     else f.alpha = init.alpha;
780 
781     committmu(n, f);
782 }
783 
inittmus()784 void inittmus()
785 {
786     GLint val;
787     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &val);
788     hwtexsize = val;
789 
790     if(hasTE && !hasMT) maxtmus = 1;
791     else if(hasTE && hasMT)
792     {
793         glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &val);
794         maxtmus = max(1, min(MAXTMUS, int(val)));
795         loopi(maxtmus)
796         {
797             glActiveTexture_(GL_TEXTURE0_ARB+i);
798             resettmu(i);
799         }
800         glActiveTexture_(GL_TEXTURE0_ARB);
801     }
802 }
803 
cleanuptmus()804 void cleanuptmus()
805 {
806     tmu invalidtmu = INVALIDTMU;
807     loopi(MAXTMUS) tmus[i] = invalidtmu;
808 }
809 
810 
811 // only works on 32 bit surfaces with alpha in 4th byte!
blitsurface(SDL_Surface * dst,SDL_Surface * src,int x,int y)812 void blitsurface(SDL_Surface *dst, SDL_Surface *src, int x, int y)
813 {
814     uchar *dstp = (uchar *)dst->pixels + y*dst->pitch + x*4,
815           *srcp = (uchar *)src->pixels;
816     int dstpitch = dst->pitch - 4*src->w,
817         srcpitch = src->pitch - 4*src->w;
818     loop(dy, src->h)
819     {
820         loop(dx, src->w)
821         {
822             uint k1 = (255U - srcp[3]) * dstp[3], k2 = srcp[3] * 255U, kmax = max(dstp[3], srcp[3]), kscale = max(kmax * 255U, 1U);
823             dstp[0] = (dstp[0]*k1 + srcp[0]*k2) / kscale;
824             dstp[1] = (dstp[1]*k1 + srcp[1]*k2) / kscale;
825             dstp[2] = (dstp[2]*k1 + srcp[2]*k2) / kscale;
826             dstp[3] = kmax;
827             dstp += 4;
828             srcp += 4;
829         }
830         dstp += dstpitch;
831         srcp += srcpitch;
832     }
833 }
834 
835 Texture *e_wall = NULL, *e_floor = NULL, *e_ceil = NULL;
836 
guidetoggle()837 void guidetoggle()
838 {
839     if(player1->state == CS_EDITING)
840     {
841         Slot *sw = &slots[DEFAULT_WALL];
842         Slot *sf = &slots[DEFAULT_FLOOR];
843         Slot *sc = &slots[DEFAULT_CEIL];
844 
845         //if textures match original texture
846         if(e_wall == NULL || e_floor == NULL || e_ceil == NULL)
847         {
848             //replace defaults with grid texures
849             e_wall = sw->tex;
850             e_floor = sf->tex;
851             e_ceil = sc->tex;
852             sw->tex = textureload("packages/textures/map_editor/wall.png");
853             sf->tex = textureload("packages/textures/map_editor/floor.png");
854             sc->tex = textureload("packages/textures/map_editor/ceil.png");
855             conoutf("Guide: \f0on");
856         }
857         else
858         {
859             // restore textures
860             if(e_wall) sw->tex = e_wall;
861             if(e_floor) sf->tex = e_floor;
862             if(e_ceil) sc->tex = e_ceil;
863             e_wall = NULL;
864             e_floor = NULL;
865             e_ceil = NULL;
866             conoutf("Guide: \fBoff");
867         }
868     }
869     else
870     {
871         conoutf("\fBGuide view is only avaiable when editing.");
872     }
873 }
874 
875 COMMAND(guidetoggle, "");