1 #include "engine.h"
2 
3 struct QuadNode
4 {
5     int x, y, size;
6     uint filled;
7     QuadNode *child[4];
8 
QuadNodeQuadNode9     QuadNode(int x, int y, int size) : x(x), y(y), size(size), filled(0) { loopi(4) child[i] = 0; }
10 
clearQuadNode11     void clear()
12     {
13         loopi(4) DELETEP(child[i]);
14     }
15 
~QuadNodeQuadNode16     ~QuadNode()
17     {
18         clear();
19     }
20 
insertQuadNode21     void insert(int mx, int my, int msize)
22     {
23         if(size == msize)
24         {
25             filled = 0xF;
26             return;
27         }
28         int csize = size>>1, i = 0;
29         if(mx >= x+csize) i |= 1;
30         if(my >= y+csize) i |= 2;
31         if(csize == msize)
32         {
33             filled |= (1 << i);
34             return;
35         }
36         if(!child[i]) child[i] = new QuadNode(i&1 ? x+csize : x, i&2 ? y+csize : y, csize);
37         child[i]->insert(mx, my, msize);
38         loopj(4) if(child[j])
39         {
40             if(child[j]->filled == 0xF)
41             {
42                 DELETEP(child[j]);
43                 filled |= (1 << j);
44             }
45         }
46     }
47 
genmatsurfQuadNode48     void genmatsurf(ushort mat, uchar orient, uchar visible, int x, int y, int z, int size, materialsurface *&matbuf)
49     {
50         materialsurface &m = *matbuf++;
51         m.material = mat;
52         m.orient = orient;
53         m.visible = visible;
54         m.csize = size;
55         m.rsize = size;
56         int dim = dimension(orient);
57         m.o[C[dim]] = x;
58         m.o[R[dim]] = y;
59         m.o[dim] = z;
60     }
61 
genmatsurfsQuadNode62     void genmatsurfs(ushort mat, uchar orient, uchar flags, int z, materialsurface *&matbuf)
63     {
64         if(filled == 0xF) genmatsurf(mat, orient, flags, x, y, z, size, matbuf);
65         else if(filled)
66         {
67             int csize = size>>1;
68             loopi(4) if(filled & (1 << i))
69                 genmatsurf(mat, orient, flags, i&1 ? x+csize : x, i&2 ? y+csize : y, z, csize, matbuf);
70         }
71         loopi(4) if(child[i]) child[i]->genmatsurfs(mat, orient, flags, z, matbuf);
72     }
73 };
74 
75 static float wfwave, wfscroll, wfxscale, wfyscale;
76 
renderwaterfall(const materialsurface & m,float offset,const vec * normal=NULL)77 static void renderwaterfall(const materialsurface &m, float offset, const vec *normal = NULL)
78 {
79     if(gle::attribbuf.empty())
80     {
81         gle::defvertex();
82         if(normal) gle::defnormal();
83         gle::deftexcoord0();
84         gle::begin(GL_QUADS);
85     }
86     float x = m.o.x, y = m.o.y, zmin = m.o.z, zmax = zmin;
87     if(m.ends&1) zmin += -WATER_OFFSET-WATER_AMPLITUDE;
88     if(m.ends&2) zmax += wfwave;
89     int csize = m.csize, rsize = m.rsize;
90 #define GENFACEORIENT(orient, v0, v1, v2, v3) \
91         case orient: v0 v1 v2 v3 break;
92 #undef GENFACEVERTX
93 #define GENFACEVERTX(orient, vert, mx,my,mz, sx,sy,sz) \
94             { \
95                 vec v(mx sx, my sy, mz sz); \
96                 gle::attrib(v); \
97                 GENFACENORMAL \
98                 gle::attribf(wfxscale*v.y, wfyscale*(v.z+wfscroll)); \
99             }
100 #undef GENFACEVERTY
101 #define GENFACEVERTY(orient, vert, mx,my,mz, sx,sy,sz) \
102             { \
103                 vec v(mx sx, my sy, mz sz); \
104                 gle::attrib(v); \
105                 GENFACENORMAL \
106                 gle::attribf(wfxscale*v.x, wfyscale*(v.z+wfscroll)); \
107             }
108 #define GENFACENORMAL gle::attrib(n);
109     if(normal)
110     {
111         vec n = *normal;
112         switch(m.orient) { GENFACEVERTSXY(x, x, y, y, zmin, zmax, /**/, + csize, /**/, + rsize, + offset, - offset) }
113     }
114 #undef GENFACENORMAL
115 #define GENFACENORMAL
116     else switch(m.orient) { GENFACEVERTSXY(x, x, y, y, zmin, zmax, /**/, + csize, /**/, + rsize, + offset, - offset) }
117 #undef GENFACENORMAL
118 #undef GENFACEORIENT
119 #undef GENFACEVERTX
120 #define GENFACEVERTX(o,n, x,y,z, xv,yv,zv) GENFACEVERT(o,n, x,y,z, xv,yv,zv)
121 #undef GENFACEVERTY
122 #define GENFACEVERTY(o,n, x,y,z, xv,yv,zv) GENFACEVERT(o,n, x,y,z, xv,yv,zv)
123 }
124 
drawmaterial(const materialsurface & m,float offset)125 static void drawmaterial(const materialsurface &m, float offset)
126 {
127     if(gle::attribbuf.empty())
128     {
129         gle::defvertex();
130         gle::begin(GL_QUADS);
131     }
132     float x = m.o.x, y = m.o.y, z = m.o.z, csize = m.csize, rsize = m.rsize;
133     switch(m.orient)
134     {
135 #define GENFACEORIENT(orient, v0, v1, v2, v3) \
136         case orient: v0 v1 v2 v3 break;
137 #define GENFACEVERT(orient, vert, mx,my,mz, sx,sy,sz) \
138             gle::attribf(mx sx, my sy, mz sz);
139         GENFACEVERTS(x, x, y, y, z, z, /**/, + csize, /**/, + rsize, + offset, - offset)
140 #undef GENFACEORIENT
141 #undef GENFACEVERT
142     }
143 }
144 
145 extern const namemap materials[] =
146 {
147     {"air", MAT_AIR},
148     {"water", MAT_WATER}, {"water1", MAT_WATER}, {"water2", MAT_WATER+1}, {"water3", MAT_WATER+2}, {"water4", MAT_WATER+3},
149     {"glass", MAT_GLASS}, {"glass1", MAT_GLASS}, {"glass2", MAT_GLASS+1}, {"glass3", MAT_GLASS+2}, {"glass4", MAT_GLASS+3},
150     {"lava", MAT_LAVA}, {"lava1", MAT_LAVA}, {"lava2", MAT_LAVA+1}, {"lava3", MAT_LAVA+2}, {"lava4", MAT_LAVA+3},
151     {"clip", MAT_CLIP},
152     {"noclip", MAT_NOCLIP},
153     {"aiclip", MAT_AICLIP},
154     {"death", MAT_DEATH},
155     {"ladder", MAT_LADDER},
156     {"alpha", MAT_ALPHA},
157     {"hurt", MAT_HURT},
158 };
159 
findmaterial(const char * name,bool tryint)160 int findmaterial(const char *name, bool tryint)
161 {
162     loopi(sizeof(materials)/sizeof(materials[0])) if(!strcmp(materials[i].name, name)) { return materials[i].id; }
163     return tryint && isnumeric(*name) ? atoi(name) : -1;
164 }
165 
findmaterialname(int type)166 const char *findmaterialname(int type)
167 {
168     loopi(sizeof(materials)/sizeof(materials[0])) if(materials[i].id == type) { return materials[i].name; }
169     return NULL;
170 }
171 
getmaterialdesc(int mat,const char * prefix)172 const char *getmaterialdesc(int mat, const char *prefix)
173 {
174     static const ushort matmasks[] = { MATF_VOLUME|MATF_INDEX, MATF_CLIP, MAT_DEATH, MAT_LADDER, MAT_ALPHA, MAT_HURT };
175     static string desc;
176     desc[0] = '\0';
177     loopi(sizeof(matmasks)/sizeof(matmasks[0])) if(mat&matmasks[i])
178     {
179         const char *matname = findmaterialname(mat&matmasks[i]);
180         if(matname)
181         {
182             concatstring(desc, desc[0] ? ", " : prefix);
183             concatstring(desc, matname);
184         }
185     }
186     return desc;
187 }
188 
visiblematerial(const cube & c,int orient,const ivec & co,int size,ushort matmask)189 int visiblematerial(const cube &c, int orient, const ivec &co, int size, ushort matmask)
190 {
191     ushort mat = c.material&matmask;
192     switch(mat)
193     {
194     case MAT_AIR:
195          break;
196 
197     case MAT_LAVA:
198     case MAT_WATER:
199         if(visibleface(c, orient, co, size, mat, MAT_AIR, matmask))
200             return (orient != O_BOTTOM ? MATSURF_VISIBLE : MATSURF_EDIT_ONLY);
201         break;
202 
203     case MAT_GLASS:
204         if(visibleface(c, orient, co, size, MAT_GLASS, MAT_AIR, matmask))
205             return MATSURF_VISIBLE;
206         break;
207 
208     default:
209         if(visibleface(c, orient, co, size, mat, MAT_AIR, matmask))
210             return MATSURF_EDIT_ONLY;
211         break;
212     }
213     return MATSURF_NOT_VISIBLE;
214 }
215 
genmatsurfs(const cube & c,const ivec & co,int size,vector<materialsurface> & matsurfs)216 void genmatsurfs(const cube &c, const ivec &co, int size, vector<materialsurface> &matsurfs)
217 {
218     loopi(6)
219     {
220         static const ushort matmasks[] = { MATF_VOLUME|MATF_INDEX, MATF_CLIP, MAT_DEATH, MAT_LADDER, MAT_ALPHA, MAT_HURT };
221         loopj(sizeof(matmasks)/sizeof(matmasks[0]))
222         {
223             int matmask = matmasks[j];
224             int vis = visiblematerial(c, i, co, size, matmask&~MATF_INDEX);
225             if(vis != MATSURF_NOT_VISIBLE)
226             {
227                 materialsurface m;
228                 m.material = c.material&matmask;
229                 m.orient = i;
230                 m.visible = vis;
231                 m.o = co;
232                 m.csize = m.rsize = size;
233                 if(dimcoord(i)) m.o[dimension(i)] += size;
234                 matsurfs.add(m);
235                 break;
236             }
237         }
238     }
239 }
240 
mergematcmp(const materialsurface & x,const materialsurface & y)241 static inline bool mergematcmp(const materialsurface &x, const materialsurface &y)
242 {
243     int dim = dimension(x.orient), c = C[dim], r = R[dim];
244     if(x.o[r] + x.rsize < y.o[r] + y.rsize) return true;
245     if(x.o[r] + x.rsize > y.o[r] + y.rsize) return false;
246     return x.o[c] < y.o[c];
247 }
248 
mergematr(materialsurface * m,int sz,materialsurface & n)249 static int mergematr(materialsurface *m, int sz, materialsurface &n)
250 {
251     int dim = dimension(n.orient), c = C[dim], r = R[dim];
252     for(int i = sz-1; i >= 0; --i)
253     {
254         if(m[i].o[r] + m[i].rsize < n.o[r]) break;
255         if(m[i].o[r] + m[i].rsize == n.o[r] && m[i].o[c] == n.o[c] && m[i].csize == n.csize)
256         {
257             n.o[r] = m[i].o[r];
258             n.rsize += m[i].rsize;
259             memmove(&m[i], &m[i+1], (sz - (i+1)) * sizeof(materialsurface));
260             return 1;
261         }
262     }
263     return 0;
264 }
265 
mergematc(materialsurface & m,materialsurface & n)266 static int mergematc(materialsurface &m, materialsurface &n)
267 {
268     int dim = dimension(n.orient), c = C[dim], r = R[dim];
269     if(m.o[r] == n.o[r] && m.rsize == n.rsize && m.o[c] + m.csize == n.o[c])
270     {
271         n.o[c] = m.o[c];
272         n.csize += m.csize;
273         return 1;
274     }
275     return 0;
276 }
277 
mergemat(materialsurface * m,int sz,materialsurface & n)278 static int mergemat(materialsurface *m, int sz, materialsurface &n)
279 {
280     for(bool merged = false; sz; merged = true)
281     {
282         int rmerged = mergematr(m, sz, n);
283         sz -= rmerged;
284         if(!rmerged && merged) break;
285         if(!sz) break;
286         int cmerged = mergematc(m[sz-1], n);
287         sz -= cmerged;
288         if(!cmerged) break;
289     }
290     m[sz++] = n;
291     return sz;
292 }
293 
mergemats(materialsurface * m,int sz)294 static int mergemats(materialsurface *m, int sz)
295 {
296     quicksort(m, sz, mergematcmp);
297 
298     int nsz = 0;
299     loopi(sz) nsz = mergemat(m, nsz, m[i]);
300     return nsz;
301 }
302 
optmatcmp(const materialsurface & x,const materialsurface & y)303 static inline bool optmatcmp(const materialsurface &x, const materialsurface &y)
304 {
305     if(x.material < y.material) return true;
306     if(x.material > y.material) return false;
307     if(x.orient > y.orient) return true;
308     if(x.orient < y.orient) return false;
309     int dim = dimension(x.orient);
310     return x.o[dim] < y.o[dim];
311 }
312 
313 VARF(0, optmats, 0, 1, 1, allchanged());
314 
optimizematsurfs(materialsurface * matbuf,int matsurfs)315 int optimizematsurfs(materialsurface *matbuf, int matsurfs)
316 {
317     quicksort(matbuf, matsurfs, optmatcmp);
318     if(!optmats) return matsurfs;
319     materialsurface *cur = matbuf, *end = matbuf+matsurfs;
320     while(cur < end)
321     {
322          materialsurface *start = cur++;
323          int dim = dimension(start->orient);
324          while(cur < end &&
325                cur->material == start->material &&
326                cur->orient == start->orient &&
327                cur->visible == start->visible &&
328                cur->o[dim] == start->o[dim])
329             ++cur;
330          if(!isliquid(start->material&MATF_VOLUME) || start->orient != O_TOP || !vertwater)
331          {
332             if(start!=matbuf) memmove(matbuf, start, (cur-start)*sizeof(materialsurface));
333             matbuf += mergemats(matbuf, cur-start);
334          }
335          else if(cur-start>=4)
336          {
337             QuadNode vmats(0, 0, hdr.worldsize);
338             loopi(cur-start) vmats.insert(start[i].o[C[dim]], start[i].o[R[dim]], start[i].csize);
339             vmats.genmatsurfs(start->material, start->orient, start->visible, start->o[dim], matbuf);
340          }
341          else
342          {
343             if(start!=matbuf) memmove(matbuf, start, (cur-start)*sizeof(materialsurface));
344             matbuf += cur-start;
345          }
346     }
347     return matsurfs - (end-matbuf);
348 }
349 
350 struct waterinfo
351 {
352     materialsurface *m;
353     double depth, area;
354 };
355 
setupmaterials(int start,int len)356 void setupmaterials(int start, int len)
357 {
358     int hasmat = 0;
359     vector<waterinfo> water;
360     unionfind uf;
361     if(!len) len = valist.length();
362     for(int i = start; i < len; i++)
363     {
364         vtxarray *va = valist[i];
365         materialsurface *skip = NULL;
366         loopj(va->matsurfs)
367         {
368             materialsurface &m = va->matbuf[j];
369             int matvol = m.material&MATF_VOLUME;
370             if(matvol==MAT_WATER && m.orient==O_TOP)
371             {
372                 m.index = water.length();
373                 loopvk(water)
374                 {
375                     materialsurface &n = *water[k].m;
376                     if(m.material!=n.material || m.o.z!=n.o.z) continue;
377                     if(n.o.x+n.rsize==m.o.x || m.o.x+m.rsize==n.o.x)
378                     {
379                         if(n.o.y+n.csize>m.o.y && n.o.y<m.o.y+m.csize) uf.unite(m.index, n.index);
380                     }
381                     else if(n.o.y+n.csize==m.o.y || m.o.y+m.csize==n.o.y)
382                     {
383                         if(n.o.x+n.rsize>m.o.x && n.o.x<m.o.x+m.rsize) uf.unite(m.index, n.index);
384                     }
385                 }
386                 waterinfo &wi = water.add();
387                 wi.m = &m;
388                 vec center(m.o.x+m.rsize/2, m.o.y+m.csize/2, m.o.z-WATER_OFFSET);
389                 m.light = brightestlight(center, vec(0, 0, 1));
390                 float depth = raycube(center, vec(0, 0, -1), 10000);
391                 wi.depth = double(depth)*m.rsize*m.csize;
392                 wi.area = m.rsize*m.csize;
393             }
394             else if(isliquid(matvol) && m.orient!=O_BOTTOM && m.orient!=O_TOP)
395             {
396                 m.ends = 0;
397                 int dim = dimension(m.orient), coord = dimcoord(m.orient);
398                 ivec o(m.o);
399                 o.z -= 1;
400                 o[dim] += coord ? 1 : -1;
401                 int minc = o[dim^1], maxc = minc + (C[dim]==2 ? m.rsize : m.csize);
402                 ivec co;
403                 int csize;
404                 while(o[dim^1] < maxc)
405                 {
406                     cube &c = lookupcube(o, 0, co, csize);
407                     if(isliquid(c.material&MATF_VOLUME)) { m.ends |= 1; break; }
408                     o[dim^1] += csize;
409                 }
410                 o[dim^1] = minc;
411                 o.z += R[dim]==2 ? m.rsize : m.csize;
412                 o[dim] -= coord ? 2 : -2;
413                 while(o[dim^1] < maxc)
414                 {
415                     cube &c = lookupcube(o, 0, co, csize);
416                     if(visiblematerial(c, O_TOP, co, csize)) { m.ends |= 2; break; }
417                     o[dim^1] += csize;
418                 }
419             }
420             else if(matvol==MAT_GLASS)
421             {
422                 int dim = dimension(m.orient);
423                 vec center(m.o);
424                 center[R[dim]] += m.rsize/2;
425                 center[C[dim]] += m.csize/2;
426                 m.envmap = closestenvmap(center);
427             }
428             if(matvol) hasmat |= 1<<m.material;
429             m.skip = 0;
430             if(skip && m.material == skip->material && m.orient == skip->orient && skip->skip < 0xFFFF)
431                 skip->skip++;
432             else
433                 skip = &m;
434         }
435     }
436     loopv(water)
437     {
438         int root = uf.find(i);
439         if(i==root) continue;
440         materialsurface &m = *water[i].m, &n = *water[root].m;
441         if(m.light && (m.light->type == ET_SUNLIGHT || !m.light->attrs[0] || !n.light || (n.light->type != ET_SUNLIGHT && n.light->attrs[0] && m.light->attrs[0] > n.light->attrs[0]))) n.light = m.light;
442         water[root].depth += water[i].depth;
443         water[root].area += water[i].area;
444     }
445     loopv(water)
446     {
447         int root = uf.find(i);
448         water[i].m->light = water[root].m->light;
449         water[i].m->depth = (short)(water[root].depth/water[root].area);
450     }
451     if(hasmat&(0xF<<MAT_WATER))
452     {
453         loadcaustics(true);
454         preloadwatershaders(true);
455         loopi(4) if(hasmat&(1<<(MAT_WATER+i))) lookupmaterialslot(MAT_WATER+i);
456     }
457     if(hasmat&(0xF<<MAT_LAVA))
458     {
459         useshaderbyname("lava");
460         useshaderbyname("lavaglare");
461         loopi(4) if(hasmat&(1<<(MAT_LAVA+i))) lookupmaterialslot(MAT_LAVA+i);
462     }
463     if(hasmat&(0xF<<MAT_GLASS)) useshaderbyname("glass");
464 }
465 
466 VAR(IDF_PERSIST, showmat, 0, 0, 1);
467 
468 static int sortdim[3];
469 static ivec sortorigin;
470 static bool sortedit;
471 
vismatcmp(const materialsurface * xm,const materialsurface * ym)472 static inline bool vismatcmp(const materialsurface *xm, const materialsurface *ym)
473 {
474     const materialsurface &x = *xm, &y = *ym;
475     if(!sortedit)
476     {
477         if((x.material&MATF_VOLUME) == MAT_LAVA) { if((y.material&MATF_VOLUME) != MAT_LAVA) return true; }
478         else if((y.material&MATF_VOLUME) == MAT_LAVA) return false;
479     }
480     int xdim = dimension(x.orient), ydim = dimension(y.orient);
481     loopi(3)
482     {
483         int dim = sortdim[i], xmin, xmax, ymin, ymax;
484         xmin = xmax = x.o[dim];
485         if(dim==C[xdim]) xmax += x.csize;
486         else if(dim==R[xdim]) xmax += x.rsize;
487         ymin = ymax = y.o[dim];
488         if(dim==C[ydim]) ymax += y.csize;
489         else if(dim==R[ydim]) ymax += y.rsize;
490         if(xmax > ymin && ymax > xmin) continue;
491         int c = sortorigin[dim];
492         if(c > xmin && c < xmax) return sortedit;
493         if(c > ymin && c < ymax) return !sortedit;
494         xmin = abs(xmin - c);
495         xmax = abs(xmax - c);
496         ymin = abs(ymin - c);
497         ymax = abs(ymax - c);
498         if(max(xmin, xmax) <= min(ymin, ymax)) return sortedit;
499         else if(max(ymin, ymax) <= min(xmin, xmax)) return !sortedit;
500     }
501     if(x.material < y.material) return sortedit;
502     if(x.material > y.material) return !sortedit;
503     return false;
504 }
505 
sortmaterials(vector<materialsurface * > & vismats)506 void sortmaterials(vector<materialsurface *> &vismats)
507 {
508     sortorigin = ivec(camera1->o);
509     if(reflecting) sortorigin.z = int(reflectz - (camera1->o.z - reflectz));
510     vec dir(camera1->yaw*RAD, reflecting ? -camera1->pitch : camera1->pitch);
511     loopi(3) { dir[i] = fabs(dir[i]); sortdim[i] = i; }
512     if(dir[sortdim[2]] > dir[sortdim[1]]) swap(sortdim[2], sortdim[1]);
513     if(dir[sortdim[1]] > dir[sortdim[0]]) swap(sortdim[1], sortdim[0]);
514     if(dir[sortdim[2]] > dir[sortdim[1]]) swap(sortdim[2], sortdim[1]);
515 
516     for(vtxarray *va = reflecting ? reflectedva : visibleva; va; va = reflecting ? va->rnext : va->next)
517     {
518         if(!va->matsurfs || va->occluded >= OCCLUDE_BB) continue;
519         if(reflecting || refracting>0 ? va->o.z+va->size <= reflectz : va->o.z >= reflectz) continue;
520         loopi(va->matsurfs)
521         {
522             materialsurface &m = va->matbuf[i];
523             if(!editmode || !showmat || drawtex)
524             {
525                 int matvol = m.material&MATF_VOLUME;
526                 if(matvol==MAT_WATER && (m.orient==O_TOP || (refracting<0 && reflectz>hdr.worldsize))) { i += m.skip; continue; }
527                 if(m.visible == MATSURF_EDIT_ONLY) { i += m.skip; continue; }
528                 if(glaring && matvol!=MAT_LAVA) { i += m.skip; continue; }
529             }
530             else if(glaring) continue;
531             vismats.add(&m);
532         }
533     }
534     sortedit = editmode && showmat && !drawtex;
535     vismats.sort(vismatcmp);
536 }
537 
rendermatgrid(vector<materialsurface * > & vismats)538 void rendermatgrid(vector<materialsurface *> &vismats)
539 {
540     enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
541     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
542     int lastmat = -1;
543     loopvrev(vismats)
544     {
545         materialsurface &m = *vismats[i];
546         if(m.material != lastmat)
547         {
548             xtraverts += gle::end();
549             bvec color;
550             switch(m.material&~MATF_INDEX)
551             {
552                 case MAT_WATER:  color = bvec(  0,   0,  85); break; // blue
553                 case MAT_CLIP:   color = bvec( 85,   0,   0); break; // red
554                 case MAT_GLASS:  color = bvec(  0,  85,  85); break; // cyan
555                 case MAT_NOCLIP: color = bvec(  0,  85,   0); break; // green
556                 case MAT_LAVA:   color = bvec( 85,  40,   0); break; // orange
557                 case MAT_AICLIP: color = bvec( 85,  85,   0); break; // yellow
558                 case MAT_DEATH:  color = bvec( 40,  40,  40); break; // black
559                 case MAT_LADDER: color = bvec(128,  64, 224); break; // violet
560                 case MAT_ALPHA:  color = bvec( 85,   0,  85); break; // pink
561                 case MAT_HURT:   color = bvec(128, 128, 128); break; // grey
562                 default: continue;
563             }
564             gle::color(color);
565             lastmat = m.material;
566         }
567         drawmaterial(m, -0.1f);
568     }
569     xtraverts += gle::end();
570     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
571     disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
572 }
573 
574 #define GLASSVARS(name) \
575     bvec name##col(0x20, 0x80, 0xC0); \
576     VARF(IDF_HEX|IDF_WORLD, name##colour, 0, 0x2080C0, 0xFFFFFF, \
577     { \
578         if(!name##colour) name##colour = 0x2080C0; \
579         name##col = bvec((name##colour>>16)&0xFF, (name##colour>>8)&0xFF, name##colour&0xFF); \
580     });
581 
582 GLASSVARS(glass)
583 GLASSVARS(glass2)
584 GLASSVARS(glass3)
585 GLASSVARS(glass4)
586 
587 GETMATIDXVAR(glass, colour, int)
588 GETMATIDXVAR(glass, col, const bvec &)
589 
590 VAR(IDF_PERSIST, glassenv, 0, 1, 1);
591 
drawglass(const materialsurface & m,float offset,const vec * normal=NULL)592 static void drawglass(const materialsurface &m, float offset, const vec *normal = NULL)
593 {
594     if(gle::attribbuf.empty())
595     {
596         gle::defvertex();
597         if(normal) gle::defnormal();
598         gle::deftexcoord0(3);
599         gle::begin(GL_QUADS);
600     }
601     #define GENFACEORIENT(orient, v0, v1, v2, v3) \
602         case orient: v0 v1 v2 v3 break;
603     #define GENFACEVERT(orient, vert, mx,my,mz, sx,sy,sz) \
604         { \
605             vec v(mx sx, my sy, mz sz); \
606             vec reflect = vec(v).sub(camera1->o); \
607             reflect[dimension(orient)] = -reflect[dimension(orient)]; \
608             gle::attrib(v); \
609             GENFACENORMAL \
610             gle::attrib(reflect); \
611         }
612     #define GENFACENORMAL gle::attrib(n);
613     float x = m.o.x, y = m.o.y, z = m.o.z, csize = m.csize, rsize = m.rsize;
614     if(normal)
615     {
616         vec n = *normal;
617         switch(m.orient) { GENFACEVERTS(x, x, y, y, z, z, /**/, + csize, /**/, + rsize, + offset, - offset) }
618     }
619     #undef GENFACENORMAL
620     #define GENFACENORMAL
621     else switch(m.orient) { GENFACEVERTS(x, x, y, y, z, z, /**/, + csize, /**/, + rsize, + offset, - offset) }
622     #undef GENFACENORMAL
623     #undef GENFACEORIENT
624     #undef GENFACEVERT
625 }
626 
627 VARF(IDF_PERSIST, waterfallenv, 0, 1, 1, preloadwatershaders());
628 
rendermaterials()629 void rendermaterials()
630 {
631     vector<materialsurface *> vismats;
632     sortmaterials(vismats);
633     if(vismats.empty()) return;
634 
635     glDisable(GL_CULL_FACE);
636 
637     MSlot *mslot = NULL;
638     int lastorient = -1, lastmat = -1, usedwaterfall = -1;
639     bool depth = true, blended = false;
640     ushort envmapped = EMID_NONE;
641     static const vec normals[6] =
642     {
643         vec(-1, 0, 0),
644         vec( 1, 0, 0),
645         vec(0, -1, 0),
646         vec(0,  1, 0),
647         vec(0, 0, -1),
648         vec(0, 0,  1)
649     };
650 
651     GLOBALPARAM(camera, camera1->o);
652 
653     int lastfogtype = 1;
654     if(editmode && showmat && !drawtex)
655     {
656         glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
657         glEnable(GL_BLEND); blended = true;
658         foggednotextureshader->set();
659         zerofogcolor(); lastfogtype = 0;
660         loopv(vismats)
661         {
662             const materialsurface &m = *vismats[i];
663             if(lastmat!=m.material)
664             {
665                 xtraverts += gle::end();
666                 bvec color;
667                 switch(m.material&~MATF_INDEX)
668                 {
669                     case MAT_WATER:    color = bvec(255, 128,   0); break; // blue
670                     case MAT_CLIP:     color = bvec(  0, 255, 255); break; // red
671                     case MAT_GLASS:    color = bvec(255,   0,   0); break; // cyan
672                     case MAT_NOCLIP:   color = bvec(255,   0, 255); break; // green
673                     case MAT_LAVA:     color = bvec(  0, 128, 255); break; // orange
674                     case MAT_AICLIP:   color = bvec(  0,   0, 255); break; // yellow
675                     case MAT_DEATH:    color = bvec(192, 192, 192); break; // black
676                     case MAT_LADDER:   color = bvec(64,  196,  32); break; // violet
677                     case MAT_ALPHA:    color = bvec(  0, 255,   0); break; // pink
678                     case MAT_HURT:     color = bvec(128, 128, 128); break; // grey
679                     default: continue;
680                 }
681                 gle::color(color);
682                 lastmat = m.material;
683             }
684             drawmaterial(m, -0.1f);
685         }
686     }
687     else loopv(vismats)
688     {
689         const materialsurface &m = *vismats[i];
690         int matvol = m.material&~MATF_INDEX;
691         if(lastmat!=m.material || lastorient!=m.orient || (matvol==MAT_GLASS && envmapped && m.envmap != envmapped))
692         {
693             int fogtype = lastfogtype;
694             switch(matvol)
695             {
696                 case MAT_WATER:
697                     if(m.orient == O_TOP) continue;
698                     if(lastmat == m.material) break;
699                     mslot = &lookupmaterialslot(m.material, false);
700                     if(!mslot->loaded || !mslot->sts.inrange(1)) continue;
701                     else
702                     {
703                         xtraverts += gle::end();
704                         glBindTexture(GL_TEXTURE_2D, mslot->sts[1].t->id);
705                         float angle = fmod(float(lastmillis/600.0f/(2*M_PI)), 1.0f),
706                               s = angle - int(angle) - 0.5f;
707                         s *= 8 - fabs(s)*16;
708                         wfwave = vertwater ? WATER_AMPLITUDE*s-WATER_OFFSET : -WATER_OFFSET;
709                         wfscroll = 16.0f*lastmillis/1000.0f;
710                         wfxscale = TEX_SCALE/(mslot->sts[1].t->xs*mslot->scale);
711                         wfyscale = TEX_SCALE/(mslot->sts[1].t->ys*mslot->scale);
712 
713                         bvec wfcol = getwaterfallcol(m.material);
714                         if(wfcol.iszero()) wfcol = getwatercol(m.material);
715                         gle::color(wfcol, 192);
716 
717                         int wfog = getwaterfog(m.material);
718                         if(!wfog && !waterfallenv)
719                         {
720                             foggednotextureshader->set();
721                             fogtype = 1;
722                             if(blended) { glDisable(GL_BLEND); blended = false; }
723                             if(!depth) { glDepthMask(GL_TRUE); depth = true; }
724                             break;
725                         }
726                         else if((!waterfallrefract || reflecting || refracting) && !waterfallenv)
727                         {
728                             glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
729                             foggedshader->set();
730                             fogtype = 0;
731                             if(!blended) { glEnable(GL_BLEND); blended = true; }
732                             if(depth) { glDepthMask(GL_FALSE); depth = false; }
733                         }
734                         else
735                         {
736                             fogtype = 1;
737 
738                             if(waterfallrefract && wfog && !reflecting && !refracting)
739                             {
740                                 if(waterfallenv) SETSHADER(waterfallenvrefract);
741                                 else SETSHADER(waterfallrefract);
742                                 if(blended) { glDisable(GL_BLEND); blended = false; }
743                                 if(!depth) { glDepthMask(GL_TRUE); depth = true; }
744                             }
745                             else
746                             {
747                                 SETSHADER(waterfallenv);
748                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
749                                 if(wfog)
750                                 {
751                                     if(!blended) { glEnable(GL_BLEND); blended = true; }
752                                     if(depth) { glDepthMask(GL_FALSE); depth = false; }
753                                 }
754                                 else
755                                 {
756                                     if(blended) { glDisable(GL_BLEND); blended = false; }
757                                     if(!depth) { glDepthMask(GL_TRUE); depth = true; }
758                                 }
759                             }
760 
761                             if(usedwaterfall != m.material)
762                             {
763                                 Texture *dudv = mslot->sts.inrange(5) ? mslot->sts[5].t : notexture;
764                                 float scale = 8.0f/(dudv->ys*mslot->scale);
765                                 LOCALPARAMF(dudvoffset, 0, scale*16*lastmillis/1000.0f);
766 
767                                 glActiveTexture_(GL_TEXTURE1);
768                                 glBindTexture(GL_TEXTURE_2D, mslot->sts.inrange(4) ? mslot->sts[4].t->id : notexture->id);
769                                 glActiveTexture_(GL_TEXTURE2);
770                                 glBindTexture(GL_TEXTURE_2D, mslot->sts.inrange(5) ? mslot->sts[5].t->id : notexture->id);
771                                 if(waterfallenv)
772                                 {
773                                     glActiveTexture_(GL_TEXTURE3);
774                                     glBindTexture(GL_TEXTURE_CUBE_MAP, lookupenvmap(*mslot));
775                                 }
776                                 if(waterfallrefract && (!reflecting || !refracting) && usedwaterfall < 0)
777                                 {
778                                     glActiveTexture_(GL_TEXTURE4);
779                                     extern void setupwaterfallrefract();
780                                     setupwaterfallrefract();
781                                 }
782                                 glActiveTexture_(GL_TEXTURE0);
783 
784                                 usedwaterfall = m.material;
785                             }
786                         }
787                     }
788                     break;
789 
790                 case MAT_LAVA:
791                     if(lastmat==m.material && lastorient!=O_TOP && m.orient!=O_TOP) break;
792                     mslot = &lookupmaterialslot(m.material, false);
793                     if(!mslot->loaded) continue;
794                     else
795                     {
796                         int subslot = m.orient==O_TOP ? 0 : 1;
797                         if(!mslot->sts.inrange(subslot)) continue;
798                         xtraverts += gle::end();
799                         glBindTexture(GL_TEXTURE_2D, mslot->sts[subslot].t->id);
800                     }
801                     if(m.orient!=O_TOP)
802                     {
803                         float angle = fmod(float(lastmillis/2000.0f/(2*M_PI)), 1.0f),
804                               s = angle - int(angle) - 0.5f;
805                         s *= 8 - fabs(s)*16;
806                         wfwave = vertwater ? WATER_AMPLITUDE*s-WATER_OFFSET : -WATER_OFFSET;
807                         wfscroll = 16.0f*lastmillis/3000.0f;
808                         wfxscale = TEX_SCALE/(mslot->sts[1].t->xs*mslot->scale);
809                         wfyscale = TEX_SCALE/(mslot->sts[1].t->ys*mslot->scale);
810                     }
811                     if(lastmat!=m.material)
812                     {
813                         if(!depth) { glDepthMask(GL_TRUE); depth = true; }
814                         if(blended) { glDisable(GL_BLEND); blended = false; }
815                         float t = lastmillis/2000.0f;
816                         t -= floor(t);
817                         t = 1.0f - 2*fabs(t-0.5f);
818                         extern int glare;
819                         if(glare) t = 0.625f + 0.075f*t;
820                         else t = 0.5f + 0.5f*t;
821                         gle::colorf(t, t, t);
822                         if(glaring) SETSHADER(lavaglare); else SETSHADER(lava);
823                         fogtype = 1;
824                     }
825                     break;
826 
827                 case MAT_GLASS:
828                     if((m.envmap==EMID_NONE || !glassenv || envmapped==m.envmap) && lastmat==m.material) break;
829                     xtraverts += gle::end();
830                     if(m.envmap!=EMID_NONE && glassenv && envmapped!=m.envmap)
831                     {
832                         glBindTexture(GL_TEXTURE_CUBE_MAP, lookupenvmap(m.envmap));
833                         envmapped = m.envmap;
834                     }
835                     if(lastmat!=m.material)
836                     {
837                         if(!blended) { glEnable(GL_BLEND); blended = true; }
838                         if(depth) { glDepthMask(GL_FALSE); depth = false; }
839                         const bvec &gcol = getglasscol(m.material);
840                         if(m.envmap!=EMID_NONE && glassenv)
841                         {
842                             glBlendFunc(GL_ONE, GL_SRC_ALPHA);
843                             gle::color(gcol);
844                             SETSHADER(glass);
845                         }
846                         else
847                         {
848                             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
849                             gle::color(gcol, 40);
850                             foggednotextureshader->set();
851                             fogtype = 1;
852                         }
853                     }
854                     break;
855 
856                 default: continue;
857             }
858             lastmat = m.material;
859             lastorient = m.orient;
860             if(fogtype!=lastfogtype)
861             {
862                 if(fogtype) resetfogcolor();
863                 else zerofogcolor();
864                 lastfogtype = fogtype;
865             }
866         }
867         switch(matvol)
868         {
869             case MAT_WATER:
870                 renderwaterfall(m, 0.1f, waterfallenv ? &normals[m.orient] : NULL);
871                 break;
872 
873             case MAT_LAVA:
874                 if(m.orient==O_TOP) renderlava(m, mslot->sts[0].t, mslot->scale);
875                 else renderwaterfall(m, 0.1f);
876                 break;
877 
878             case MAT_GLASS:
879                 if(m.envmap!=EMID_NONE && glassenv) drawglass(m, 0.1f, &normals[m.orient]);
880                 else drawmaterial(m, 0.1f);
881                 break;
882         }
883     }
884 
885     xtraverts += gle::end();
886 
887     if(!depth) glDepthMask(GL_TRUE);
888     if(blended) glDisable(GL_BLEND);
889     if(!lastfogtype) resetfogcolor();
890     extern int wireframe;
891     if(editmode && showmat && !drawtex && !wireframe)
892     {
893         foggednotextureshader->set();
894         rendermatgrid(vismats);
895     }
896 
897     glEnable(GL_CULL_FACE);
898 }
899 
900