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