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, "");