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