1 // octarender.cpp: fill vertex arrays with different cube surfaces.
2 
3 #include "engine.h"
4 
5 struct vboinfo
6 {
7     int uses;
8     uchar *data;
9 };
10 
11 hashtable<GLuint, vboinfo> vbos;
12 
13 VAR(0, printvbo, 0, 0, 1);
14 VARFN(0, vbosize, maxvbosize, 0, 1<<14, 1<<16, allchanged());
15 
16 enum
17 {
18     VBO_VBUF = 0,
19     VBO_EBUF,
20     VBO_SKYBUF,
21     VBO_DECALBUF,
22     NUMVBO
23 };
24 
25 static vector<uchar> vbodata[NUMVBO];
26 static vector<vtxarray *> vbovas[NUMVBO];
27 static int vbosize[NUMVBO];
28 
destroyvbo(GLuint vbo)29 void destroyvbo(GLuint vbo)
30 {
31     vboinfo *exists = vbos.access(vbo);
32     if(!exists) return;
33     vboinfo &vbi = *exists;
34     if(vbi.uses <= 0) return;
35     vbi.uses--;
36     if(!vbi.uses)
37     {
38         glDeleteBuffers_(1, &vbo);
39         if(vbi.data) delete[] vbi.data;
40         vbos.remove(vbo);
41     }
42 }
43 
genvbo(int type,void * buf,int len,vtxarray ** vas,int numva)44 void genvbo(int type, void *buf, int len, vtxarray **vas, int numva)
45 {
46     gle::disable();
47 
48     GLuint vbo;
49     glGenBuffers_(1, &vbo);
50     GLenum target = type==VBO_VBUF ? GL_ARRAY_BUFFER : GL_ELEMENT_ARRAY_BUFFER;
51     glBindBuffer_(target, vbo);
52     glBufferData_(target, len, buf, GL_STATIC_DRAW);
53     glBindBuffer_(target, 0);
54 
55     vboinfo &vbi = vbos[vbo];
56     vbi.uses = numva;
57     vbi.data = new uchar[len];
58     memcpy(vbi.data, buf, len);
59 
60     if(printvbo) conoutf("VBO %d: type %d, size %d, %d uses", vbo, type, len, numva);
61 
62     loopi(numva)
63     {
64         vtxarray *va = vas[i];
65         switch(type)
66         {
67             case VBO_VBUF:
68                 va->vbuf = vbo;
69                 va->vdata = (vertex *)vbi.data;
70                 break;
71             case VBO_EBUF:
72                 va->ebuf = vbo;
73                 va->edata = (ushort *)vbi.data;
74                 break;
75             case VBO_SKYBUF:
76                 va->skybuf = vbo;
77                 va->skydata = (ushort *)vbi.data;
78                 break;
79             case VBO_DECALBUF:
80                 va->decalbuf = vbo;
81                 va->decaldata = (ushort *)vbi.data;
82                 break;
83         }
84     }
85 }
86 
flushvbo(int type=-1)87 void flushvbo(int type = -1)
88 {
89     if(type < 0)
90     {
91         loopi(NUMVBO) flushvbo(i);
92         return;
93     }
94 
95     vector<uchar> &data = vbodata[type];
96     if(data.empty()) return;
97     vector<vtxarray *> &vas = vbovas[type];
98     genvbo(type, data.getbuf(), data.length(), vas.getbuf(), vas.length());
99     data.setsize(0);
100     vas.setsize(0);
101     vbosize[type] = 0;
102 }
103 
addvbo(vtxarray * va,int type,int numelems,int elemsize)104 uchar *addvbo(vtxarray *va, int type, int numelems, int elemsize)
105 {
106     switch(type)
107     {
108         case VBO_VBUF: va->voffset = vbosize[type]; break;
109         case VBO_EBUF: va->eoffset = vbosize[type]; break;
110         case VBO_SKYBUF: va->skyoffset = vbosize[type]; break;
111         case VBO_DECALBUF: va->decaloffset = vbosize[type]; break;
112     }
113 
114     vbosize[type] += numelems;
115 
116     vector<uchar> &data = vbodata[type];
117     vector<vtxarray *> &vas = vbovas[type];
118 
119     vas.add(va);
120 
121     int len = numelems*elemsize;
122     uchar *buf = data.reserve(len).buf;
123     data.advance(len);
124     return buf;
125 }
126 
127 struct verthash
128 {
129     static const int SIZE = 1<<13;
130     int table[SIZE];
131     vector<vertex> verts;
132     vector<int> chain;
133 
verthashverthash134     verthash() { clearverts(); }
135 
clearvertsverthash136     void clearverts()
137     {
138         memset(table, -1, sizeof(table));
139         chain.setsize(0);
140         verts.setsize(0);
141     }
142 
addvertverthash143     int addvert(const vertex &v)
144     {
145         uint h = hthash(v.pos)&(SIZE-1);
146         for(int i = table[h]; i>=0; i = chain[i])
147         {
148             const vertex &c = verts[i];
149             if(c.pos==v.pos && c.tc==v.tc && c.norm==v.norm && c.tangent==v.tangent)
150                  return i;
151         }
152         if(verts.length() >= USHRT_MAX) return -1;
153         verts.add(v);
154         chain.add(table[h]);
155         return table[h] = verts.length()-1;
156     }
157 
addvertverthash158     int addvert(const vec &pos, const vec &tc = vec(0, 0, 0), const bvec &norm = bvec(128, 128, 128), const bvec4 &tangent = bvec4(128, 128, 128, 128))
159     {
160         vertex vtx;
161         vtx.pos = pos;
162         vtx.tc = tc;
163         vtx.norm = norm;
164         vtx.tangent = tangent;
165         return addvert(vtx);
166     }
167 };
168 
169 enum
170 {
171     NO_ALPHA = 0,
172     ALPHA_BACK,
173     ALPHA_FRONT,
174     ALPHA_REFRACT
175 };
176 
compareentcolor(ushort entid1,ushort entid2)177 bool compareentcolor(ushort entid1, ushort entid2)
178 {
179     if(entid1 != USHRT_MAX && entid2 != USHRT_MAX)
180     {
181         const vector<extentity *> &ents = entities::getents();
182         bool found1 = ents.inrange(entid1) && ents[entid1]->type == ET_DECAL,
183              found2 = ents.inrange(entid2) && ents[entid2]->type == ET_DECAL;
184         if(found1 && found2)
185         {
186             int blend1 = ents[entid1]->attrs[5] > 0 && ents[entid1]->attrs[5] < 100 ? ents[entid1]->attrs[5] : 100,
187                 blend2 = ents[entid2]->attrs[5] > 0 && ents[entid2]->attrs[5] < 100 ? ents[entid2]->attrs[5] : 100;
188             if(blend1 != blend2) return false;
189             int color1 = ents[entid1]->attrs[6] > 0 ? ents[entid1]->attrs[6] : 0xFFFFFF,
190                 color2 = ents[entid2]->attrs[6] > 0 ? ents[entid2]->attrs[6] : 0xFFFFFF;
191             if(color1 != color2) return false;
192             int palette1 = ents[entid1]->attrs[7] || ents[entid1]->attrs[8] ? game::hexpalette(ents[entid1]->attrs[7], ents[entid1]->attrs[8]) : 0xFFFFFF,
193                 palette2 = ents[entid2]->attrs[7] || ents[entid2]->attrs[8] ? game::hexpalette(ents[entid2]->attrs[7], ents[entid2]->attrs[8]) : 0xFFFFFF;
194             if(palette1 != palette2) return false;
195         }
196         else if(found1 || found2) return false;
197     }
198     else if(entid1 != USHRT_MAX || entid2 != USHRT_MAX) return false;
199     return true;
200 }
201 
sortentcolor(ushort entid1,ushort entid2)202 int sortentcolor(ushort entid1, ushort entid2)
203 {
204     if(entid1 != USHRT_MAX && entid2 != USHRT_MAX)
205     {
206         const vector<extentity *> &ents = entities::getents();
207         bool found1 = ents.inrange(entid1), found2 = ents.inrange(entid2);
208         if(found1 && found2)
209         {
210             bool decal1 = ents[entid1]->type == ET_DECAL, decal2 = ents[entid2]->type == ET_DECAL;
211             if(decal1 && decal2)
212             {
213                 int blend1 = ents[entid1]->attrs[5] > 0 && ents[entid1]->attrs[5] < 100 ? ents[entid1]->attrs[5] : 100,
214                     blend2 = ents[entid2]->attrs[5] > 0 && ents[entid2]->attrs[5] < 100 ? ents[entid2]->attrs[5] : 100;
215                 if(blend1 < blend2) return -1;
216                 if(blend1 > blend2) return 1;
217                 int color1 = ents[entid1]->attrs[6] > 0 ? ents[entid1]->attrs[6] : 0xFFFFFF,
218                     color2 = ents[entid2]->attrs[6] > 0 ? ents[entid2]->attrs[6] : 0xFFFFFF;
219                 if(color1 < color2) return -1;
220                 if(color1 > color2) return 1;
221                 int palette1 = ents[entid1]->attrs[7] || ents[entid1]->attrs[8] ? game::hexpalette(ents[entid1]->attrs[7], ents[entid1]->attrs[8]) : 0xFFFFFF,
222                     palette2 = ents[entid2]->attrs[7] || ents[entid2]->attrs[8] ? game::hexpalette(ents[entid2]->attrs[7], ents[entid2]->attrs[8]) : 0xFFFFFF;
223                 if(palette1 < palette2) return -1;
224                 if(palette1 > palette2) return 1;
225             }
226             else if(decal1) return -1;
227             else if(decal2) return 1;
228         }
229         else if(found1) return -1;
230         else if(found2) return 1;
231     }
232     else if(entid1 != USHRT_MAX) return -1;
233     else if(entid2 != USHRT_MAX) return 1;
234     return 0;
235 }
236 
237 
238 struct sortkey
239 {
240     ushort tex, envmap, entid;
241     uchar orient, layer, alpha;
242 
sortkeysortkey243     sortkey() {}
sortkeysortkey244     sortkey(ushort tex, uchar orient, uchar layer = LAYER_TOP, ushort envmap = EMID_NONE, uchar alpha = NO_ALPHA, ushort entid = USHRT_MAX)
245      : tex(tex), envmap(envmap), entid(entid), orient(orient), layer(layer), alpha(alpha)
246     {}
247 
operator ==sortkey248     bool operator==(const sortkey &o) const { return tex==o.tex && envmap==o.envmap && orient==o.orient && layer==o.layer && alpha==o.alpha && compareentcolor(entid, o.entid); }
249 
sortsortkey250     static inline bool sort(const sortkey &x, const sortkey &y)
251     {
252         if(x.alpha < y.alpha) return true;
253         if(x.alpha > y.alpha) return false;
254         if(x.layer < y.layer) return true;
255         if(x.layer > y.layer) return false;
256         if(x.tex == y.tex)
257         {
258             if(x.envmap < y.envmap) return true;
259             if(x.envmap > y.envmap) return false;
260             if(x.orient < y.orient) return true;
261             if(x.orient > y.orient) return false;
262             return false;
263         }
264         VSlot &xs = lookupvslot(x.tex, false), &ys = lookupvslot(y.tex, false);
265         if(xs.slot->shader < ys.slot->shader) return true;
266         if(xs.slot->shader > ys.slot->shader) return false;
267         if(xs.slot->params.length() < ys.slot->params.length()) return true;
268         if(xs.slot->params.length() > ys.slot->params.length()) return false;
269         if(x.tex < y.tex) return true;
270         if(x.tex > y.tex) return false;
271         return sortentcolor(x.entid, y.entid) < 0;
272     }
273 };
274 
htcmp(const sortkey & x,const sortkey & y)275 static inline bool htcmp(const sortkey &x, const sortkey &y)
276 {
277     return x == y;
278 }
279 
hthash(const sortkey & k)280 static inline uint hthash(const sortkey &k)
281 {
282     return k.tex;
283 }
284 
285 struct decalkey
286 {
287     ushort tex, envmap, reuse, entid;
288 
decalkeydecalkey289     decalkey() {}
decalkeydecalkey290     decalkey(ushort tex, ushort envmap = EMID_NONE, ushort reuse = 0, ushort entid = USHRT_MAX)
291      : tex(tex), envmap(envmap), reuse(reuse), entid(entid)
292     {}
293 
operator ==decalkey294     bool operator==(const decalkey &o) const { return tex==o.tex && envmap==o.envmap && reuse==o.reuse && compareentcolor(entid, o.entid); }
295 
sortdecalkey296     static inline bool sort(const decalkey &x, const decalkey &y)
297     {
298         if(x.tex == y.tex)
299         {
300             if(x.envmap < y.envmap) return true;
301             if(x.envmap > y.envmap) return false;
302             if(x.reuse < y.reuse) return true;
303             else return false;
304         }
305         DecalSlot &xs = lookupdecalslot(x.tex, false), &ys = lookupdecalslot(y.tex, false);
306         if(xs.slot->shader < ys.slot->shader) return true;
307         if(xs.slot->shader > ys.slot->shader) return false;
308         if(xs.slot->params.length() < ys.slot->params.length()) return true;
309         if(xs.slot->params.length() > ys.slot->params.length()) return false;
310         if(x.tex < y.tex) return true;
311         if(x.tex > y.tex) return false;
312         return sortentcolor(x.entid, y.entid);
313     }
314 };
315 
htcmp(const decalkey & x,const decalkey & y)316 static inline bool htcmp(const decalkey &x, const decalkey &y)
317 {
318     return x == y;
319 }
320 
hthash(const decalkey & k)321 static inline uint hthash(const decalkey &k)
322 {
323     return k.tex;
324 }
325 
326 struct sortval
327 {
328      vector<ushort> tris;
329 
sortvalsortval330      sortval() {}
331 };
332 struct vacollect : verthash
333 {
334     ivec origin;
335     int size;
336     hashtable<sortkey, sortval> indices;
337     hashtable<decalkey, sortval> decalindices;
338     vector<ushort> skyindices;
339     vector<sortkey> texs;
340     vector<decalkey> decaltexs;
341     vector<grasstri> grasstris;
342     vector<materialsurface> matsurfs;
343     vector<octaentities *> mapmodels, decals, extdecals;
344     int worldtris, skytris, decaltris;
345     vec alphamin, alphamax;
346     vec refractmin, refractmax;
347     vec skymin, skymax;
348     ivec nogimin, nogimax;
349 
clearvacollect350     void clear()
351     {
352         clearverts();
353         worldtris = skytris = decaltris = 0;
354         indices.clear();
355         decalindices.clear();
356         skyindices.setsize(0);
357         matsurfs.setsize(0);
358         mapmodels.setsize(0);
359         decals.setsize(0);
360         extdecals.setsize(0);
361         grasstris.setsize(0);
362         texs.setsize(0);
363         decaltexs.setsize(0);
364         alphamin = refractmin = skymin = vec(1e16f, 1e16f, 1e16f);
365         alphamax = refractmax = skymax = vec(-1e16f, -1e16f, -1e16f);
366         nogimin = ivec(INT_MAX, INT_MAX, INT_MAX);
367         nogimax = ivec(INT_MIN, INT_MIN, INT_MIN);
368     }
369 
optimizevacollect370     void optimize()
371     {
372         enumeratekt(indices, sortkey, k, sortval, t,
373         {
374             if(t.tris.length()) texs.add(k);
375         });
376         texs.sort(sortkey::sort);
377 
378         matsurfs.shrink(optimizematsurfs(matsurfs.getbuf(), matsurfs.length()));
379     }
380 
381 #define GENVERTS(type, ptr, body) do \
382     { \
383         type *f = (type *)ptr; \
384         loopv(verts) \
385         { \
386             const vertex &v = verts[i]; \
387             body; \
388             f++; \
389         } \
390     } while(0)
391 
genvertsvacollect392     void genverts(void *buf)
393     {
394         GENVERTS(vertex, buf, { *f = v; f->norm.flip(); f->tangent.flip(); });
395     }
396 
gendecalvacollect397     void gendecal(const extentity &e, DecalSlot &s, const decalkey &key)
398     {
399         matrix3 orient;
400         orient.identity();
401         if(e.attrs[1]) orient.rotate_around_z(sincosmod360(e.attrs[1]));
402         if(e.attrs[2]) orient.rotate_around_x(sincosmod360(e.attrs[2]));
403         if(e.attrs[3]) orient.rotate_around_y(sincosmod360(-e.attrs[3]));
404         vec size(max(float(e.attrs[4]), 1.0f));
405         size.y *= s.depth;
406         if(!s.sts.empty())
407         {
408             Texture *t = s.sts[0].t;
409             if(t->xs < t->ys) size.x *= t->xs / float(t->ys);
410             else if(t->xs > t->ys) size.z *= t->ys / float(t->xs);
411         }
412         vec center = orient.transform(vec(0, size.y*0.5f, 0)).add(e.o), radius = orient.abstransform(vec(size).mul(0.5f));
413         vec bbmin = vec(center).sub(radius), bbmax = vec(center).add(radius);
414         vec clipoffset = orient.transposedtransform(center).msub(size, 0.5f);
415         loopv(texs)
416         {
417             const sortkey &k = texs[i];
418             if(k.layer == LAYER_BLEND || k.alpha != NO_ALPHA) continue;
419             const sortval &t = indices[k];
420             if(t.tris.empty()) continue;
421             decalkey tkey(key);
422             if(shouldreuseparams(s, lookupvslot(k.tex, false))) tkey.reuse = k.tex;
423             for(int j = 0; j < t.tris.length(); j += 3)
424             {
425                 const vertex &t0 = verts[t.tris[j]], &t1 = verts[t.tris[j+1]], &t2 = verts[t.tris[j+2]];
426                 vec v0 = t0.pos, v1 = t1.pos, v2 = t2.pos;
427                 vec tmin = vec(v0).min(v1).min(v2), tmax = vec(v0).max(v1).max(v2);
428                 if(tmin.x >= bbmax.x || tmin.y >= bbmax.y || tmin.z >= bbmax.z ||
429                    tmax.x <= bbmin.x || tmax.y <= bbmin.y || tmax.z <= bbmin.z)
430                     continue;
431                 float f0 = t0.norm.tonormal().dot(orient.b), f1 = t1.norm.tonormal().dot(orient.b), f2 = t2.norm.tonormal().dot(orient.b);
432                 if(f0 >= 0 && f1 >= 0 && f2 >= 0) continue;
433                 vec p1[9], p2[9];
434                 p1[0] = v0; p1[1] = v1; p1[2] = v2;
435                 int nump = polyclip(p1, 3, orient.b, clipoffset.y, clipoffset.y + size.y, p2);
436                 if(nump < 3) continue;
437                 nump = polyclip(p2, nump, orient.a, clipoffset.x, clipoffset.x + size.x, p1);
438                 if(nump < 3) continue;
439                 nump = polyclip(p1, nump, orient.c, clipoffset.z, clipoffset.z + size.z, p2);
440                 if(nump < 3) continue;
441 
442                 bvec4 n0 = t0.norm, n1 = t1.norm, n2 = t2.norm,
443                       x0 = t0.tangent, x1 = t1.tangent, x2 = t2.tangent;
444                 vec e1 = vec(v1).sub(v0), e2 = vec(v2).sub(v0);
445                 float d11 = e1.dot(e1), d12 = e1.dot(e2), d22 = e2.dot(e2);
446                 int idx[9];
447                 loopk(nump)
448                 {
449                     vertex v;
450                     v.pos = p2[k];
451                     vec ep = vec(v.pos).sub(v0);
452                     float dp1 = ep.dot(e1), dp2 = ep.dot(e2), denom = d11*d22 - d12*d12,
453                           b1 = (d22*dp1 - d12*dp2) / denom,
454                           b2 = (d11*dp2 - d12*dp1) / denom,
455                           b0 = 1 - b1 - b2;
456                     v.norm.lerp(n0, n1, n2, b0, b1, b2);
457                     v.norm.w = uchar(127.5f - 127.5f*(f0*b0 + f1*b1 + f2*b2));
458                     vec tc = orient.transposedtransform(vec(center).sub(v.pos)).div(size).add(0.5f);
459                     v.tc = vec(tc.x, tc.z, s.fade ? tc.y * s.depth / s.fade : 1.0f);
460                     v.tangent.lerp(x0, x1, x2, b0, b1, b2);
461                     idx[k] = addvert(v);
462                 }
463                 vector<ushort> &tris = decalindices[tkey].tris;
464                 loopk(nump-2) if(idx[0] != idx[k+1] && idx[k+1] != idx[k+2] && idx[k+2] != idx[0])
465                 {
466                     tris.add(idx[0]);
467                     tris.add(idx[k+1]);
468                     tris.add(idx[k+2]);
469                     decaltris += 3;
470                 }
471             }
472         }
473     }
474 
gendecalsvacollect475     void gendecals()
476     {
477         if(decals.length()) extdecals.put(decals.getbuf(), decals.length());
478         if(extdecals.empty()) return;
479         vector<extentity *> &ents = entities::getents();
480         loopv(extdecals)
481         {
482             octaentities *oe = extdecals[i];
483             loopvj(oe->decals)
484             {
485                 extentity &e = *ents[oe->decals[j]];
486                 if(e.flags&EF_RENDER || !checkmapvariant(e.attrs[9]) || !checkmapeffects(e.attrs[10])) continue;
487                 e.flags |= EF_RENDER;
488                 DecalSlot &s = lookupdecalslot(e.attrs[0], true);
489                 if(!s.shader) continue;
490                 ushort envmap = s.shader->type&SHADER_ENVMAP ? (s.texmask&(1<<TEX_ENVMAP) ? EMID_CUSTOM : closestenvmap(e.o)) : EMID_NONE;
491                 decalkey k(e.attrs[0], envmap, 0, oe->decals[j]);
492                 gendecal(e, s, k);
493             }
494         }
495         loopv(extdecals)
496         {
497             octaentities *oe = extdecals[i];
498             loopvj(oe->decals)
499             {
500                 extentity &e = *ents[oe->decals[j]];
501                 if(e.flags&EF_RENDER) e.flags &= ~EF_RENDER;
502             }
503         }
504         enumeratekt(decalindices, decalkey, k, sortval, t,
505         {
506             if(t.tris.length()) decaltexs.add(k);
507         });
508         decaltexs.sort(decalkey::sort);
509     }
510 
setupdatavacollect511     void setupdata(vtxarray *va)
512     {
513         optimize();
514         gendecals();
515 
516         va->verts = verts.length();
517         va->tris = worldtris/3;
518         va->vbuf = 0;
519         va->vdata = 0;
520         va->minvert = 0;
521         va->maxvert = va->verts-1;
522         va->voffset = 0;
523         if(va->verts)
524         {
525             if(vbosize[VBO_VBUF] + verts.length() > maxvbosize ||
526                vbosize[VBO_EBUF] + worldtris > USHRT_MAX ||
527                vbosize[VBO_SKYBUF] + skytris > USHRT_MAX ||
528                vbosize[VBO_DECALBUF] + decaltris > USHRT_MAX)
529                 flushvbo();
530 
531             uchar *vdata = addvbo(va, VBO_VBUF, va->verts, sizeof(vertex));
532             genverts(vdata);
533             va->minvert += va->voffset;
534             va->maxvert += va->voffset;
535         }
536 
537         va->matbuf = NULL;
538         va->matsurfs = matsurfs.length();
539         va->matmask = 0;
540         if(va->matsurfs)
541         {
542             va->matbuf = new materialsurface[matsurfs.length()];
543             memcpy(va->matbuf, matsurfs.getbuf(), matsurfs.length()*sizeof(materialsurface));
544             loopv(matsurfs)
545             {
546                 materialsurface &m = matsurfs[i];
547                 if(m.visible == MATSURF_EDIT_ONLY) continue;
548                 switch(m.material)
549                 {
550                     case MAT_GLASS: case MAT_LAVA: case MAT_WATER: break;
551                     default: continue;
552                 }
553                 va->matmask |= 1<<m.material;
554             }
555         }
556 
557         va->skybuf = 0;
558         va->skydata = 0;
559         va->skyoffset = 0;
560         va->sky = skyindices.length();
561         if(va->sky)
562         {
563             ushort *skydata = (ushort *)addvbo(va, VBO_SKYBUF, va->sky, sizeof(ushort));
564             memcpy(skydata, skyindices.getbuf(), va->sky*sizeof(ushort));
565             if(va->voffset) loopi(va->sky) skydata[i] += va->voffset;
566         }
567 
568         va->texelems = NULL;
569         va->texs = texs.length();
570         va->blendtris = 0;
571         va->blends = 0;
572         va->alphabacktris = 0;
573         va->alphaback = 0;
574         va->alphafronttris = 0;
575         va->alphafront = 0;
576         va->refracttris = 0;
577         va->refract = 0;
578         va->ebuf = 0;
579         va->edata = 0;
580         va->eoffset = 0;
581         if(va->texs)
582         {
583             va->texelems = new elementset[va->texs];
584             ushort *edata = (ushort *)addvbo(va, VBO_EBUF, worldtris, sizeof(ushort)), *curbuf = edata;
585             loopv(texs)
586             {
587                 const sortkey &k = texs[i];
588                 const sortval &t = indices[k];
589                 elementset &e = va->texelems[i];
590                 e.texture = k.tex;
591                 e.orient = k.orient;
592                 e.layer = k.layer;
593                 e.envmap = k.envmap;
594                 e.entid = k.entid;
595                 ushort *startbuf = curbuf;
596                 e.minvert = USHRT_MAX;
597                 e.maxvert = 0;
598 
599                 if(t.tris.length())
600                 {
601                     memcpy(curbuf, t.tris.getbuf(), t.tris.length() * sizeof(ushort));
602 
603                     loopvj(t.tris)
604                     {
605                         curbuf[j] += va->voffset;
606                         e.minvert = min(e.minvert, curbuf[j]);
607                         e.maxvert = max(e.maxvert, curbuf[j]);
608                     }
609 
610                     curbuf += t.tris.length();
611                 }
612                 e.length = curbuf-startbuf;
613 
614                 if(k.layer==LAYER_BLEND) { va->texs--; va->tris -= e.length/3; va->blends++; va->blendtris += e.length/3; }
615                 else if(k.alpha==ALPHA_BACK) { va->texs--; va->tris -= e.length/3; va->alphaback++; va->alphabacktris += e.length/3; }
616                 else if(k.alpha==ALPHA_FRONT) { va->texs--; va->tris -= e.length/3; va->alphafront++; va->alphafronttris += e.length/3; }
617                 else if(k.alpha==ALPHA_REFRACT) { va->texs--; va->tris -= e.length/3; va->refract++; va->refracttris += e.length/3; }
618             }
619         }
620 
621         va->texmask = 0;
622         va->dyntexs = 0;
623         loopi(va->texs+va->blends+va->alphaback+va->alphafront+va->refract)
624         {
625             VSlot &vslot = lookupvslot(va->texelems[i].texture, false);
626             if(vslot.isdynamic()) va->dyntexs++;
627             Slot &slot = *vslot.slot;
628             loopvj(slot.sts) va->texmask |= 1<<slot.sts[j].type;
629             if(slot.shader && slot.shader->type&SHADER_ENVMAP) va->texmask |= 1<<TEX_ENVMAP;
630         }
631 
632         va->decalbuf = 0;
633         va->decaldata = 0;
634         va->decaloffset = 0;
635         va->decalelems = NULL;
636         va->decaltexs = decaltexs.length();
637         va->decaltris = decaltris/3;
638         if(va->decaltexs)
639         {
640             va->decalelems = new elementset[va->decaltexs];
641             ushort *edata = (ushort *)addvbo(va, VBO_DECALBUF, decaltris, sizeof(ushort)), *curbuf = edata;
642             loopv(decaltexs)
643             {
644                 const decalkey &k = decaltexs[i];
645                 const sortval &t = decalindices[k];
646                 elementset &e = va->decalelems[i];
647                 e.texture = k.tex;
648                 e.reuse = k.reuse;
649                 e.envmap = k.envmap;
650                 e.entid = k.entid;
651                 ushort *startbuf = curbuf;
652                 e.minvert = USHRT_MAX;
653                 e.maxvert = 0;
654 
655                 if(t.tris.length())
656                 {
657                     memcpy(curbuf, t.tris.getbuf(), t.tris.length() * sizeof(ushort));
658 
659                     loopvj(t.tris)
660                     {
661                         curbuf[j] += va->voffset;
662                         e.minvert = min(e.minvert, curbuf[j]);
663                         e.maxvert = max(e.maxvert, curbuf[j]);
664                     }
665 
666                     curbuf += t.tris.length();
667                 }
668                 e.length = curbuf-startbuf;
669             }
670         }
671 
672         if(grasstris.length())
673         {
674             va->grasstris.move(grasstris);
675             loadgrassshaders();
676         }
677 
678         if(mapmodels.length()) va->mapmodels.put(mapmodels.getbuf(), mapmodels.length());
679         if(decals.length()) va->decals.put(decals.getbuf(), decals.length());
680     }
681 
emptyvavacollect682     bool emptyva()
683     {
684         return verts.empty() && matsurfs.empty() && skyindices.empty() && grasstris.empty() && mapmodels.empty() && decals.empty();
685     }
686 } vc;
687 
688 int recalcprogress = 0;
689 #define rcprogress(s)     if((recalcprogress++&0xFFF)==0) progress(recalcprogress/(float)allocnodes, s);
690 
691 vector<tjoint> tjoints;
692 
693 VARF(IDF_PERSIST, filltjoints, 0, 1, 1, allchanged());
694 
reduceslope(ivec & n)695 void reduceslope(ivec &n)
696 {
697     int mindim = -1, minval = 64;
698     loopi(3) if(n[i])
699     {
700         int val = abs(n[i]);
701         if(mindim < 0 || val < minval)
702         {
703             mindim = i;
704             minval = val;
705         }
706     }
707     if(!(n[R[mindim]]%minval) && !(n[C[mindim]]%minval)) n.div(minval);
708     while(!((n.x|n.y|n.z)&1)) n.shr(1);
709 }
710 
711 // [rotation][orient]
712 extern const vec orientation_tangent[8][6] =
713 {
714     { vec( 0,  1,  0), vec( 0, -1,  0), vec(-1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0) },
715     { vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0, -1,  0), vec( 0,  1,  0) },
716     { vec( 0, -1,  0), vec( 0,  1,  0), vec( 1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0) },
717     { vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  1,  0), vec( 0, -1,  0) },
718     { vec( 0, -1,  0), vec( 0,  1,  0), vec( 1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0) },
719     { vec( 0,  1,  0), vec( 0, -1,  0), vec(-1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0) },
720     { vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0, -1,  0), vec( 0,  1,  0) },
721     { vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  1,  0), vec( 0, -1,  0) },
722 };
723 extern const vec orientation_bitangent[8][6] =
724 {
725     { vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0, -1,  0), vec( 0,  1,  0) },
726     { vec( 0, -1,  0), vec( 0,  1,  0), vec( 1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0) },
727     { vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  1,  0), vec( 0, -1,  0) },
728     { vec( 0,  1,  0), vec( 0, -1,  0), vec(-1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0) },
729     { vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0,  0, -1), vec( 0, -1,  0), vec( 0,  1,  0) },
730     { vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  0,  1), vec( 0,  1,  0), vec( 0, -1,  0) },
731     { vec( 0,  1,  0), vec( 0, -1,  0), vec(-1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0), vec( 1,  0,  0) },
732     { vec( 0, -1,  0), vec( 0,  1,  0), vec( 1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0), vec(-1,  0,  0) },
733 };
734 
addtris(VSlot & vslot,int orient,const sortkey & key,vertex * verts,int * index,int numverts,int convex,int tj)735 void addtris(VSlot &vslot, int orient, const sortkey &key, vertex *verts, int *index, int numverts, int convex, int tj)
736 {
737     int &total = key.tex==DEFAULT_SKY ? vc.skytris : vc.worldtris;
738     int edge = orient*(MAXFACEVERTS+1);
739     loopi(numverts-2) if(index[0]!=index[i+1] && index[i+1]!=index[i+2] && index[i+2]!=index[0])
740     {
741         vector<ushort> &idxs = key.tex==DEFAULT_SKY ? vc.skyindices : vc.indices[key].tris;
742         int left = index[0], mid = index[i+1], right = index[i+2], start = left, i0 = left, i1 = -1;
743         loopk(4)
744         {
745             int i2 = -1, ctj = -1, cedge = -1;
746             switch(k)
747             {
748             case 1: i1 = i2 = mid; cedge = edge+i+1; break;
749             case 2: if(i1 != mid || i0 == left) { i0 = i1; i1 = right; } i2 = right; if(i+1 == numverts-2) cedge = edge+i+2; break;
750             case 3: if(i0 == start) { i0 = i1; i1 = left; } i2 = left; // fall-through
751             default: if(!i) cedge = edge; break;
752             }
753             if(i1 != i2)
754             {
755                 if(total + 3 > USHRT_MAX) return;
756                 total += 3;
757                 idxs.add(i0);
758                 idxs.add(i1);
759                 idxs.add(i2);
760                 i1 = i2;
761             }
762             if(cedge >= 0)
763             {
764                 for(ctj = tj;;)
765                 {
766                     if(ctj < 0) break;
767                     if(tjoints[ctj].edge < cedge) { ctj = tjoints[ctj].next; continue; }
768                     if(tjoints[ctj].edge != cedge) ctj = -1;
769                     break;
770                 }
771             }
772             if(ctj >= 0)
773             {
774                 int e1 = cedge%(MAXFACEVERTS+1), e2 = (e1+1)%numverts;
775                 vertex &v1 = verts[e1], &v2 = verts[e2];
776                 ivec d(vec(v2.pos).sub(v1.pos).mul(8));
777                 int axis = abs(d.x) > abs(d.y) ? (abs(d.x) > abs(d.z) ? 0 : 2) : (abs(d.y) > abs(d.z) ? 1 : 2);
778                 if(d[axis] < 0) d.neg();
779                 reduceslope(d);
780                 int origin = int(min(v1.pos[axis], v2.pos[axis])*8)&~0x7FFF,
781                     offset1 = (int(v1.pos[axis]*8) - origin) / d[axis],
782                     offset2 = (int(v2.pos[axis]*8) - origin) / d[axis];
783                 vec o = vec(v1.pos).sub(vec(d).mul(offset1/8.0f));
784                 float doffset = 1.0f / (offset2 - offset1);
785 
786                 if(i1 < 0) for(;;)
787                 {
788                     tjoint &t = tjoints[ctj];
789                     if(t.next < 0 || tjoints[t.next].edge != cedge) break;
790                     ctj = t.next;
791                 }
792                 while(ctj >= 0)
793                 {
794                     tjoint &t = tjoints[ctj];
795                     if(t.edge != cedge) break;
796                     float offset = (t.offset - offset1) * doffset;
797                     vertex vt;
798                     vt.pos = vec(d).mul(t.offset/8.0f).add(o);
799                     vt.tc.lerp(v1.tc, v2.tc, offset);
800                     vt.norm.lerp(v1.norm, v2.norm, offset);
801                     vt.tangent.lerp(v1.tangent, v2.tangent, offset);
802                     if(v1.tangent.w != v2.tangent.w)
803                         vt.tangent.w = orientation_bitangent[vslot.rotation][orient].scalartriple(vt.norm.tonormal(), vt.tangent.tonormal()) < 0 ? 0 : 255;
804                     int i2 = vc.addvert(vt);
805                     if(i2 < 0) return;
806                     if(i1 >= 0)
807                     {
808                         if(total + 3 > USHRT_MAX) return;
809                         total += 3;
810                         idxs.add(i0);
811                         idxs.add(i1);
812                         idxs.add(i2);
813                         i1 = i2;
814                     }
815                     else start = i0 = i2;
816                     ctj = t.next;
817                 }
818             }
819         }
820     }
821 }
822 
addgrasstri(int face,vertex * verts,int numv,ushort texture,int layer)823 void addgrasstri(int face, vertex *verts, int numv, ushort texture, int layer)
824 {
825     grasstri &g = vc.grasstris.add();
826     int i1, i2, i3, i4;
827     if(numv <= 3 && face%2) { i1 = face+1; i2 = face+2; i3 = i4 = 0; }
828     else { i1 = 0; i2 = face+1; i3 = face+2; i4 = numv > 3 ? face+3 : i3; }
829     g.v[0] = verts[i1].pos;
830     g.v[1] = verts[i2].pos;
831     g.v[2] = verts[i3].pos;
832     g.v[3] = verts[i4].pos;
833     g.numv = numv;
834 
835     g.surface.toplane(g.v[0], g.v[1], g.v[2]);
836     if(g.surface.z <= 0) { vc.grasstris.pop(); return; }
837 
838     g.minz = min(min(g.v[0].z, g.v[1].z), min(g.v[2].z, g.v[3].z));
839     g.maxz = max(max(g.v[0].z, g.v[1].z), max(g.v[2].z, g.v[3].z));
840 
841     g.center = vec(0, 0, 0);
842     loopk(numv) g.center.add(g.v[k]);
843     g.center.div(numv);
844     g.radius = 0;
845     loopk(numv) g.radius = max(g.radius, g.v[k].dist(g.center));
846 
847     g.texture = texture;
848     g.blend = layer == LAYER_BLEND ? ((int(g.center.x)>>12)+1) | (((int(g.center.y)>>12)+1)<<8) : 0;
849 }
850 
calctexgen(VSlot & vslot,int orient,vec4 & sgen,vec4 & tgen)851 static inline void calctexgen(VSlot &vslot, int orient, vec4 &sgen, vec4 &tgen)
852 {
853     Texture *tex = vslot.slot->sts.empty() ? notexture : vslot.slot->sts[0].t;
854     const texrotation &r = texrotations[vslot.rotation];
855     float k = TEX_SCALE/vslot.scale,
856           xs = r.flipx ? -tex->xs : tex->xs,
857           ys = r.flipy ? -tex->ys : tex->ys,
858           sk = k/xs, tk = k/ys,
859           soff = -(r.swapxy ? vslot.offset.y : vslot.offset.x)/xs,
860           toff = -(r.swapxy ? vslot.offset.x : vslot.offset.y)/ys;
861     sgen = vec4(0, 0, 0, soff);
862     tgen = vec4(0, 0, 0, toff);
863     if(r.swapxy) switch(orient)
864     {
865         case 0: sgen.z = -sk; tgen.y = tk;  break;
866         case 1: sgen.z = -sk; tgen.y = -tk; break;
867         case 2: sgen.z = -sk; tgen.x = -tk; break;
868         case 3: sgen.z = -sk; tgen.x = tk;  break;
869         case 4: sgen.y = -sk; tgen.x = tk;  break;
870         case 5: sgen.y = sk;  tgen.x = tk;  break;
871     }
872     else switch(orient)
873     {
874         case 0: sgen.y = sk;  tgen.z = -tk; break;
875         case 1: sgen.y = -sk; tgen.z = -tk; break;
876         case 2: sgen.x = -sk; tgen.z = -tk; break;
877         case 3: sgen.x = sk;  tgen.z = -tk; break;
878         case 4: sgen.x = sk;  tgen.y = -tk; break;
879         case 5: sgen.x = sk;  tgen.y = tk;  break;
880     }
881 }
882 
encodenormal(const vec & n)883 ushort encodenormal(const vec &n)
884 {
885     if(n.iszero()) return 0;
886     int yaw = int(-atan2(n.x, n.y)/RAD), pitch = int(asin(n.z)/RAD);
887     return ushort(clamp(pitch + 90, 0, 180)*360 + (yaw < 0 ? yaw%360 + 360 : yaw%360) + 1);
888 }
889 
decodenormal(ushort norm)890 vec decodenormal(ushort norm)
891 {
892     if(!norm) return vec(0, 0, 1);
893     norm--;
894     const vec2 &yaw = sincos360[norm%360], &pitch = sincos360[norm/360+270];
895     return vec(-yaw.y*pitch.x, yaw.x*pitch.x, pitch.y);
896 }
897 
guessnormals(const vec * pos,int numverts,vec * normals)898 void guessnormals(const vec *pos, int numverts, vec *normals)
899 {
900     vec n1, n2;
901     n1.cross(pos[0], pos[1], pos[2]);
902     if(numverts != 4)
903     {
904         n1.normalize();
905         loopk(numverts) normals[k] = n1;
906         return;
907     }
908     n2.cross(pos[0], pos[2], pos[3]);
909     if(n1.iszero())
910     {
911         n2.normalize();
912         loopk(4) normals[k] = n2;
913         return;
914     }
915     else n1.normalize();
916     if(n2.iszero())
917     {
918         loopk(4) normals[k] = n1;
919         return;
920     }
921     else n2.normalize();
922     vec avg = vec(n1).add(n2).normalize();
923     normals[0] = avg;
924     normals[1] = n1;
925     normals[2] = avg;
926     normals[3] = n2;
927 }
928 
addcubeverts(VSlot & vslot,int orient,int size,vec * pos,int convex,ushort texture,vertinfo * vinfo,int numverts,int tj=-1,ushort envmap=EMID_NONE,int grassy=0,bool alpha=false,int layer=LAYER_TOP)929 void addcubeverts(VSlot &vslot, int orient, int size, vec *pos, int convex, ushort texture, vertinfo *vinfo, int numverts, int tj = -1, ushort envmap = EMID_NONE, int grassy = 0, bool alpha = false, int layer = LAYER_TOP)
930 {
931     vec4 sgen, tgen;
932     calctexgen(vslot, orient, sgen, tgen);
933     vertex verts[MAXFACEVERTS];
934     int index[MAXFACEVERTS];
935     vec normals[MAXFACEVERTS];
936     loopk(numverts)
937     {
938         vertex &v = verts[k];
939         v.pos = pos[k];
940         v.tc = vec(sgen.dot(v.pos), tgen.dot(v.pos), 0);
941         if(vinfo && vinfo[k].norm)
942         {
943             vec n = decodenormal(vinfo[k].norm), t = orientation_tangent[vslot.rotation][orient];
944             t.project(n).normalize();
945             v.norm = bvec(n);
946             v.tangent = bvec4(bvec(t), orientation_bitangent[vslot.rotation][orient].scalartriple(n, t) < 0 ? 0 : 255);
947         }
948         else if(texture != DEFAULT_SKY)
949         {
950             if(!k) guessnormals(pos, numverts, normals);
951             const vec &n = normals[k];
952             vec t = orientation_tangent[vslot.rotation][orient];
953             t.project(n).normalize();
954             v.norm = bvec(n);
955             v.tangent = bvec4(bvec(t), orientation_bitangent[vslot.rotation][orient].scalartriple(n, t) < 0 ? 0 : 255);
956         }
957         else
958         {
959             v.norm = bvec(128, 128, 255);
960             v.tangent = bvec4(255, 128, 128, 255);
961         }
962         index[k] = vc.addvert(v);
963         if(index[k] < 0) return;
964     }
965 
966     if(alpha)
967     {
968         loopk(numverts) { vc.alphamin.min(pos[k]); vc.alphamax.max(pos[k]); }
969         if(vslot.refractscale > 0) loopk(numverts) { vc.refractmin.min(pos[k]); vc.refractmax.max(pos[k]); }
970     }
971     if(texture == DEFAULT_SKY) loopi(numverts) if(pos[i][orient>>1] != ((orient&1)<<worldscale))
972     {
973         loopk(numverts) { vc.skymin.min(pos[k]); vc.skymax.max(pos[k]); }
974         break;
975     }
976 
977     sortkey key(texture, vslot.scroll.iszero() ? O_ANY : orient, layer&LAYER_BOTTOM ? layer : LAYER_TOP, envmap, alpha ? (vslot.refractscale > 0 ? ALPHA_REFRACT : (vslot.alphaback ? ALPHA_BACK : ALPHA_FRONT)) : NO_ALPHA);
978     addtris(vslot, orient, key, verts, index, numverts, convex, tj);
979 
980     if(grassy)
981     {
982         for(int i = 0; i < numverts-2; i += 2)
983         {
984             int faces = 0;
985             if(index[0]!=index[i+1] && index[i+1]!=index[i+2] && index[i+2]!=index[0]) faces |= 1;
986             if(i+3 < numverts && index[0]!=index[i+2] && index[i+2]!=index[i+3] && index[i+3]!=index[0]) faces |= 2;
987             if(grassy > 1 && faces==3) addgrasstri(i, verts, 4, texture, layer);
988             else
989             {
990                 if(faces&1) addgrasstri(i, verts, 3, texture, layer);
991                 if(faces&2) addgrasstri(i+1, verts, 3, texture, layer);
992             }
993         }
994     }
995 }
996 
997 struct edgegroup
998 {
999     ivec slope, origin;
1000     int axis;
1001 };
1002 
hthash(const edgegroup & g)1003 static inline uint hthash(const edgegroup &g)
1004 {
1005     return g.slope.x^g.slope.y^g.slope.z^g.origin.x^g.origin.y^g.origin.z;
1006 }
1007 
htcmp(const edgegroup & x,const edgegroup & y)1008 static inline bool htcmp(const edgegroup &x, const edgegroup &y)
1009 {
1010     return x.slope==y.slope && x.origin==y.origin;
1011 }
1012 
1013 enum
1014 {
1015     CE_START = 1<<0,
1016     CE_END   = 1<<1,
1017     CE_FLIP  = 1<<2,
1018     CE_DUP   = 1<<3
1019 };
1020 
1021 struct cubeedge
1022 {
1023     cube *c;
1024     int next, offset;
1025     ushort size;
1026     uchar index, flags;
1027 };
1028 
1029 vector<cubeedge> cubeedges;
1030 hashtable<edgegroup, int> edgegroups(1<<13);
1031 
gencubeedges(cube & c,const ivec & co,int size)1032 void gencubeedges(cube &c, const ivec &co, int size)
1033 {
1034     ivec pos[MAXFACEVERTS];
1035     int vis;
1036     loopi(6) if((vis = visibletris(c, i, co, size)))
1037     {
1038         int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0;
1039         if(numverts)
1040         {
1041             vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
1042             ivec vo = ivec(co).mask(~0xFFF).shl(3);
1043             loopj(numverts)
1044             {
1045                 vertinfo &v = verts[j];
1046                 pos[j] = ivec(v.x, v.y, v.z).add(vo);
1047             }
1048         }
1049         else if(c.merged&(1<<i)) continue;
1050         else
1051         {
1052             ivec v[4];
1053             genfaceverts(c, i, v);
1054             int order = vis&4 || (!flataxisface(c, i) && faceconvexity(v) < 0) ? 1 : 0;
1055             ivec vo = ivec(co).shl(3);
1056             pos[numverts++] = v[order].mul(size).add(vo);
1057             if(vis&1) pos[numverts++] = v[order+1].mul(size).add(vo);
1058             pos[numverts++] = v[order+2].mul(size).add(vo);
1059             if(vis&2) pos[numverts++] = v[(order+3)&3].mul(size).add(vo);
1060         }
1061         loopj(numverts)
1062         {
1063             int e1 = j, e2 = j+1 < numverts ? j+1 : 0;
1064             ivec d = pos[e2];
1065             d.sub(pos[e1]);
1066             if(d.iszero()) continue;
1067             int axis = abs(d.x) > abs(d.y) ? (abs(d.x) > abs(d.z) ? 0 : 2) : (abs(d.y) > abs(d.z) ? 1 : 2);
1068             if(d[axis] < 0)
1069             {
1070                 d.neg();
1071                 swap(e1, e2);
1072             }
1073             reduceslope(d);
1074 
1075             int t1 = pos[e1][axis]/d[axis],
1076                 t2 = pos[e2][axis]/d[axis];
1077             edgegroup g;
1078             g.origin = ivec(pos[e1]).sub(ivec(d).mul(t1));
1079             g.slope = d;
1080             g.axis = axis;
1081             cubeedge ce;
1082             ce.c = &c;
1083             ce.offset = t1;
1084             ce.size = t2 - t1;
1085             ce.index = i*(MAXFACEVERTS+1)+j;
1086             ce.flags = CE_START | CE_END | (e1!=j ? CE_FLIP : 0);
1087             ce.next = -1;
1088 
1089             bool insert = true;
1090             int *exists = edgegroups.access(g);
1091             if(exists)
1092             {
1093                 int prev = -1, cur = *exists;
1094                 while(cur >= 0)
1095                 {
1096                     cubeedge &p = cubeedges[cur];
1097                     if(p.flags&CE_DUP ?
1098                         ce.offset>=p.offset && ce.offset+ce.size<=p.offset+p.size :
1099                         ce.offset==p.offset && ce.size==p.size)
1100                     {
1101                         p.flags |= CE_DUP;
1102                         insert = false;
1103                         break;
1104                     }
1105                     else if(ce.offset >= p.offset)
1106                     {
1107                         if(ce.offset == p.offset+p.size) ce.flags &= ~CE_START;
1108                         prev = cur;
1109                         cur = p.next;
1110                     }
1111                     else break;
1112                 }
1113                 if(insert)
1114                 {
1115                     ce.next = cur;
1116                     while(cur >= 0)
1117                     {
1118                         cubeedge &p = cubeedges[cur];
1119                         if(ce.offset+ce.size==p.offset) { ce.flags &= ~CE_END; break; }
1120                         cur = p.next;
1121                     }
1122                     if(prev>=0) cubeedges[prev].next = cubeedges.length();
1123                     else *exists = cubeedges.length();
1124                 }
1125             }
1126             else edgegroups[g] = cubeedges.length();
1127 
1128             if(insert) cubeedges.add(ce);
1129         }
1130     }
1131 }
1132 
gencubeedges(cube * c=worldroot,const ivec & co=ivec (0,0,0),int size=worldsize>>1)1133 void gencubeedges(cube *c = worldroot, const ivec &co = ivec(0, 0, 0), int size = worldsize>>1)
1134 {
1135     rcprogress("Fixing t-joints...");
1136     neighbourstack[++neighbourdepth] = c;
1137     loopi(8)
1138     {
1139         ivec o(i, co, size);
1140         if(c[i].ext) c[i].ext->tjoints = -1;
1141         if(c[i].children) gencubeedges(c[i].children, o, size>>1);
1142         else if(!isempty(c[i])) gencubeedges(c[i], o, size);
1143     }
1144     --neighbourdepth;
1145 }
1146 
gencubeverts(cube & c,const ivec & co,int size,int csi)1147 void gencubeverts(cube &c, const ivec &co, int size, int csi)
1148 {
1149     if(!(c.visible&0xC0)) return;
1150 
1151     int vismask = ~c.merged & 0x3F;
1152     if(!(c.visible&0x80)) vismask &= c.visible;
1153     if(!vismask) return;
1154 
1155     int tj = filltjoints && c.ext ? c.ext->tjoints : -1, vis = 0;
1156     loopi(6) if(vismask&(1<<i) && (vis = visibletris(c, i, co, size)))
1157     {
1158         vec pos[MAXFACEVERTS];
1159         vertinfo *verts = NULL;
1160         int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0, convex = 0;
1161         if(numverts)
1162         {
1163             verts = c.ext->verts() + c.ext->surfaces[i].verts;
1164             vec vo(ivec(co).mask(~0xFFF));
1165             loopj(numverts) pos[j] = vec(verts[j].getxyz()).mul(1.0f/8).add(vo);
1166             if(!flataxisface(c, i)) convex = faceconvexity(verts, numverts, size);
1167         }
1168         else
1169         {
1170             ivec v[4];
1171             genfaceverts(c, i, v);
1172             if(!flataxisface(c, i)) convex = faceconvexity(v);
1173             int order = vis&4 || convex < 0 ? 1 : 0;
1174             vec vo(co);
1175             pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
1176             if(vis&1) pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
1177             pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
1178             if(vis&2) pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
1179         }
1180 
1181         VSlot &vslot = lookupvslot(c.texture[i], true),
1182               *layer = vslot.layer && !(c.material&MAT_ALPHA) ? &lookupvslot(vslot.layer, true) : NULL;
1183         ushort envmap = vslot.slot->shader && vslot.slot->shader->type&SHADER_ENVMAP ? (vslot.slot->texmask&(1<<TEX_ENVMAP) ? EMID_CUSTOM : closestenvmap(i, co, size)) : EMID_NONE,
1184                envmap2 = layer && layer->slot->shader && layer->slot->shader->type&SHADER_ENVMAP ? (layer->slot->texmask&(1<<TEX_ENVMAP) ? EMID_CUSTOM : closestenvmap(i, co, size)) : EMID_NONE;
1185         while(tj >= 0 && tjoints[tj].edge < i*(MAXFACEVERTS+1)) tj = tjoints[tj].next;
1186         int hastj = tj >= 0 && tjoints[tj].edge < (i+1)*(MAXFACEVERTS+1) ? tj : -1;
1187         int grassy = vslot.slot->grass && i!=O_BOTTOM ? (vis!=3 || convex ? 1 : 2) : 0;
1188         if(!c.ext)
1189             addcubeverts(vslot, i, size, pos, convex, c.texture[i], NULL, numverts, hastj, envmap, grassy, (c.material&MAT_ALPHA)!=0);
1190         else
1191         {
1192             const surfaceinfo &surf = c.ext->surfaces[i];
1193             if(!surf.numverts || surf.numverts&LAYER_TOP)
1194                 addcubeverts(vslot, i, size, pos, convex, c.texture[i], verts, numverts, hastj, envmap, grassy, (c.material&MAT_ALPHA)!=0, surf.numverts&LAYER_BLEND);
1195             if(surf.numverts&LAYER_BOTTOM)
1196                 addcubeverts(layer ? *layer : vslot, i, size, pos, convex, vslot.layer, verts, numverts, hastj, envmap2, 0, false, surf.numverts&LAYER_TOP ? LAYER_BOTTOM : LAYER_TOP);
1197         }
1198     }
1199 }
1200 
1201 ////////// Vertex Arrays //////////////
1202 
1203 int allocva = 0;
1204 int wtris = 0, wverts = 0, vtris = 0, vverts = 0, glde = 0, gbatches = 0;
1205 vector<vtxarray *> valist, varoot;
1206 
newva(const ivec & o,int size)1207 vtxarray *newva(const ivec &o, int size)
1208 {
1209     vtxarray *va = new vtxarray;
1210     va->parent = NULL;
1211     va->o = o;
1212     va->size = size;
1213     va->curvfc = VFC_NOT_VISIBLE;
1214     va->occluded = OCCLUDE_NOTHING;
1215     va->query = NULL;
1216     va->bbmin = va->alphamin = va->refractmin = va->skymin = ivec(-1, -1, -1);
1217     va->bbmax = va->alphamax = va->refractmax = va->skymax = ivec(-1, -1, -1);
1218     va->hasmerges = 0;
1219     va->mergelevel = -1;
1220 
1221     vc.setupdata(va);
1222 
1223     if(va->alphafronttris || va->alphabacktris || va->refracttris)
1224     {
1225         va->alphamin = ivec(vec(vc.alphamin).mul(8)).shr(3);
1226         va->alphamax = ivec(vec(vc.alphamax).mul(8)).add(7).shr(3);
1227     }
1228 
1229     if(va->refracttris)
1230     {
1231         va->refractmin = ivec(vec(vc.refractmin).mul(8)).shr(3);
1232         va->refractmax = ivec(vec(vc.refractmax).mul(8)).add(7).shr(3);
1233     }
1234 
1235     if(va->sky && vc.skymax.x >= 0)
1236     {
1237         va->skymin = ivec(vec(vc.skymin).mul(8)).shr(3);
1238         va->skymax = ivec(vec(vc.skymax).mul(8)).add(7).shr(3);
1239     }
1240 
1241     va->nogimin = vc.nogimin;
1242     va->nogimax = vc.nogimax;
1243 
1244     wverts += va->verts;
1245     wtris  += va->tris + va->blends + va->alphabacktris + va->alphafronttris + va->refracttris + va->decaltris;
1246     allocva++;
1247     valist.add(va);
1248 
1249     return va;
1250 }
1251 
destroyva(vtxarray * va,bool reparent)1252 void destroyva(vtxarray *va, bool reparent)
1253 {
1254     wverts -= va->verts;
1255     wtris -= va->tris + va->blends + va->alphabacktris + va->alphafronttris + va->refracttris + va->decaltris;
1256     allocva--;
1257     valist.removeobj(va);
1258     if(!va->parent) varoot.removeobj(va);
1259     if(reparent)
1260     {
1261         if(va->parent) va->parent->children.removeobj(va);
1262         loopv(va->children)
1263         {
1264             vtxarray *child = va->children[i];
1265             child->parent = va->parent;
1266             if(child->parent) child->parent->children.add(child);
1267         }
1268     }
1269     if(va->vbuf) destroyvbo(va->vbuf);
1270     if(va->ebuf) destroyvbo(va->ebuf);
1271     if(va->skybuf) destroyvbo(va->skybuf);
1272     if(va->decalbuf) destroyvbo(va->decalbuf);
1273     if(va->texelems) delete[] va->texelems;
1274     if(va->decalelems) delete[] va->decalelems;
1275     if(va->matbuf) delete[] va->matbuf;
1276     delete va;
1277 }
1278 
clearvas(cube * c)1279 void clearvas(cube *c)
1280 {
1281     loopi(8)
1282     {
1283         if(c[i].ext)
1284         {
1285             if(c[i].ext->va) destroyva(c[i].ext->va, false);
1286             c[i].ext->va = NULL;
1287             c[i].ext->tjoints = -1;
1288         }
1289         if(c[i].children) clearvas(c[i].children);
1290     }
1291 }
1292 
1293 ivec worldmin(0, 0, 0), worldmax(0, 0, 0), nogimin(0, 0, 0), nogimax(0, 0, 0);
1294 
updatevabb(vtxarray * va,bool force)1295 void updatevabb(vtxarray *va, bool force)
1296 {
1297     if(!force && va->bbmin.x >= 0) return;
1298 
1299     va->bbmin = va->geommin;
1300     va->bbmax = va->geommax;
1301     va->bbmin.min(va->lavamin);
1302     va->bbmax.max(va->lavamax);
1303     va->bbmin.min(va->watermin);
1304     va->bbmax.max(va->watermax);
1305     va->bbmin.min(va->glassmin);
1306     va->bbmax.max(va->glassmax);
1307     loopv(va->children)
1308     {
1309         vtxarray *child = va->children[i];
1310         updatevabb(child, force);
1311         va->bbmin.min(child->bbmin);
1312         va->bbmax.max(child->bbmax);
1313     }
1314     loopv(va->mapmodels)
1315     {
1316         octaentities *oe = va->mapmodels[i];
1317         va->bbmin.min(oe->bbmin);
1318         va->bbmax.max(oe->bbmax);
1319     }
1320     loopv(va->decals)
1321     {
1322         octaentities *oe = va->decals[i];
1323         va->bbmin.min(oe->bbmin);
1324         va->bbmax.max(oe->bbmax);
1325     }
1326     va->bbmin.max(va->o);
1327     va->bbmax.min(ivec(va->o).add(va->size));
1328     worldmin.min(va->bbmin);
1329     worldmax.max(va->bbmax);
1330     nogimin.min(va->nogimin);
1331     nogimax.max(va->nogimax);
1332 }
1333 
updatevabbs(bool force)1334 void updatevabbs(bool force)
1335 {
1336     if(force)
1337     {
1338         worldmin = nogimin = ivec(worldsize, worldsize, worldsize);
1339         worldmax = nogimax = ivec(0, 0, 0);
1340         loopv(varoot) updatevabb(varoot[i], true);
1341         if(worldmin.x >= worldmax.x)
1342         {
1343             worldmin = ivec(0, 0, 0);
1344             worldmax = ivec(worldsize, worldsize, worldsize);
1345         }
1346     }
1347     else loopv(varoot) updatevabb(varoot[i]);
1348 }
1349 
1350 struct mergedface
1351 {
1352     uchar orient, numverts;
1353     ushort mat, tex, envmap;
1354     vertinfo *verts;
1355     int tjoints;
1356 };
1357 
1358 #define MAXMERGELEVEL 12
1359 static int vahasmerges = 0, vamergemax = 0;
1360 static vector<mergedface> vamerges[MAXMERGELEVEL+1];
1361 
genmergedfaces(cube & c,const ivec & co,int size,int minlevel=-1)1362 int genmergedfaces(cube &c, const ivec &co, int size, int minlevel = -1)
1363 {
1364     if(!c.ext || isempty(c)) return -1;
1365     int tj = c.ext->tjoints, maxlevel = -1;
1366     loopi(6) if(c.merged&(1<<i))
1367     {
1368         surfaceinfo &surf = c.ext->surfaces[i];
1369         int numverts = surf.numverts&MAXFACEVERTS;
1370         if(!numverts)
1371         {
1372             if(minlevel < 0) vahasmerges |= MERGE_PART;
1373             continue;
1374         }
1375         mergedface mf;
1376         mf.orient = i;
1377         mf.mat = c.material;
1378         mf.tex = c.texture[i];
1379         mf.envmap = EMID_NONE;
1380         mf.numverts = surf.numverts;
1381         mf.verts = c.ext->verts() + surf.verts;
1382         mf.tjoints = -1;
1383         int level = calcmergedsize(i, co, size, mf.verts, mf.numverts&MAXFACEVERTS);
1384         if(level > minlevel)
1385         {
1386             maxlevel = max(maxlevel, level);
1387 
1388             while(tj >= 0 && tjoints[tj].edge < i*(MAXFACEVERTS+1)) tj = tjoints[tj].next;
1389             if(tj >= 0 && tjoints[tj].edge < (i+1)*(MAXFACEVERTS+1)) mf.tjoints = tj;
1390 
1391             VSlot &vslot = lookupvslot(mf.tex, true),
1392                   *layer = vslot.layer && !(c.material&MAT_ALPHA) ? &lookupvslot(vslot.layer, true) : NULL;
1393             if(vslot.slot->shader && vslot.slot->shader->type&SHADER_ENVMAP)
1394                 mf.envmap = vslot.slot->texmask&(1<<TEX_ENVMAP) ? EMID_CUSTOM : closestenvmap(i, co, size);
1395             ushort envmap2 = layer && layer->slot->shader && layer->slot->shader->type&SHADER_ENVMAP ? (layer->slot->texmask&(1<<TEX_ENVMAP) ? EMID_CUSTOM : closestenvmap(i, co, size)) : EMID_NONE;
1396 
1397             if(surf.numverts&LAYER_TOP) vamerges[level].add(mf);
1398             if(surf.numverts&LAYER_BOTTOM)
1399             {
1400                 mf.tex = vslot.layer;
1401                 mf.envmap = envmap2;
1402                 mf.numverts &= ~LAYER_BLEND;
1403                 mf.numverts |= surf.numverts&LAYER_TOP ? LAYER_BOTTOM : LAYER_TOP;
1404                 vamerges[level].add(mf);
1405             }
1406         }
1407     }
1408     if(maxlevel >= 0)
1409     {
1410         vamergemax = max(vamergemax, maxlevel);
1411         vahasmerges |= MERGE_ORIGIN;
1412     }
1413     return maxlevel;
1414 }
1415 
findmergedfaces(cube & c,const ivec & co,int size,int csi,int minlevel)1416 int findmergedfaces(cube &c, const ivec &co, int size, int csi, int minlevel)
1417 {
1418     if(c.ext && c.ext->va && !(c.ext->va->hasmerges&MERGE_ORIGIN)) return c.ext->va->mergelevel;
1419     else if(c.children)
1420     {
1421         int maxlevel = -1;
1422         loopi(8)
1423         {
1424             ivec o(i, co, size/2);
1425             int level = findmergedfaces(c.children[i], o, size/2, csi-1, minlevel);
1426             maxlevel = max(maxlevel, level);
1427         }
1428         return maxlevel;
1429     }
1430     else if(c.ext && c.merged) return genmergedfaces(c, co, size, minlevel);
1431     else return -1;
1432 }
1433 
addmergedverts(int level,const ivec & o)1434 void addmergedverts(int level, const ivec &o)
1435 {
1436     vector<mergedface> &mfl = vamerges[level];
1437     if(mfl.empty()) return;
1438     vec vo(ivec(o).mask(~0xFFF));
1439     vec pos[MAXFACEVERTS];
1440     loopv(mfl)
1441     {
1442         mergedface &mf = mfl[i];
1443         int numverts = mf.numverts&MAXFACEVERTS;
1444         loopi(numverts)
1445         {
1446             vertinfo &v = mf.verts[i];
1447             pos[i] = vec(v.x, v.y, v.z).mul(1.0f/8).add(vo);
1448         }
1449         VSlot &vslot = lookupvslot(mf.tex, true);
1450         int grassy = vslot.slot->grass && mf.orient!=O_BOTTOM && mf.numverts&LAYER_TOP ? 2 : 0;
1451         addcubeverts(vslot, mf.orient, 1<<level, pos, 0, mf.tex, mf.verts, numverts, mf.tjoints, mf.envmap, grassy, (mf.mat&MAT_ALPHA)!=0, mf.numverts&LAYER_BLEND);
1452         vahasmerges |= MERGE_USE;
1453     }
1454     mfl.setsize(0);
1455 }
1456 
finddecals(vtxarray * va)1457 static inline void finddecals(vtxarray *va)
1458 {
1459     if(va->hasmerges&(MERGE_ORIGIN|MERGE_PART))
1460     {
1461         loopv(va->decals) vc.extdecals.add(va->decals[i]);
1462         loopv(va->children) finddecals(va->children[i]);
1463     }
1464 }
1465 
rendercube(cube & c,const ivec & co,int size,int csi,int & maxlevel)1466 void rendercube(cube &c, const ivec &co, int size, int csi, int &maxlevel) // creates vertices and indices ready to be put into a va
1467 {
1468     //if(size<=16) return;
1469     if(c.ext && c.ext->va)
1470     {
1471         maxlevel = max(maxlevel, c.ext->va->mergelevel);
1472         finddecals(c.ext->va);
1473         return; // don't re-render
1474     }
1475 
1476     if(c.children)
1477     {
1478         neighbourstack[++neighbourdepth] = c.children;
1479         c.escaped = 0;
1480         loopi(8)
1481         {
1482             ivec o(i, co, size/2);
1483             int level = -1;
1484             rendercube(c.children[i], o, size/2, csi-1, level);
1485             if(level >= csi)
1486                 c.escaped |= 1<<i;
1487             maxlevel = max(maxlevel, level);
1488         }
1489         --neighbourdepth;
1490 
1491         if(csi <= MAXMERGELEVEL && vamerges[csi].length()) addmergedverts(csi, co);
1492 
1493         if(c.ext && c.ext->ents)
1494         {
1495             if(c.ext->ents->mapmodels.length()) vc.mapmodels.add(c.ext->ents);
1496             if(c.ext->ents->decals.length()) vc.decals.add(c.ext->ents);
1497         }
1498         return;
1499     }
1500 
1501     if(!isempty(c))
1502     {
1503         gencubeverts(c, co, size, csi);
1504         if(c.merged) maxlevel = max(maxlevel, genmergedfaces(c, co, size));
1505     }
1506     if(c.material != MAT_AIR)
1507     {
1508         genmatsurfs(c, co, size, vc.matsurfs);
1509         if(c.material&MAT_NOGI)
1510         {
1511             vc.nogimin.min(co);
1512             vc.nogimax.max(ivec(co).add(size));
1513         }
1514     }
1515 
1516     if(c.ext && c.ext->ents)
1517     {
1518         if(c.ext->ents->mapmodels.length()) vc.mapmodels.add(c.ext->ents);
1519         if(c.ext->ents->decals.length()) vc.decals.add(c.ext->ents);
1520     }
1521 
1522     if(csi <= MAXMERGELEVEL && vamerges[csi].length()) addmergedverts(csi, co);
1523 }
1524 
calcgeombb(const ivec & co,int size,ivec & bbmin,ivec & bbmax)1525 void calcgeombb(const ivec &co, int size, ivec &bbmin, ivec &bbmax)
1526 {
1527     vec vmin(co), vmax = vmin;
1528     vmin.add(size);
1529 
1530     loopv(vc.verts)
1531     {
1532         const vec &v = vc.verts[i].pos;
1533         vmin.min(v);
1534         vmax.max(v);
1535     }
1536 
1537     bbmin = ivec(vmin.mul(8)).shr(3);
1538     bbmax = ivec(vmax.mul(8)).add(7).shr(3);
1539 }
1540 
1541 static int entdepth = -1;
1542 static octaentities *entstack[32];
1543 
setva(cube & c,const ivec & co,int size,int csi)1544 void setva(cube &c, const ivec &co, int size, int csi)
1545 {
1546     ASSERT(size <= 0x1000);
1547 
1548     int vamergeoffset[MAXMERGELEVEL+1];
1549     loopi(MAXMERGELEVEL+1) vamergeoffset[i] = vamerges[i].length();
1550 
1551     vc.origin = co;
1552     vc.size = size;
1553 
1554     loopi(entdepth+1)
1555     {
1556         octaentities *oe = entstack[i];
1557         if(oe->decals.length()) vc.extdecals.add(oe);
1558     }
1559 
1560     int maxlevel = -1;
1561     rendercube(c, co, size, csi, maxlevel);
1562 
1563     if(size == min(0x1000, worldsize/2) || !vc.emptyva())
1564     {
1565         vtxarray *va = newva(co, size);
1566         ext(c).va = va;
1567         calcgeombb(co, size, va->geommin, va->geommax);
1568         calcmatbb(va, co, size, vc.matsurfs);
1569         va->hasmerges = vahasmerges;
1570         va->mergelevel = vamergemax;
1571     }
1572     else
1573     {
1574         loopi(MAXMERGELEVEL+1) vamerges[i].setsize(vamergeoffset[i]);
1575     }
1576 
1577     vc.clear();
1578 }
1579 
setcubevisibility(cube & c,const ivec & co,int size)1580 static inline int setcubevisibility(cube &c, const ivec &co, int size)
1581 {
1582     if(isempty(c) && (c.material&MATF_CLIP) != MAT_CLIP) return 0;
1583     int numvis = 0, vismask = 0, collidemask = 0, checkmask = 0;
1584     loopi(6)
1585     {
1586         int facemask = classifyface(c, i, co, size);
1587         if(facemask&1)
1588         {
1589             vismask |= 1<<i;
1590             if(c.merged&(1<<i))
1591             {
1592                 if(c.ext && c.ext->surfaces[i].numverts&MAXFACEVERTS) numvis++;
1593             }
1594             else
1595             {
1596                 numvis++;
1597                 if(c.texture[i] != DEFAULT_SKY && !(c.ext && c.ext->surfaces[i].numverts&MAXFACEVERTS)) checkmask |= 1<<i;
1598             }
1599         }
1600         if(facemask&2) collidemask |= 1<<i;
1601     }
1602     c.visible = collidemask | (vismask ? (vismask != collidemask ? (checkmask ? 0x80|0x40 : 0x80) : 0x40) : 0);
1603     return numvis;
1604 }
1605 
1606 VARF(0, vafacemax, 64, 384, 256*256, allchanged());
1607 VARF(0, vafacemin, 0, 96, 256*256, allchanged());
1608 VARF(0, vacubesize, 32, 128, 0x1000, allchanged());
1609 
updateva(cube * c,const ivec & co,int size,int csi)1610 int updateva(cube *c, const ivec &co, int size, int csi)
1611 {
1612     rcprogress("Recalculating geometry...");
1613     int ccount = 0, cmergemax = vamergemax, chasmerges = vahasmerges;
1614     neighbourstack[++neighbourdepth] = c;
1615     loopi(8)                                    // counting number of semi-solid/solid children cubes
1616     {
1617         int count = 0, childpos = varoot.length();
1618         ivec o(i, co, size);
1619         vamergemax = 0;
1620         vahasmerges = 0;
1621         if(c[i].ext && c[i].ext->va)
1622         {
1623             varoot.add(c[i].ext->va);
1624             if(c[i].ext->va->hasmerges&MERGE_ORIGIN) findmergedfaces(c[i], o, size, csi, csi);
1625         }
1626         else
1627         {
1628             if(c[i].children)
1629             {
1630                 if(c[i].ext && c[i].ext->ents) entstack[++entdepth] = c[i].ext->ents;
1631                 count += updateva(c[i].children, o, size/2, csi-1);
1632                 if(c[i].ext && c[i].ext->ents) --entdepth;
1633             }
1634             else count += setcubevisibility(c[i], o, size);
1635             int tcount = count + (csi <= MAXMERGELEVEL ? vamerges[csi].length() : 0);
1636             if(tcount > vafacemax || (tcount >= vafacemin && size >= vacubesize) || size == min(0x1000, worldsize/2))
1637             {
1638                 loadprogress = clamp(recalcprogress/float(allocnodes), 0.0f, 1.0f);
1639                 setva(c[i], o, size, csi);
1640                 if(c[i].ext && c[i].ext->va)
1641                 {
1642                     while(varoot.length() > childpos)
1643                     {
1644                         vtxarray *child = varoot.pop();
1645                         c[i].ext->va->children.add(child);
1646                         child->parent = c[i].ext->va;
1647                     }
1648                     varoot.add(c[i].ext->va);
1649                     if(vamergemax > size)
1650                     {
1651                         cmergemax = max(cmergemax, vamergemax);
1652                         chasmerges |= vahasmerges&~MERGE_USE;
1653                     }
1654                     continue;
1655                 }
1656                 else count = 0;
1657             }
1658         }
1659         if(csi+1 <= MAXMERGELEVEL && vamerges[csi].length()) vamerges[csi+1].move(vamerges[csi]);
1660         cmergemax = max(cmergemax, vamergemax);
1661         chasmerges |= vahasmerges;
1662         ccount += count;
1663     }
1664     --neighbourdepth;
1665     vamergemax = cmergemax;
1666     vahasmerges = chasmerges;
1667 
1668     return ccount;
1669 }
1670 
addtjoint(const edgegroup & g,const cubeedge & e,int offset)1671 void addtjoint(const edgegroup &g, const cubeedge &e, int offset)
1672 {
1673     int vcoord = (g.slope[g.axis]*offset + g.origin[g.axis]) & 0x7FFF;
1674     tjoint &tj = tjoints.add();
1675     tj.offset = vcoord / g.slope[g.axis];
1676     tj.edge = e.index;
1677 
1678     int prev = -1, cur = ext(*e.c).tjoints;
1679     while(cur >= 0)
1680     {
1681         tjoint &o = tjoints[cur];
1682         if(tj.edge < o.edge || (tj.edge==o.edge && (e.flags&CE_FLIP ? tj.offset > o.offset : tj.offset < o.offset))) break;
1683         prev = cur;
1684         cur = o.next;
1685     }
1686 
1687     tj.next = cur;
1688     if(prev < 0) e.c->ext->tjoints = tjoints.length()-1;
1689     else tjoints[prev].next = tjoints.length()-1;
1690 }
1691 
findtjoints(int cur,const edgegroup & g)1692 void findtjoints(int cur, const edgegroup &g)
1693 {
1694     int active = -1;
1695     while(cur >= 0)
1696     {
1697         cubeedge &e = cubeedges[cur];
1698         int prevactive = -1, curactive = active;
1699         while(curactive >= 0)
1700         {
1701             cubeedge &a = cubeedges[curactive];
1702             if(a.offset+a.size <= e.offset)
1703             {
1704                 if(prevactive >= 0) cubeedges[prevactive].next = a.next;
1705                 else active = a.next;
1706             }
1707             else
1708             {
1709                 prevactive = curactive;
1710                 if(!(a.flags&CE_DUP))
1711                 {
1712                     if(e.flags&CE_START && e.offset > a.offset && e.offset < a.offset+a.size)
1713                         addtjoint(g, a, e.offset);
1714                     if(e.flags&CE_END && e.offset+e.size > a.offset && e.offset+e.size < a.offset+a.size)
1715                         addtjoint(g, a, e.offset+e.size);
1716                 }
1717                 if(!(e.flags&CE_DUP))
1718                 {
1719                     if(a.flags&CE_START && a.offset > e.offset && a.offset < e.offset+e.size)
1720                         addtjoint(g, e, a.offset);
1721                     if(a.flags&CE_END && a.offset+a.size > e.offset && a.offset+a.size < e.offset+e.size)
1722                         addtjoint(g, e, a.offset+a.size);
1723                 }
1724             }
1725             curactive = a.next;
1726         }
1727         int next = e.next;
1728         e.next = active;
1729         active = cur;
1730         cur = next;
1731     }
1732 }
1733 
findtjoints()1734 void findtjoints()
1735 {
1736     recalcprogress = 0;
1737     gencubeedges();
1738     tjoints.setsize(0);
1739     enumeratekt(edgegroups, edgegroup, g, int, e, findtjoints(e, g));
1740     cubeedges.setsize(0);
1741     edgegroups.clear();
1742 }
1743 
octarender()1744 void octarender()                               // creates va s for all leaf cubes that don't already have them
1745 {
1746     int csi = 0;
1747     while(1<<csi < worldsize) csi++;
1748 
1749     recalcprogress = 0;
1750     varoot.setsize(0);
1751     updateva(worldroot, ivec(0, 0, 0), worldsize/2, csi-1);
1752     loadprogress = 0;
1753     flushvbo();
1754 
1755     explicitsky = 0;
1756     loopv(valist)
1757     {
1758         vtxarray *va = valist[i];
1759         explicitsky += va->sky;
1760     }
1761 
1762     visibleva = NULL;
1763 }
1764 
precachetextures()1765 void precachetextures()
1766 {
1767     vector<int> texs;
1768     loopv(valist)
1769     {
1770         vtxarray *va = valist[i];
1771         loopj(va->texs + va->blends)
1772         {
1773             int tex = va->texelems[j].texture;
1774             if(texs.find(tex) < 0)
1775             {
1776                 texs.add(tex);
1777 
1778                 VSlot &vslot = lookupvslot(tex, false);
1779                 if(vslot.layer && texs.find(vslot.layer) < 0) texs.add(vslot.layer);
1780                 if(vslot.detail && texs.find(vslot.detail) < 0) texs.add(vslot.detail);
1781             }
1782         }
1783     }
1784     loopv(texs)
1785     {
1786         loadprogress = float(i+1)/texs.length();
1787         lookupvslot(texs[i]);
1788     }
1789     loadprogress = 0;
1790 }
1791 
allchanged(bool load)1792 void allchanged(bool load)
1793 {
1794     if(!connected()) load = false;
1795     if(load) initlights();
1796     progress(-1, "Clearing vertex arrays...");
1797     clearvas(worldroot);
1798     resetqueries();
1799     resetclipplanes();
1800     if(load) initenvtexs();
1801     entitiesinoctanodes();
1802     tjoints.setsize(0);
1803     if(filltjoints) findtjoints();
1804     octarender();
1805     if(load) precachetextures();
1806     setupmaterials();
1807     clearshadowcache();
1808     updatevabbs(true);
1809     if(load)
1810     {
1811         genshadowmeshes();
1812         updateblendtextures();
1813         seedparticles();
1814         genenvtexs();
1815         drawminimap();
1816     }
1817 }
1818 
recalc()1819 void recalc()
1820 {
1821     allchanged(true);
1822 }
1823 
1824 COMMAND(0, recalc, "");
1825 
1826