1 // renderva.cpp: handles the occlusion and rendering of vertex arrays
2 
3 #include "engine.h"
4 
drawtris(GLsizei numindices,const GLvoid * indices,ushort minvert,ushort maxvert)5 static inline void drawtris(GLsizei numindices, const GLvoid *indices, ushort minvert, ushort maxvert)
6 {
7     glDrawRangeElements_(GL_TRIANGLES, minvert, maxvert, numindices, GL_UNSIGNED_SHORT, indices);
8     glde++;
9 }
10 
drawvatris(vtxarray * va,GLsizei numindices,const GLvoid * indices)11 static inline void drawvatris(vtxarray *va, GLsizei numindices, const GLvoid *indices)
12 {
13     drawtris(numindices, indices, va->minvert, va->maxvert);
14 }
15 
16 ///////// view frustrum culling ///////////////////////
17 
18 plane vfcP[5];  // perpindictular vectors to view frustrum bounding planes
19 float vfcDfog;  // far plane culling distance (fog limit).
20 float vfcDnear[5], vfcDfar[5];
21 
22 vtxarray *visibleva;
23 
isfoggedsphere(float rad,const vec & cv)24 bool isfoggedsphere(float rad, const vec &cv)
25 {
26     loopi(4) if(vfcP[i].dist(cv) < -rad) return true;
27     float dist = vfcP[4].dist(cv);
28     return dist < -rad || dist > vfcDfog + rad;
29 }
30 
isvisiblesphere(float rad,const vec & cv)31 int isvisiblesphere(float rad, const vec &cv)
32 {
33     int v = VFC_FULL_VISIBLE;
34     float dist;
35 
36     loopi(5)
37     {
38         dist = vfcP[i].dist(cv);
39         if(dist < -rad) return VFC_NOT_VISIBLE;
40         if(dist < rad) v = VFC_PART_VISIBLE;
41     }
42 
43     dist -= vfcDfog;
44     if(dist > rad) return VFC_FOGGED;  //VFC_NOT_VISIBLE;    // culling when fog is closer than size of world results in HOM
45     if(dist > -rad) v = VFC_PART_VISIBLE;
46 
47     return v;
48 }
49 
ishiddencube(const ivec & o,int size)50 static inline int ishiddencube(const ivec &o, int size)
51 {
52     loopi(5) if(o.dist(vfcP[i]) < -vfcDfar[i]*size) return true;
53     return false;
54 }
55 
isfoggedcube(const ivec & o,int size)56 static inline int isfoggedcube(const ivec &o, int size)
57 {
58     loopi(4) if(o.dist(vfcP[i]) < -vfcDfar[i]*size) return true;
59     float dist = o.dist(vfcP[4]);
60     return dist < -vfcDfar[4]*size || dist > vfcDfog - vfcDnear[4]*size;
61 }
62 
isvisiblecube(const ivec & o,int size)63 int isvisiblecube(const ivec &o, int size)
64 {
65     int v = VFC_FULL_VISIBLE;
66     float dist;
67 
68     loopi(5)
69     {
70         dist = o.dist(vfcP[i]);
71         if(dist < -vfcDfar[i]*size) return VFC_NOT_VISIBLE;
72         if(dist < -vfcDnear[i]*size) v = VFC_PART_VISIBLE;
73     }
74 
75     dist -= vfcDfog;
76     if(dist > -vfcDnear[4]*size) return VFC_FOGGED;
77     if(dist > -vfcDfar[4]*size) v = VFC_PART_VISIBLE;
78 
79     return v;
80 }
81 
vadist(vtxarray * va,const vec & p)82 float vadist(vtxarray *va, const vec &p)
83 {
84     return p.dist_to_bb(va->bbmin, va->bbmax);
85 }
86 
87 #define VASORTSIZE 64
88 
89 static vtxarray *vasort[VASORTSIZE];
90 
addvisibleva(vtxarray * va)91 void addvisibleva(vtxarray *va)
92 {
93     float dist = vadist(va, camera1->o);
94     va->distance = int(dist); /*cv.dist(camera1->o) - va->size*SQRT3/2*/
95 
96     int hash = clamp(int(dist*VASORTSIZE/worldsize), 0, VASORTSIZE-1);
97     vtxarray **prev = &vasort[hash], *cur = vasort[hash];
98 
99     while(cur && va->distance >= cur->distance)
100     {
101         prev = &cur->next;
102         cur = cur->next;
103     }
104 
105     va->next = *prev;
106     *prev = va;
107 }
108 
sortvisiblevas()109 void sortvisiblevas()
110 {
111     visibleva = NULL;
112     vtxarray **last = &visibleva;
113     loopi(VASORTSIZE) if(vasort[i])
114     {
115         vtxarray *va = vasort[i];
116         *last = va;
117         while(va->next) va = va->next;
118         last = &va->next;
119     }
120 }
121 
findvisiblevas(vector<vtxarray * > & vas,bool resetocclude=false)122 void findvisiblevas(vector<vtxarray *> &vas, bool resetocclude = false)
123 {
124     loopv(vas)
125     {
126         vtxarray &v = *vas[i];
127         int prevvfc = resetocclude ? VFC_NOT_VISIBLE : v.curvfc;
128         v.curvfc = isvisiblecube(v.o, v.size);
129         if(v.curvfc!=VFC_NOT_VISIBLE)
130         {
131             if(pvsoccluded(v.o, v.size))
132             {
133                 v.curvfc += PVS_FULL_VISIBLE - VFC_FULL_VISIBLE;
134                 continue;
135             }
136             addvisibleva(&v);
137             if(v.children.length()) findvisiblevas(v.children, prevvfc>=VFC_NOT_VISIBLE);
138             if(prevvfc>=VFC_NOT_VISIBLE)
139             {
140                 v.occluded = !v.texs ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
141                 v.query = NULL;
142             }
143         }
144     }
145 }
146 
calcvfcD()147 void calcvfcD()
148 {
149     loopi(5)
150     {
151         plane &p = vfcP[i];
152         vfcDnear[i] = vfcDfar[i] = 0;
153         loopk(3) if(p[k] > 0) vfcDfar[i] += p[k];
154         else vfcDnear[i] += p[k];
155     }
156 }
157 
setvfcP(float z,const vec & bbmin,const vec & bbmax)158 void setvfcP(float z, const vec &bbmin, const vec &bbmax)
159 {
160     vec4 px = camprojmatrix.rowx(), py = camprojmatrix.rowy(), pz = camprojmatrix.rowz(), pw = camprojmatrix.roww();
161     vfcP[0] = plane(vec4(pw).mul(-bbmin.x).add(px)).normalize(); // left plane
162     vfcP[1] = plane(vec4(pw).mul(bbmax.x).sub(px)).normalize(); // right plane
163     vfcP[2] = plane(vec4(pw).mul(-bbmin.y).add(py)).normalize(); // bottom plane
164     vfcP[3] = plane(vec4(pw).mul(bbmax.y).sub(py)).normalize(); // top plane
165     vfcP[4] = plane(vec4(pw).add(pz)).normalize(); // near/far planes
166     if(z >= 0) loopi(5) vfcP[i].reflectz(z);
167 
168     vfcDfog = fog;
169     calcvfcD();
170 }
171 
172 plane oldvfcP[5];
173 
savevfcP()174 void savevfcP()
175 {
176     memcpy(oldvfcP, vfcP, sizeof(vfcP));
177 }
178 
restorevfcP()179 void restorevfcP()
180 {
181     memcpy(vfcP, oldvfcP, sizeof(vfcP));
182     calcvfcD();
183 }
184 
visiblecubes(bool cull)185 void visiblecubes(bool cull)
186 {
187     memclear(vasort);
188 
189     if(cull)
190     {
191         setvfcP();
192         findvisiblevas(varoot);
193         sortvisiblevas();
194     }
195     else
196     {
197         memclear(vfcP);
198         vfcDfog = 1000000;
199         memclear(vfcDnear);
200         memclear(vfcDfar);
201         visibleva = NULL;
202         loopv(valist)
203         {
204             vtxarray *va = valist[i];
205             va->distance = 0;
206             va->curvfc = VFC_FULL_VISIBLE;
207             va->occluded = !va->texs ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
208             va->query = NULL;
209             va->next = visibleva;
210             visibleva = va;
211         }
212     }
213 }
214 
insideva(const vtxarray * va,const vec & v,int margin=2)215 static inline bool insideva(const vtxarray *va, const vec &v, int margin = 2)
216 {
217     int size = va->size + margin;
218     return v.x>=va->o.x-margin && v.y>=va->o.y-margin && v.z>=va->o.z-margin &&
219            v.x<=va->o.x+size && v.y<=va->o.y+size && v.z<=va->o.z+size;
220 }
221 
222 ///////// occlusion queries /////////////
223 
224 #define MAXQUERY 2048
225 #define MAXQUERYFRAMES 2
226 
227 struct queryframe
228 {
229     int cur, max;
230     occludequery queries[MAXQUERY];
231 
queryframequeryframe232     queryframe() : cur(0), max(0) {}
233 
flipqueryframe234     void flip() { loopi(cur) queries[i].owner = NULL; cur = 0; }
235 
newqueryqueryframe236     occludequery *newquery(void *owner)
237     {
238         if(cur >= max)
239         {
240             if(max >= MAXQUERY) return NULL;
241             glGenQueries_(1, &queries[max++].id);
242         }
243         occludequery *query = &queries[cur++];
244         query->owner = owner;
245         query->fragments = -1;
246         return query;
247     }
248 
resetqueryframe249     void reset() { loopi(max) queries[i].owner = NULL; }
250 
cleanupqueryframe251     void cleanup()
252     {
253         loopi(max)
254         {
255             glDeleteQueries_(1, &queries[i].id);
256             queries[i].owner = NULL;
257         }
258         cur = max = 0;
259     }
260 };
261 
262 static queryframe queryframes[MAXQUERYFRAMES];
263 static uint flipquery = 0;
264 
getnumqueries()265 int getnumqueries()
266 {
267     return queryframes[flipquery].cur;
268 }
269 
flipqueries()270 void flipqueries()
271 {
272     flipquery = (flipquery + 1) % MAXQUERYFRAMES;
273     queryframes[flipquery].flip();
274 }
275 
newquery(void * owner)276 occludequery *newquery(void *owner)
277 {
278     return queryframes[flipquery].newquery(owner);
279 }
280 
resetqueries()281 void resetqueries()
282 {
283     loopi(MAXQUERYFRAMES) queryframes[i].reset();
284 }
285 
clearqueries()286 void clearqueries()
287 {
288     loopi(MAXQUERYFRAMES) queryframes[i].cleanup();
289 }
290 
291 VAR(oqfrags, 0, 8, 64);
292 VAR(oqwait, 0, 1, 1);
293 
startquery(occludequery * query)294 void startquery(occludequery *query)
295 {
296     glBeginQuery_(GL_SAMPLES_PASSED, query->id);
297 }
298 
endquery(occludequery * query)299 void endquery(occludequery *query)
300 {
301     glEndQuery_(GL_SAMPLES_PASSED);
302 }
303 
checkquery(occludequery * query,bool nowait)304 bool checkquery(occludequery *query, bool nowait)
305 {
306     GLuint fragments;
307     if(query->fragments >= 0) fragments = query->fragments;
308     else
309     {
310         if(nowait || !oqwait)
311         {
312             GLint avail;
313             glGetQueryObjectiv_(query->id, GL_QUERY_RESULT_AVAILABLE, &avail);
314             if(!avail) return false;
315         }
316         glGetQueryObjectuiv_(query->id, GL_QUERY_RESULT, &fragments);
317         query->fragments = fragments;
318     }
319     return fragments < uint(oqfrags);
320 }
321 
322 static GLuint bbvbo = 0, bbebo = 0;
323 
setupbb()324 static void setupbb()
325 {
326     if(!bbvbo)
327     {
328         glGenBuffers_(1, &bbvbo);
329         gle::bindvbo(bbvbo);
330         vec verts[8];
331         loopi(8) verts[i] = vec(i&1, (i>>1)&1, (i>>2)&1);
332         glBufferData_(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
333         gle::clearvbo();
334     }
335     if(!bbebo)
336     {
337         glGenBuffers_(1, &bbebo);
338         gle::bindebo(bbebo);
339         GLushort tris[3*2*6];
340         #define GENFACEORIENT(orient, v0, v1, v2, v3) do { \
341             int offset = orient*3*2; \
342             tris[offset + 0] = v0; \
343             tris[offset + 1] = v1; \
344             tris[offset + 2] = v2; \
345             tris[offset + 3] = v0; \
346             tris[offset + 4] = v2; \
347             tris[offset + 5] = v3; \
348         } while(0);
349         #define GENFACEVERT(orient, vert, ox,oy,oz, rx,ry,rz) (ox | oy | oz)
350         GENFACEVERTS(0, 1, 0, 2, 0, 4, , , , , , )
351         #undef GENFACEORIENT
352         #undef GENFACEVERT
353         glBufferData_(GL_ELEMENT_ARRAY_BUFFER, sizeof(tris), tris, GL_STATIC_DRAW);
354         gle::clearebo();
355     }
356 }
357 
cleanupbb()358 static void cleanupbb()
359 {
360     if(bbvbo) { glDeleteBuffers_(1, &bbvbo); bbvbo = 0; }
361     if(bbebo) { glDeleteBuffers_(1, &bbebo); bbebo = 0; }
362 }
363 
startbb(bool mask)364 void startbb(bool mask)
365 {
366     setupbb();
367     gle::bindvbo(bbvbo);
368     gle::bindebo(bbebo);
369     gle::vertexpointer(sizeof(vec), (const vec *)0);
370     gle::enablevertex();
371     SETSHADER(bbquery);
372     if(mask)
373     {
374         glDepthMask(GL_FALSE);
375         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
376     }
377 }
378 
endbb(bool mask)379 void endbb(bool mask)
380 {
381     gle::disablevertex();
382     gle::clearvbo();
383     gle::clearebo();
384     if(mask)
385     {
386         glDepthMask(GL_TRUE);
387         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
388     }
389 }
390 
drawbb(const ivec & bo,const ivec & br)391 void drawbb(const ivec &bo, const ivec &br)
392 {
393     LOCALPARAMF(bborigin, bo.x, bo.y, bo.z);
394     LOCALPARAMF(bbsize, br.x, br.y, br.z);
395     glDrawRangeElements_(GL_TRIANGLES, 0, 8-1, 3*2*6, GL_UNSIGNED_SHORT, (ushort *)0);
396     xtraverts += 8;
397 }
398 
399 extern int octaentsize;
400 
401 static octaentities *visiblemms, **lastvisiblemms;
402 
insideoe(const octaentities * oe,const vec & v,int margin=1)403 static inline bool insideoe(const octaentities *oe, const vec &v, int margin = 1)
404 {
405     return v.x>=oe->bbmin.x-margin && v.y>=oe->bbmin.y-margin && v.z>=oe->bbmin.z-margin &&
406            v.x<=oe->bbmax.x+margin && v.y<=oe->bbmax.y+margin && v.z<=oe->bbmax.z+margin;
407 }
408 
findvisiblemms(const vector<extentity * > & ents,bool doquery)409 void findvisiblemms(const vector<extentity *> &ents, bool doquery)
410 {
411     visiblemms = NULL;
412     lastvisiblemms = &visiblemms;
413     for(vtxarray *va = visibleva; va; va = va->next)
414     {
415         if(va->mapmodels.empty() || va->curvfc >= VFC_FOGGED || va->occluded >= OCCLUDE_BB) continue;
416         loopv(va->mapmodels)
417         {
418             octaentities *oe = va->mapmodels[i];
419             if(isfoggedcube(oe->o, oe->size) || pvsoccluded(oe->bbmin, oe->bbmax)) continue;
420 
421             bool occluded = doquery && oe->query && oe->query->owner == oe && checkquery(oe->query);
422             if(occluded)
423             {
424                 oe->distance = -1;
425 
426                 oe->next = NULL;
427                 *lastvisiblemms = oe;
428                 lastvisiblemms = &oe->next;
429             }
430             else
431             {
432                 int visible = 0;
433                 loopv(oe->mapmodels)
434                 {
435                     extentity &e = *ents[oe->mapmodels[i]];
436                     if(e.flags&EF_NOVIS) continue;
437                     e.flags |= EF_RENDER;
438                     ++visible;
439                 }
440                 if(!visible) continue;
441 
442                 oe->distance = int(camera1->o.dist_to_bb(oe->o, oe->size));
443 
444                 octaentities **prev = &visiblemms, *cur = visiblemms;
445                 while(cur && cur->distance >= 0 && oe->distance > cur->distance)
446                 {
447                     prev = &cur->next;
448                     cur = cur->next;
449                 }
450 
451                 if(*prev == NULL) lastvisiblemms = &oe->next;
452                 oe->next = *prev;
453                 *prev = oe;
454             }
455         }
456     }
457 }
458 
459 VAR(oqmm, 0, 4, 8);
460 
rendermapmodel(extentity & e)461 void rendermapmodel(extentity &e)
462 {
463     int anim = ANIM_MAPMODEL|ANIM_LOOP, basetime = 0;
464     if(e.flags&EF_ANIM) entities::animatemapmodel(e, anim, basetime);
465     mapmodelinfo *mmi = getmminfo(e.attr2);
466     if(mmi) rendermodel(&e.light, mmi->name, anim, e.o, e.attr1, 0, MDL_CULL_VFC | MDL_CULL_DIST | MDL_DYNLIGHT, NULL, NULL, basetime);
467 }
468 
469 vtxarray *reflectedva;
470 
renderreflectedmapmodels()471 void renderreflectedmapmodels()
472 {
473     const vector<extentity *> &ents = entities::getents();
474 
475     octaentities *mms = visiblemms;
476     if(reflecting)
477     {
478         octaentities **lastmms = &mms;
479         for(vtxarray *va = reflectedva; va; va = va->rnext)
480         {
481             if(va->mapmodels.empty() || va->distance > reflectdist) continue;
482             loopv(va->mapmodels)
483             {
484                 octaentities *oe = va->mapmodels[i];
485                 *lastmms = oe;
486                 lastmms = &oe->rnext;
487             }
488         }
489         *lastmms = NULL;
490     }
491     for(octaentities *oe = mms; oe; oe = reflecting ? oe->rnext : oe->next) if(reflecting || oe->distance >= 0)
492     {
493         if(reflecting || refracting>0 ? oe->bbmax.z <= reflectz : oe->bbmin.z >= reflectz) continue;
494         if(isfoggedcube(oe->o, oe->size)) continue;
495         loopv(oe->mapmodels)
496         {
497            extentity &e = *ents[oe->mapmodels[i]];
498            if(e.flags&(EF_NOVIS | EF_RENDER)) continue;
499            e.flags |= EF_RENDER;
500         }
501     }
502     if(mms)
503     {
504         startmodelbatches();
505         for(octaentities *oe = mms; oe; oe = reflecting ? oe->rnext : oe->next)
506         {
507             loopv(oe->mapmodels)
508             {
509                 extentity &e = *ents[oe->mapmodels[i]];
510                 if(!(e.flags&EF_RENDER)) continue;
511                 rendermapmodel(e);
512                 e.flags &= ~EF_RENDER;
513             }
514         }
515         endmodelbatches();
516     }
517 }
518 
rendermapmodels()519 void rendermapmodels()
520 {
521     static int skipoq = 0;
522     bool doquery = !drawtex && oqfrags && oqmm;
523     const vector<extentity *> &ents = entities::getents();
524     findvisiblemms(ents, doquery);
525 
526     startmodelbatches();
527     for(octaentities *oe = visiblemms; oe; oe = oe->next) if(oe->distance>=0)
528     {
529         bool rendered = false;
530         loopv(oe->mapmodels)
531         {
532             extentity &e = *ents[oe->mapmodels[i]];
533             if(!(e.flags&EF_RENDER)) continue;
534             if(!rendered)
535             {
536                 rendered = true;
537                 oe->query = doquery && oe->distance>0 && !(++skipoq%oqmm) ? newquery(oe) : NULL;
538                 if(oe->query) startmodelquery(oe->query);
539             }
540             rendermapmodel(e);
541             e.flags &= ~EF_RENDER;
542         }
543         if(rendered && oe->query) endmodelquery();
544     }
545     endmodelbatches();
546 
547     bool queried = true;
548     for(octaentities *oe = visiblemms; oe; oe = oe->next) if(oe->distance<0)
549     {
550         oe->query = doquery && !insideoe(oe, camera1->o) ? newquery(oe) : NULL;
551         if(!oe->query) continue;
552         if(queried)
553         {
554             startbb();
555             queried = false;
556         }
557         startquery(oe->query);
558         drawbb(oe->bbmin, ivec(oe->bbmax).sub(oe->bbmin));
559         endquery(oe->query);
560     }
561     if(!queried)
562     {
563         endbb();
564     }
565 }
566 
bbinsideva(const ivec & bo,const ivec & br,vtxarray * va)567 static inline bool bbinsideva(const ivec &bo, const ivec &br, vtxarray *va)
568 {
569     return bo.x >= va->bbmin.x && bo.y >= va->bbmin.y && bo.z >= va->bbmin.z &&
570         br.x <= va->bbmax.x && br.y <= va->bbmax.y && br.z <= va->bbmax.z;
571 }
572 
bboccluded(const ivec & bo,const ivec & br,cube * c,const ivec & o,int size)573 static inline bool bboccluded(const ivec &bo, const ivec &br, cube *c, const ivec &o, int size)
574 {
575     loopoctabox(o, size, bo, br)
576     {
577         ivec co(i, o, size);
578         if(c[i].ext && c[i].ext->va)
579         {
580             vtxarray *va = c[i].ext->va;
581             if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) continue;
582         }
583         if(c[i].children && bboccluded(bo, br, c[i].children, co, size>>1)) continue;
584         return false;
585     }
586     return true;
587 }
588 
bboccluded(const ivec & bo,const ivec & br)589 bool bboccluded(const ivec &bo, const ivec &br)
590 {
591     int diff = (bo.x^br.x) | (bo.y^br.y) | (bo.z^br.z);
592     if(diff&~((1<<worldscale)-1)) return false;
593     int scale = worldscale-1;
594     if(diff&(1<<scale)) return bboccluded(bo, br, worldroot, ivec(0, 0, 0), 1<<scale);
595     cube *c = &worldroot[octastep(bo.x, bo.y, bo.z, scale)];
596     if(c->ext && c->ext->va)
597     {
598         vtxarray *va = c->ext->va;
599         if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) return true;
600     }
601     scale--;
602     while(c->children && !(diff&(1<<scale)))
603     {
604         c = &c->children[octastep(bo.x, bo.y, bo.z, scale)];
605         if(c->ext && c->ext->va)
606         {
607             vtxarray *va = c->ext->va;
608             if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) return true;
609         }
610         scale--;
611     }
612     if(c->children) return bboccluded(bo, br, c->children, ivec(bo).mask(~((2<<scale)-1)), 1<<scale);
613     return false;
614 }
615 
616 VAR(outline, 0, 0, 1);
617 HVARP(outlinecolour, 0, 0, 0xFFFFFF);
618 VAR(dtoutline, 0, 1, 1);
619 
renderoutline()620 void renderoutline()
621 {
622     notextureshader->set();
623 
624     gle::enablevertex();
625 
626     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
627     gle::color(vec::hexcolor(outlinecolour));
628 
629     enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
630 
631     if(!dtoutline) glDisable(GL_DEPTH_TEST);
632 
633     vtxarray *prev = NULL;
634     for(vtxarray *va = visibleva; va; va = va->next)
635     {
636         if(va->occluded >= OCCLUDE_BB) continue;
637         if(!va->alphaback && !va->alphafront && (!va->texs || va->occluded >= OCCLUDE_GEOM)) continue;
638 
639         if(!prev || va->vbuf != prev->vbuf)
640         {
641             gle::bindvbo(va->vbuf);
642             gle::bindebo(va->ebuf);
643             const vertex *ptr = 0;
644             gle::vertexpointer(sizeof(vertex), ptr->pos.v);
645         }
646 
647         if(va->texs && va->occluded < OCCLUDE_GEOM)
648         {
649             drawvatris(va, 3*va->tris, va->edata);
650             xtravertsva += va->verts;
651         }
652         if(va->alphatris)
653         {
654             drawvatris(va, 3*va->alphatris, &va->edata[3*(va->tris + va->blendtris)]);
655             xtravertsva += 3*va->alphatris;
656         }
657 
658         prev = va;
659     }
660 
661     if(!dtoutline) glEnable(GL_DEPTH_TEST);
662 
663     disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
664 
665     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
666 
667     gle::clearvbo();
668     gle::clearebo();
669     gle::disablevertex();
670 }
671 
672 HVAR(blendbrushcolor, 0, 0x0000C0, 0xFFFFFF);
673 
renderblendbrush(GLuint tex,float x,float y,float w,float h)674 void renderblendbrush(GLuint tex, float x, float y, float w, float h)
675 {
676     SETSHADER(blendbrush);
677 
678     gle::enablevertex();
679 
680     glDepthFunc(GL_LEQUAL);
681 
682     glEnable(GL_BLEND);
683     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
684 
685     glBindTexture(GL_TEXTURE_2D, tex);
686     gle::color(vec::hexcolor(blendbrushcolor), 0.25f);
687 
688     LOCALPARAMF(texgenS, 1.0f/w, 0, 0, -x/w);
689     LOCALPARAMF(texgenT, 0, 1.0f/h, 0, -y/h);
690 
691     vtxarray *prev = NULL;
692     for(vtxarray *va = visibleva; va; va = va->next)
693     {
694         if(!va->texs || va->occluded >= OCCLUDE_GEOM) continue;
695         if(va->o.x + va->size <= x || va->o.y + va->size <= y || va->o.x >= x + w || va->o.y >= y + h) continue;
696 
697         if(!prev || va->vbuf != prev->vbuf)
698         {
699             gle::bindvbo(va->vbuf);
700             gle::bindebo(va->ebuf);
701             const vertex *ptr = 0;
702             gle::vertexpointer(sizeof(vertex), ptr->pos.v);
703         }
704 
705         drawvatris(va, 3*va->tris, va->edata);
706         xtravertsva += va->verts;
707 
708         prev = va;
709     }
710 
711     glDisable(GL_BLEND);
712 
713     glDepthFunc(GL_LESS);
714 
715     gle::clearvbo();
716     gle::clearebo();
717     gle::disablevertex();
718 }
719 
rendershadowmapreceivers()720 void rendershadowmapreceivers()
721 {
722     SETSHADER(shadowmapreceiver);
723 
724     gle::enablevertex();
725 
726     glCullFace(GL_FRONT);
727     glDepthMask(GL_FALSE);
728     glDepthFunc(GL_GREATER);
729 
730     extern int ati_minmax_bug;
731     if(!ati_minmax_bug) glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
732 
733     glEnable(GL_BLEND);
734     glBlendEquation_(GL_MAX);
735     glBlendFunc(GL_ONE, GL_ONE);
736 
737     vtxarray *prev = NULL;
738     for(vtxarray *va = visibleva; va; va = va->next)
739     {
740         if(!va->texs || va->curvfc >= VFC_FOGGED || !isshadowmapreceiver(va)) continue;
741 
742         if(!prev || va->vbuf != prev->vbuf)
743         {
744             gle::bindvbo(va->vbuf);
745             gle::bindebo(va->ebuf);
746             const vertex *ptr = 0;
747             gle::vertexpointer(sizeof(vertex), ptr->pos.v);
748         }
749 
750         drawvatris(va, 3*va->tris, va->edata);
751         xtravertsva += va->verts;
752 
753         prev = va;
754     }
755 
756     glDisable(GL_BLEND);
757     glBlendEquation_(GL_FUNC_ADD);
758 
759     glCullFace(GL_BACK);
760     glDepthMask(GL_TRUE);
761     glDepthFunc(GL_LESS);
762 
763     if(!ati_minmax_bug) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
764 
765     gle::clearvbo();
766     gle::clearebo();
767     gle::disablevertex();
768 }
769 
renderdepthobstacles(const vec & bbmin,const vec & bbmax,float scale,float * ranges,int numranges)770 void renderdepthobstacles(const vec &bbmin, const vec &bbmax, float scale, float *ranges, int numranges)
771 {
772     float scales[4] = { 0, 0, 0, 0 }, offsets[4] = { 0, 0, 0, 0 };
773     if(numranges < 0)
774     {
775         SETSHADER(depthfxsplitworld);
776 
777         loopi(-numranges)
778         {
779             if(!i) scales[i] = 1.0f/scale;
780             else scales[i] = scales[i-1]*256;
781         }
782     }
783     else
784     {
785         SETSHADER(depthfxworld);
786 
787         if(!numranges) loopi(4) scales[i] = 1.0f/scale;
788         else loopi(numranges)
789         {
790             scales[i] = 1.0f/scale;
791             offsets[i] = -ranges[i]/scale;
792         }
793     }
794     LOCALPARAMF(depthscale, scales[0], scales[1], scales[2], scales[3]);
795     LOCALPARAMF(depthoffsets, offsets[0], offsets[1], offsets[2], offsets[3]);
796 
797     gle::enablevertex();
798 
799     vtxarray *prev = NULL;
800     for(vtxarray *va = visibleva; va; va = va->next)
801     {
802         if(!va->texs || va->occluded >= OCCLUDE_GEOM ||
803            va->o.x > bbmax.x || va->o.y > bbmax.y || va->o.z > bbmax.z ||
804            va->o.x + va->size < bbmin.x || va->o.y + va->size < bbmin.y || va->o.z + va->size < bbmin.z)
805            continue;
806 
807         if(!prev || va->vbuf != prev->vbuf)
808         {
809             gle::bindvbo(va->vbuf);
810             gle::bindebo(va->ebuf);
811             const vertex *ptr = 0;
812             gle::vertexpointer(sizeof(vertex), ptr->pos.v);
813         }
814 
815         drawvatris(va, 3*va->tris, va->edata);
816         xtravertsva += va->verts;
817         if(va->alphatris > 0)
818         {
819             drawvatris(va, 3*va->alphatris, va->edata + 3*(va->tris + va->blendtris));
820             xtravertsva += 3*va->alphatris;
821         }
822 
823         prev = va;
824     }
825 
826     gle::clearvbo();
827     gle::clearebo();
828     gle::disablevertex();
829 }
830 
831 VAR(oqdist, 0, 256, 1024);
832 VAR(zpass, 0, 1, 1);
833 VAR(envpass, 0, 1, 1);
834 
835 struct renderstate
836 {
837     bool colormask, depthmask, blending;
838     int alphaing;
839     GLuint vbuf;
840     bool vattribs, vquery;
841     vec colorscale, lightcolor;
842     float alphascale;
843     GLuint textures[8];
844     Slot *slot, *texgenslot;
845     VSlot *vslot, *texgenvslot;
846     vec2 texgenscroll;
847     int texgendim;
848     int visibledynlights;
849     uint dynlightmask;
850 
renderstaterenderstate851     renderstate() : colormask(true), depthmask(true), blending(false), alphaing(0), vbuf(0), vattribs(false), vquery(false), colorscale(1, 1, 1), alphascale(0), slot(NULL), texgenslot(NULL), vslot(NULL), texgenvslot(NULL), texgenscroll(0, 0), texgendim(-1), visibledynlights(0), dynlightmask(0)
852     {
853         loopk(8) textures[k] = 0;
854     }
855 };
856 
disablevbuf(renderstate & cur)857 static inline void disablevbuf(renderstate &cur)
858 {
859     gle::clearvbo();
860     gle::clearebo();
861     cur.vbuf = 0;
862 }
863 
enablevquery(renderstate & cur)864 static inline void enablevquery(renderstate &cur)
865 {
866     if(cur.colormask) { cur.colormask = false; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); }
867     if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
868     startbb(false);
869     cur.vquery = true;
870 }
871 
disablevquery(renderstate & cur)872 static inline void disablevquery(renderstate &cur)
873 {
874     endbb(false);
875     cur.vquery = false;
876 }
877 
renderquery(renderstate & cur,occludequery * query,vtxarray * va,bool full=true)878 static void renderquery(renderstate &cur, occludequery *query, vtxarray *va, bool full = true)
879 {
880     if(!cur.vquery) enablevquery(cur);
881 
882     startquery(query);
883 
884     if(full) drawbb(ivec(va->bbmin).sub(1), ivec(va->bbmax).sub(va->bbmin).add(2));
885     else drawbb(va->geommin, ivec(va->geommax).sub(va->geommin));
886 
887     endquery(query);
888 }
889 
890 enum
891 {
892     RENDERPASS_LIGHTMAP = 0,
893     RENDERPASS_Z,
894     RENDERPASS_CAUSTICS,
895     RENDERPASS_FOG,
896     RENDERPASS_LIGHTMAP_BLEND
897 };
898 
899 struct geombatch
900 {
901     const elementset &es;
902     VSlot &vslot;
903     ushort *edata;
904     vtxarray *va;
905     int next, batch;
906 
geombatchgeombatch907     geombatch(const elementset &es, ushort *edata, vtxarray *va)
908       : es(es), vslot(lookupvslot(es.texture)), edata(edata), va(va),
909         next(-1), batch(-1)
910     {}
911 
comparegeombatch912     int compare(const geombatch &b) const
913     {
914         if(va->vbuf < b.va->vbuf) return -1;
915         if(va->vbuf > b.va->vbuf) return 1;
916         if(va->dynlightmask < b.va->dynlightmask) return -1;
917         if(va->dynlightmask > b.va->dynlightmask) return 1;
918         if(vslot.slot->shader < b.vslot.slot->shader) return -1;
919         if(vslot.slot->shader > b.vslot.slot->shader) return 1;
920         if(vslot.slot->params.length() < b.vslot.slot->params.length()) return -1;
921         if(vslot.slot->params.length() > b.vslot.slot->params.length()) return 1;
922         if(es.texture < b.es.texture) return -1;
923         if(es.texture > b.es.texture) return 1;
924         if(es.lmid < b.es.lmid) return -1;
925         if(es.lmid > b.es.lmid) return 1;
926         if(es.envmap < b.es.envmap) return -1;
927         if(es.envmap > b.es.envmap) return 1;
928         if(es.dim < b.es.dim) return -1;
929         if(es.dim > b.es.dim) return 1;
930         return 0;
931     }
932 };
933 
934 static vector<geombatch> geombatches;
935 static int firstbatch = -1, numbatches = 0;
936 
mergetexs(renderstate & cur,vtxarray * va,elementset * texs=NULL,int numtexs=0,ushort * edata=NULL)937 static void mergetexs(renderstate &cur, vtxarray *va, elementset *texs = NULL, int numtexs = 0, ushort *edata = NULL)
938 {
939     if(!texs)
940     {
941         texs = va->eslist;
942         numtexs = va->texs;
943         edata = va->edata;
944         if(cur.alphaing)
945         {
946             texs += va->texs + va->blends;
947             edata += 3*(va->tris + va->blendtris);
948             numtexs = va->alphaback;
949             if(cur.alphaing > 1) numtexs += va->alphafront;
950         }
951     }
952 
953     if(firstbatch < 0)
954     {
955         firstbatch = geombatches.length();
956         numbatches = numtexs;
957         loopi(numtexs-1)
958         {
959             geombatches.add(geombatch(texs[i], edata, va)).next = i+1;
960             edata += texs[i].length[1];
961         }
962         geombatches.add(geombatch(texs[numtexs-1], edata, va));
963         return;
964     }
965 
966     int prevbatch = -1, curbatch = firstbatch, curtex = 0;
967     do
968     {
969         geombatch &b = geombatches.add(geombatch(texs[curtex], edata, va));
970         edata += texs[curtex].length[1];
971         int dir = -1;
972         while(curbatch >= 0)
973         {
974             dir = b.compare(geombatches[curbatch]);
975             if(dir <= 0) break;
976             prevbatch = curbatch;
977             curbatch = geombatches[curbatch].next;
978         }
979         if(!dir)
980         {
981             int last = curbatch, next;
982             for(;;)
983             {
984                 next = geombatches[last].batch;
985                 if(next < 0) break;
986                 last = next;
987             }
988             if(last==curbatch)
989             {
990                 b.batch = curbatch;
991                 b.next = geombatches[curbatch].next;
992                 if(prevbatch < 0) firstbatch = geombatches.length()-1;
993                 else geombatches[prevbatch].next = geombatches.length()-1;
994                 curbatch = geombatches.length()-1;
995             }
996             else
997             {
998                 b.batch = next;
999                 geombatches[last].batch = geombatches.length()-1;
1000             }
1001         }
1002         else
1003         {
1004             numbatches++;
1005             b.next = curbatch;
1006             if(prevbatch < 0) firstbatch = geombatches.length()-1;
1007             else geombatches[prevbatch].next = geombatches.length()-1;
1008             prevbatch = geombatches.length()-1;
1009         }
1010     }
1011     while(++curtex < numtexs);
1012 }
1013 
enablevattribs(renderstate & cur,bool all=true)1014 static inline void enablevattribs(renderstate &cur, bool all = true)
1015 {
1016     gle::enablevertex();
1017     if(all)
1018     {
1019         gle::enabletexcoord0();
1020         gle::enabletexcoord1();
1021         gle::enablenormal();
1022         gle::enabletangent();
1023     }
1024     cur.vattribs = true;
1025 }
1026 
disablevattribs(renderstate & cur,bool all=true)1027 static inline void disablevattribs(renderstate &cur, bool all = true)
1028 {
1029     gle::disablevertex();
1030     if(all)
1031     {
1032         gle::disabletexcoord0();
1033         gle::disabletexcoord1();
1034         gle::disablenormal();
1035         gle::disabletangent();
1036     }
1037     cur.vattribs = false;
1038 }
1039 
changevbuf(renderstate & cur,int pass,vtxarray * va)1040 static void changevbuf(renderstate &cur, int pass, vtxarray *va)
1041 {
1042     gle::bindvbo(va->vbuf);
1043     gle::bindebo(va->ebuf);
1044     cur.vbuf = va->vbuf;
1045 
1046     vertex *vdata = (vertex *)0;
1047     gle::vertexpointer(sizeof(vertex), vdata->pos.v);
1048 
1049     if(pass==RENDERPASS_LIGHTMAP)
1050     {
1051         gle::normalpointer(sizeof(vertex), vdata->norm.v, GL_BYTE);
1052         gle::texcoord0pointer(sizeof(vertex), vdata->tc.v);
1053         gle::texcoord1pointer(sizeof(vertex), vdata->lm.v, GL_SHORT);
1054         gle::tangentpointer(sizeof(vertex), vdata->tangent.v, GL_BYTE);
1055     }
1056 }
1057 
changebatchtmus(renderstate & cur,int pass,geombatch & b)1058 static void changebatchtmus(renderstate &cur, int pass, geombatch &b)
1059 {
1060     bool changed = false;
1061     extern bool brightengeom;
1062     extern int fullbright;
1063     int lmid = brightengeom && (b.es.lmid < LMID_RESERVED || (fullbright && editmode)) ? LMID_BRIGHT : b.es.lmid;
1064     if(cur.textures[1]!=lightmaptexs[lmid].id)
1065     {
1066         glActiveTexture_(GL_TEXTURE1);
1067         glBindTexture(GL_TEXTURE_2D, cur.textures[1] = lightmaptexs[lmid].id);
1068         changed = true;
1069     }
1070     int tmu = 2;
1071     if(b.vslot.slot->shader->type&SHADER_NORMALSLMS)
1072     {
1073         if(cur.textures[tmu]!=lightmaptexs[lmid+1].id)
1074         {
1075             glActiveTexture_(GL_TEXTURE0+tmu);
1076             glBindTexture(GL_TEXTURE_2D, cur.textures[tmu] = lightmaptexs[lmid+1].id);
1077             changed = true;
1078         }
1079         tmu++;
1080     }
1081     if(b.vslot.slot->shader->type&SHADER_ENVMAP && b.es.envmap!=EMID_CUSTOM)
1082     {
1083         GLuint emtex = lookupenvmap(b.es.envmap);
1084         if(cur.textures[tmu]!=emtex)
1085         {
1086             glActiveTexture_(GL_TEXTURE0+tmu);
1087             glBindTexture(GL_TEXTURE_CUBE_MAP, cur.textures[tmu] = emtex);
1088             changed = true;
1089         }
1090     }
1091     if(changed) glActiveTexture_(GL_TEXTURE0);
1092 
1093     if(cur.dynlightmask != b.va->dynlightmask)
1094     {
1095         cur.visibledynlights = setdynlights(b.va);
1096         cur.dynlightmask = b.va->dynlightmask;
1097     }
1098 }
1099 
changeslottmus(renderstate & cur,int pass,Slot & slot,VSlot & vslot)1100 static void changeslottmus(renderstate &cur, int pass, Slot &slot, VSlot &vslot)
1101 {
1102     if(pass==RENDERPASS_LIGHTMAP)
1103     {
1104         GLuint diffusetex = slot.sts.empty() ? notexture->id : slot.sts[0].t->id;
1105         if(cur.textures[0]!=diffusetex)
1106             glBindTexture(GL_TEXTURE_2D, cur.textures[0] = diffusetex);
1107     }
1108 
1109     if(cur.alphaing)
1110     {
1111         float alpha = cur.alphaing > 1 ? vslot.alphafront : vslot.alphaback;
1112         if(cur.colorscale != vslot.colorscale || cur.alphascale != alpha)
1113         {
1114             cur.colorscale = vslot.colorscale;
1115             cur.alphascale = alpha;
1116             GLOBALPARAMF(colorparams, 2*alpha*vslot.colorscale.x, 2*alpha*vslot.colorscale.y, 2*alpha*vslot.colorscale.z, alpha);
1117             setfogcolor(vec(curfogcolor).mul(alpha));
1118         }
1119     }
1120     else if(cur.colorscale != vslot.colorscale)
1121     {
1122         cur.colorscale = vslot.colorscale;
1123         GLOBALPARAMF(colorparams, 2*vslot.colorscale.x, 2*vslot.colorscale.y, 2*vslot.colorscale.z, 1);
1124     }
1125     int tmu = 2, envmaptmu = -1;
1126     if(slot.shader->type&SHADER_NORMALSLMS) tmu++;
1127     if(slot.shader->type&SHADER_ENVMAP) envmaptmu = tmu++;
1128     loopvj(slot.sts)
1129     {
1130         Slot::Tex &t = slot.sts[j];
1131         if(t.type==TEX_DIFFUSE || t.combined>=0) continue;
1132         if(t.type==TEX_ENVMAP)
1133         {
1134             if(envmaptmu>=0 && t.t && cur.textures[envmaptmu]!=t.t->id)
1135             {
1136                 glActiveTexture_(GL_TEXTURE0+envmaptmu);
1137                 glBindTexture(GL_TEXTURE_CUBE_MAP, cur.textures[envmaptmu] = t.t->id);
1138             }
1139         }
1140         else
1141         {
1142             if(cur.textures[tmu]!=t.t->id)
1143             {
1144                 glActiveTexture_(GL_TEXTURE0+tmu);
1145                 glBindTexture(GL_TEXTURE_2D, cur.textures[tmu] = t.t->id);
1146             }
1147             if(++tmu >= 8) break;
1148         }
1149     }
1150     glActiveTexture_(GL_TEXTURE0);
1151 
1152     cur.slot = &slot;
1153     cur.vslot = &vslot;
1154 }
1155 
changeshader(renderstate & cur,Shader * s,Slot & slot,VSlot & vslot,bool shadowed)1156 static void changeshader(renderstate &cur, Shader *s, Slot &slot, VSlot &vslot, bool shadowed)
1157 {
1158     if(glaring)
1159     {
1160         static Shader *noglareshader = NULL, *noglareblendshader = NULL, *noglarealphashader = NULL;
1161         Shader *fallback;
1162         if(cur.blending) { if(!noglareblendshader) noglareblendshader = lookupshaderbyname("noglareblendworld"); fallback = noglareblendshader; }
1163         else if(cur.alphaing) { if(!noglarealphashader) noglarealphashader = lookupshaderbyname("noglarealphaworld"); fallback = noglarealphashader; }
1164         else { if(!noglareshader) noglareshader = lookupshaderbyname("noglareworld"); fallback = noglareshader; }
1165         if(s->hasoption(4)) s->setvariant(cur.visibledynlights, 4, slot, vslot, fallback);
1166         else s->setvariant(cur.blending ? 1 : 0, 4, slot, vslot, fallback);
1167     }
1168     else if(fading && !cur.blending && !cur.alphaing)
1169     {
1170         if(shadowed) s->setvariant(cur.visibledynlights, 3, slot, vslot);
1171         else s->setvariant(cur.visibledynlights, 2, slot, vslot);
1172     }
1173     else if(shadowed) s->setvariant(cur.visibledynlights, 1, slot, vslot);
1174     else if(!cur.visibledynlights) s->set(slot, vslot);
1175     else s->setvariant(cur.visibledynlights-1, 0, slot, vslot);
1176 }
1177 
changetexgen(renderstate & cur,int dim,Slot & slot,VSlot & vslot)1178 static void changetexgen(renderstate &cur, int dim, Slot &slot, VSlot &vslot)
1179 {
1180     if(cur.texgenslot != &slot || cur.texgenvslot != &vslot)
1181     {
1182         Texture *curtex = !cur.texgenslot || cur.texgenslot->sts.empty() ? notexture : cur.texgenslot->sts[0].t,
1183                 *tex = slot.sts.empty() ? notexture : slot.sts[0].t;
1184         if(!cur.texgenvslot || slot.sts.empty() ||
1185             (curtex->xs != tex->xs || curtex->ys != tex->ys ||
1186              cur.texgenvslot->rotation != vslot.rotation || cur.texgenvslot->scale != vslot.scale ||
1187              cur.texgenvslot->offset != vslot.offset || cur.texgenvslot->scroll != vslot.scroll))
1188         {
1189             const texrotation &r = texrotations[vslot.rotation];
1190             float xs = r.flipx ? -tex->xs : tex->xs,
1191                   ys = r.flipy ? -tex->ys : tex->ys;
1192             vec2 scroll(vslot.scroll);
1193             if(r.swapxy) swap(scroll.x, scroll.y);
1194             scroll.x *= lastmillis*tex->xs/xs;
1195             scroll.y *= lastmillis*tex->ys/ys;
1196             if(cur.texgenscroll != scroll)
1197             {
1198                 cur.texgenscroll = scroll;
1199                 cur.texgendim = -1;
1200             }
1201         }
1202         cur.texgenslot = &slot;
1203         cur.texgenvslot = &vslot;
1204     }
1205 
1206     if(cur.texgendim == dim) return;
1207     GLOBALPARAM(texgenscroll, cur.texgenscroll);
1208     cur.texgendim = dim;
1209 }
1210 
renderbatch(renderstate & cur,int pass,geombatch & b)1211 static void renderbatch(renderstate &cur, int pass, geombatch &b)
1212 {
1213     geombatch *shadowed = NULL;
1214     int rendered = -1;
1215     for(geombatch *curbatch = &b;; curbatch = &geombatches[curbatch->batch])
1216     {
1217         ushort len = curbatch->es.length[curbatch->va->shadowed ? 0 : 1];
1218         if(len)
1219         {
1220             if(rendered < 0)
1221             {
1222                 changeshader(cur, b.vslot.slot->shader, *b.vslot.slot, b.vslot, false);
1223                 rendered = 0;
1224                 gbatches++;
1225             }
1226             ushort minvert = curbatch->es.minvert[0], maxvert = curbatch->es.maxvert[0];
1227             if(!curbatch->va->shadowed) { minvert = min(minvert, curbatch->es.minvert[1]); maxvert = max(maxvert, curbatch->es.maxvert[1]); }
1228             drawtris(len, curbatch->edata, minvert, maxvert);
1229             vtris += len/3;
1230         }
1231         if(curbatch->es.length[1] > len && !shadowed) shadowed = curbatch;
1232         if(curbatch->batch < 0) break;
1233     }
1234     if(shadowed) for(geombatch *curbatch = shadowed;; curbatch = &geombatches[curbatch->batch])
1235     {
1236         if(curbatch->va->shadowed && curbatch->es.length[1] > curbatch->es.length[0])
1237         {
1238             if(rendered < 1)
1239             {
1240                 changeshader(cur, b.vslot.slot->shader, *b.vslot.slot, b.vslot, true);
1241                 rendered = 1;
1242                 gbatches++;
1243             }
1244             ushort len = curbatch->es.length[1] - curbatch->es.length[0];
1245             drawtris(len, curbatch->edata + curbatch->es.length[0], curbatch->es.minvert[1], curbatch->es.maxvert[1]);
1246             vtris += len/3;
1247         }
1248         if(curbatch->batch < 0) break;
1249     }
1250 }
1251 
resetbatches()1252 static void resetbatches()
1253 {
1254     geombatches.setsize(0);
1255     firstbatch = -1;
1256     numbatches = 0;
1257 }
1258 
renderbatches(renderstate & cur,int pass)1259 static void renderbatches(renderstate &cur, int pass)
1260 {
1261     cur.slot = NULL;
1262     cur.vslot = NULL;
1263     int curbatch = firstbatch;
1264     if(curbatch >= 0)
1265     {
1266         if(cur.alphaing)
1267         {
1268             if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
1269         }
1270         else if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
1271         if(!cur.colormask) { cur.colormask = true; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, cur.alphaing ? GL_FALSE : GL_TRUE); }
1272         if(!cur.vattribs)
1273         {
1274             if(cur.vquery) disablevquery(cur);
1275             enablevattribs(cur);
1276         }
1277     }
1278     while(curbatch >= 0)
1279     {
1280         geombatch &b = geombatches[curbatch];
1281         curbatch = b.next;
1282 
1283         if(cur.vbuf != b.va->vbuf) changevbuf(cur, pass, b.va);
1284         if(cur.vslot != &b.vslot)
1285         {
1286             changeslottmus(cur, pass, *b.vslot.slot, b.vslot);
1287             if(cur.texgendim != b.es.dim || (cur.texgendim <= 2 && cur.texgenvslot != &b.vslot)) changetexgen(cur, b.es.dim, *b.vslot.slot, b.vslot);
1288         }
1289         else if(cur.texgendim != b.es.dim) changetexgen(cur, b.es.dim, *b.vslot.slot, b.vslot);
1290         if(pass == RENDERPASS_LIGHTMAP) changebatchtmus(cur, pass, b);
1291 
1292         renderbatch(cur, pass, b);
1293     }
1294 
1295     resetbatches();
1296 }
1297 
renderzpass(renderstate & cur,vtxarray * va)1298 void renderzpass(renderstate &cur, vtxarray *va)
1299 {
1300     if(!cur.vattribs)
1301     {
1302         if(cur.vquery) disablevquery(cur);
1303         enablevattribs(cur, false);
1304     }
1305     if(cur.vbuf!=va->vbuf) changevbuf(cur, RENDERPASS_Z, va);
1306     if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
1307     if(cur.colormask) { cur.colormask = false; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); }
1308     int firsttex = 0, numtris = va->tris;
1309     ushort *edata = va->edata;
1310     if(cur.alphaing)
1311     {
1312         firsttex += va->texs + va->blends;
1313         edata += 3*(va->tris + va->blendtris);
1314         numtris = va->alphatris;
1315         xtravertsva += 3*numtris;
1316     }
1317     else xtravertsva += va->verts;
1318     nocolorshader->set();
1319     drawvatris(va, 3*numtris, edata);
1320 }
1321 
1322 vector<vtxarray *> foggedvas;
1323 
1324 #define startvaquery(va, flush) \
1325     do { \
1326         if(va->query) \
1327         { \
1328             flush; \
1329             startquery(va->query); \
1330         } \
1331     } while(0)
1332 
1333 
1334 #define endvaquery(va, flush) \
1335     do { \
1336         if(va->query) \
1337         { \
1338             flush; \
1339             endquery(va->query); \
1340         } \
1341     } while(0)
1342 
renderfoggedvas(renderstate & cur,bool doquery=false)1343 void renderfoggedvas(renderstate &cur, bool doquery = false)
1344 {
1345     static Shader *fogshader = NULL;
1346     if(!fogshader) fogshader = lookupshaderbyname("fogworld");
1347     if(fading) fogshader->setvariant(0, 2);
1348     else fogshader->set();
1349 
1350     if(!cur.vattribs) enablevattribs(cur, false);
1351 
1352     loopv(foggedvas)
1353     {
1354         vtxarray *va = foggedvas[i];
1355         if(cur.vbuf!=va->vbuf) changevbuf(cur, RENDERPASS_FOG, va);
1356 
1357         if(doquery) startvaquery(va, );
1358         drawvatris(va, 3*va->tris, va->edata);
1359         vtris += va->tris;
1360         if(doquery) endvaquery(va, );
1361     }
1362 
1363     foggedvas.setsize(0);
1364 }
1365 
1366 VAR(batchgeom, 0, 1, 1);
1367 
renderva(renderstate & cur,vtxarray * va,int pass=RENDERPASS_LIGHTMAP,bool fogpass=false,bool doquery=false)1368 void renderva(renderstate &cur, vtxarray *va, int pass = RENDERPASS_LIGHTMAP, bool fogpass = false, bool doquery = false)
1369 {
1370     switch(pass)
1371     {
1372         case RENDERPASS_LIGHTMAP:
1373             if(!cur.alphaing) vverts += va->verts;
1374             va->shadowed = false;
1375             va->dynlightmask = 0;
1376             if(fogpass ? va->geommax.z<=reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED)
1377             {
1378                 if(!cur.alphaing && !cur.blending) foggedvas.add(va);
1379                 break;
1380             }
1381             if(!drawtex && !glaring && !cur.alphaing)
1382             {
1383                 va->shadowed = isshadowmapreceiver(va);
1384                 calcdynlightmask(va);
1385             }
1386             if(doquery) startvaquery(va, { if(geombatches.length()) renderbatches(cur, pass); });
1387             mergetexs(cur, va);
1388             if(doquery) endvaquery(va, { if(geombatches.length()) renderbatches(cur, pass); });
1389             else if(!batchgeom && geombatches.length()) renderbatches(cur, pass);
1390             break;
1391 
1392         case RENDERPASS_LIGHTMAP_BLEND:
1393         {
1394             if(doquery) startvaquery(va, { if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP); });
1395             mergetexs(cur, va, &va->eslist[va->texs], va->blends, va->edata + 3*va->tris);
1396             if(doquery) endvaquery(va, { if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP); });
1397             else if(!batchgeom && geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1398             break;
1399         }
1400 
1401         case RENDERPASS_FOG:
1402             if(cur.vbuf!=va->vbuf) changevbuf(cur, pass, va);
1403             drawvatris(va, 3*va->tris, va->edata);
1404             xtravertsva += va->verts;
1405             break;
1406 
1407         case RENDERPASS_CAUSTICS:
1408             if(cur.vbuf!=va->vbuf) changevbuf(cur, pass, va);
1409             drawvatris(va, 3*va->tris, va->edata);
1410             xtravertsva += va->verts;
1411             break;
1412 
1413         case RENDERPASS_Z:
1414             if(doquery) startvaquery(va, );
1415             renderzpass(cur, va);
1416             if(doquery) endvaquery(va, );
1417             break;
1418     }
1419 }
1420 
1421 #define NUMCAUSTICS 32
1422 
1423 static Texture *caustictex[NUMCAUSTICS] = { NULL };
1424 
loadcaustics(bool force)1425 void loadcaustics(bool force)
1426 {
1427     static bool needcaustics = false;
1428     if(force) needcaustics = true;
1429     if(!caustics || !needcaustics) return;
1430     useshaderbyname("caustic");
1431     if(caustictex[0]) return;
1432     loopi(NUMCAUSTICS)
1433     {
1434         defformatstring(name, "<grey><noswizzle>packages/caustics/caust%.2d.png", i);
1435         caustictex[i] = textureload(name);
1436     }
1437 }
1438 
cleanupva()1439 void cleanupva()
1440 {
1441     clearvas(worldroot);
1442     clearqueries();
1443     cleanupbb();
1444     cleanupgrass();
1445     loopi(NUMCAUSTICS) caustictex[i] = NULL;
1446 }
1447 
1448 VARR(causticscale, 0, 50, 10000);
1449 VARR(causticmillis, 0, 75, 1000);
1450 FVARR(causticcontrast, 0, 0.6f, 1);
1451 VARFP(caustics, 0, 1, 1, loadcaustics());
1452 
setupcaustics(float blend)1453 void setupcaustics(float blend)
1454 {
1455     if(!caustictex[0]) loadcaustics(true);
1456 
1457     vec s = vec(0.011f, 0, 0.0066f).mul(100.0f/causticscale), t = vec(0, 0.011f, 0.0066f).mul(100.0f/causticscale);
1458     int tex = (lastmillis/causticmillis)%NUMCAUSTICS;
1459     float frac = float(lastmillis%causticmillis)/causticmillis;
1460     loopi(2)
1461     {
1462         glActiveTexture_(GL_TEXTURE0+i);
1463         glBindTexture(GL_TEXTURE_2D, caustictex[(tex+i)%NUMCAUSTICS]->id);
1464     }
1465     glActiveTexture_(GL_TEXTURE0);
1466     SETSHADER(caustic);
1467     LOCALPARAM(texgenS, s);
1468     LOCALPARAM(texgenT, t);
1469     blend *= causticcontrast;
1470     LOCALPARAMF(frameblend, blend*(1-frac), blend*frac, blend, 1 - blend);
1471 }
1472 
setupgeom(renderstate & cur)1473 void setupgeom(renderstate &cur)
1474 {
1475     GLOBALPARAMF(colorparams, 2, 2, 2, 1);
1476     GLOBALPARAM(camera, camera1->o);
1477     GLOBALPARAMF(ambient, ambientcolor.x/255.0f, ambientcolor.y/255.0f, ambientcolor.z/255.0f);
1478     GLOBALPARAMF(millis, lastmillis/1000.0f);
1479 
1480     glActiveTexture_(GL_TEXTURE0);
1481 }
1482 
cleanupgeom(renderstate & cur)1483 void cleanupgeom(renderstate &cur)
1484 {
1485     if(cur.vattribs) disablevattribs(cur);
1486     if(cur.vbuf) disablevbuf(cur);
1487 }
1488 
1489 #define FIRSTVA (reflecting ? reflectedva : visibleva)
1490 #define NEXTVA (reflecting ? va->rnext : va->next)
1491 
rendergeommultipass(renderstate & cur,int pass,bool fogpass)1492 static void rendergeommultipass(renderstate &cur, int pass, bool fogpass)
1493 {
1494     if(cur.vbuf) disablevbuf(cur);
1495     if(!cur.vattribs) enablevattribs(cur, false);
1496     cur.texgendim = -1;
1497     for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
1498     {
1499         if(!va->texs) continue;
1500         if(refracting)
1501         {
1502             if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_GEOM) continue;
1503             if(ishiddencube(va->o, va->size)) continue;
1504         }
1505         else if(reflecting)
1506         {
1507             if(va->geommax.z <= reflectz) continue;
1508         }
1509         else if(va->occluded >= OCCLUDE_GEOM) continue;
1510         if(fogpass ? va->geommax.z <= reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED) continue;
1511         renderva(cur, va, pass, fogpass);
1512     }
1513     if(geombatches.length()) renderbatches(cur, pass);
1514 }
1515 
1516 VAR(oqgeom, 0, 1, 1);
1517 
rendergeom(float causticspass,bool fogpass)1518 void rendergeom(float causticspass, bool fogpass)
1519 {
1520     if(causticspass && (!causticscale || !causticmillis)) causticspass = 0;
1521 
1522     bool mainpass = !reflecting && !refracting && !drawtex && !glaring,
1523          doOQ = oqfrags && oqgeom && mainpass,
1524          doZP = doOQ && zpass,
1525          doSM = shadowmap && !drawtex && !glaring;
1526     renderstate cur;
1527     if(mainpass)
1528     {
1529         flipqueries();
1530         vtris = vverts = 0;
1531     }
1532     if(!doZP)
1533     {
1534         if(shadowmap && mainpass) rendershadowmap();
1535         setupgeom(cur);
1536         if(doSM) pushshadowmap();
1537     }
1538 
1539     finddynlights();
1540 
1541     resetbatches();
1542 
1543     int blends = 0;
1544     for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
1545     {
1546         if(!va->texs) continue;
1547         if(refracting)
1548         {
1549             if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_GEOM) continue;
1550             if(ishiddencube(va->o, va->size)) continue;
1551         }
1552         else if(reflecting)
1553         {
1554             if(va->geommax.z <= reflectz) continue;
1555         }
1556         else if(doOQ && (zpass || va->distance > oqdist) && !insideva(va, camera1->o))
1557         {
1558             if(va->parent && va->parent->occluded >= OCCLUDE_BB)
1559             {
1560                 va->query = NULL;
1561                 va->occluded = OCCLUDE_PARENT;
1562                 continue;
1563             }
1564             va->occluded = va->query && va->query->owner == va && checkquery(va->query) ? min(va->occluded+1, int(OCCLUDE_BB)) : OCCLUDE_NOTHING;
1565             va->query = newquery(va);
1566             if((!va->query && zpass) || !va->occluded)
1567                 va->occluded = pvsoccluded(va->geommin, va->geommax) ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
1568             if(va->occluded >= OCCLUDE_GEOM)
1569             {
1570                 if(va->query)
1571                 {
1572                     if(!zpass && geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1573                     if(cur.vattribs) disablevattribs(cur, !doZP);
1574                     if(cur.vbuf) disablevbuf(cur);
1575                     renderquery(cur, va->query, va);
1576                 }
1577                 continue;
1578             }
1579         }
1580         else
1581         {
1582             va->query = NULL;
1583             va->occluded = pvsoccluded(va->geommin, va->geommax) ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
1584             if(va->occluded >= OCCLUDE_GEOM) continue;
1585         }
1586 
1587         if(!doZP) blends += va->blends;
1588         renderva(cur, va, doZP ? RENDERPASS_Z : RENDERPASS_LIGHTMAP, fogpass, doOQ);
1589     }
1590 
1591     if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1592 
1593     if(cur.vquery) disablevquery(cur);
1594     if(cur.vattribs) disablevattribs(cur, !doZP);
1595     if(cur.vbuf) disablevbuf(cur);
1596 
1597     if(!cur.colormask) { cur.colormask = true; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); }
1598     if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
1599 
1600     bool multipassing = false;
1601 
1602     if(doZP)
1603     {
1604 		glFlush();
1605 
1606         if(shadowmap && mainpass) rendershadowmap();
1607         setupgeom(cur);
1608         if(doSM) pushshadowmap();
1609 
1610         if(!multipassing) { multipassing = true; glDepthFunc(GL_LEQUAL); }
1611         cur.texgendim = -1;
1612 
1613         for(vtxarray *va = visibleva; va; va = va->next)
1614         {
1615             if(!va->texs || va->occluded >= OCCLUDE_GEOM) continue;
1616             blends += va->blends;
1617             renderva(cur, va, RENDERPASS_LIGHTMAP, fogpass);
1618         }
1619         if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1620         for(vtxarray *va = visibleva; va; va = va->next)
1621         {
1622             if(!va->texs || va->occluded < OCCLUDE_GEOM) continue;
1623             else if((va->parent && va->parent->occluded >= OCCLUDE_BB) ||
1624                     (va->query && checkquery(va->query)))
1625             {
1626                 va->occluded = OCCLUDE_BB;
1627                 continue;
1628             }
1629             else
1630             {
1631                 va->occluded = pvsoccluded(va->geommin, va->geommax) ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
1632                 if(va->occluded >= OCCLUDE_GEOM) continue;
1633             }
1634 
1635             blends += va->blends;
1636             renderva(cur, va, RENDERPASS_LIGHTMAP, fogpass);
1637         }
1638         if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1639     }
1640 
1641     if(blends)
1642     {
1643         if(cur.vbuf) disablevbuf(cur);
1644 
1645         if(!multipassing) { multipassing = true; glDepthFunc(GL_LEQUAL); }
1646         glDepthMask(GL_FALSE);
1647         glEnable(GL_BLEND);
1648         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1649         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
1650 
1651         cur.texgendim = -1;
1652         cur.blending = true;
1653         for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
1654         {
1655             if(!va->blends) continue;
1656             if(refracting)
1657             {
1658                 if(refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) continue;
1659                 if(ishiddencube(va->o, va->size)) continue;
1660                 if(va->occluded >= OCCLUDE_GEOM) continue;
1661             }
1662             else if(reflecting)
1663             {
1664                 if(va->geommax.z <= reflectz) continue;
1665             }
1666             else if(va->occluded >= OCCLUDE_GEOM) continue;
1667             if(fogpass ? va->geommax.z <= reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED) continue;
1668             renderva(cur, va, RENDERPASS_LIGHTMAP_BLEND, fogpass);
1669         }
1670         if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1671         cur.blending = false;
1672 
1673         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1674         glDisable(GL_BLEND);
1675         glDepthMask(GL_TRUE);
1676     }
1677 
1678     if(doSM) popshadowmap();
1679 
1680     if(cur.vattribs) disablevattribs(cur);
1681 
1682     if(foggedvas.length()) renderfoggedvas(cur, doOQ && !zpass);
1683 
1684     if(causticspass)
1685     {
1686         if(!multipassing) { multipassing = true; glDepthFunc(GL_LEQUAL); }
1687         glDepthMask(GL_FALSE);
1688         glEnable(GL_BLEND);
1689 
1690         setupcaustics(causticspass);
1691         glBlendFunc(GL_ZERO, GL_SRC_COLOR);
1692         if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
1693         rendergeommultipass(cur, RENDERPASS_CAUSTICS, fogpass);
1694         if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1695 
1696         glDisable(GL_BLEND);
1697         glDepthMask(GL_TRUE);
1698     }
1699 
1700     if(multipassing) glDepthFunc(GL_LESS);
1701 
1702     cleanupgeom(cur);
1703 }
1704 
renderalphageom(bool fogpass)1705 void renderalphageom(bool fogpass)
1706 {
1707     static vector<vtxarray *> alphavas;
1708     alphavas.setsize(0);
1709     bool hasback = false;
1710     for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
1711     {
1712         if(!va->alphatris) continue;
1713         if(refracting)
1714         {
1715             if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_BB) continue;
1716             if(ishiddencube(va->o, va->size)) continue;
1717             if(va->occluded >= OCCLUDE_GEOM && pvsoccluded(va->geommin, va->geommax)) continue;
1718         }
1719         else if(reflecting)
1720         {
1721             if(va->geommax.z <= reflectz) continue;
1722         }
1723         else
1724         {
1725             if(va->occluded >= OCCLUDE_BB) continue;
1726             if(va->occluded >= OCCLUDE_GEOM && pvsoccluded(va->geommin, va->geommax)) continue;
1727         }
1728         if(fogpass ? va->geommax.z <= reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED) continue;
1729         alphavas.add(va);
1730         if(va->alphabacktris) hasback = true;
1731     }
1732     if(alphavas.empty()) return;
1733 
1734     resetbatches();
1735 
1736     renderstate cur;
1737     cur.alphaing = 1;
1738 
1739     loop(front, 2) if(front || hasback)
1740     {
1741         cur.alphaing = front+1;
1742         if(!front) glCullFace(GL_FRONT);
1743         cur.vbuf = 0;
1744         cur.texgendim = -1;
1745         loopv(alphavas) renderva(cur, alphavas[i], RENDERPASS_Z);
1746         if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
1747         cur.colormask = true;
1748         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
1749 
1750         if(cur.vattribs) disablevattribs(cur, false);
1751         if(cur.vbuf) disablevbuf(cur);
1752 
1753         setupgeom(cur);
1754 
1755         glDepthFunc(GL_LEQUAL);
1756         glEnable(GL_BLEND);
1757         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1758         cur.vbuf = 0;
1759         cur.texgendim = -1;
1760         cur.colorscale = vec(1, 1, 1);
1761         cur.alphascale = -1;
1762         loopv(alphavas) if(front || alphavas[i]->alphabacktris) renderva(cur, alphavas[i], RENDERPASS_LIGHTMAP, fogpass);
1763         if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1764 
1765         cleanupgeom(cur);
1766 
1767         resetfogcolor();
1768         if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
1769         glDisable(GL_BLEND);
1770         glDepthFunc(GL_LESS);
1771         if(!front) glCullFace(GL_BACK);
1772     }
1773 
1774     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, fading ? GL_FALSE : GL_TRUE);
1775 }
1776 
findreflectedvas(vector<vtxarray * > & vas,int prevvfc=VFC_PART_VISIBLE)1777 void findreflectedvas(vector<vtxarray *> &vas, int prevvfc = VFC_PART_VISIBLE)
1778 {
1779     loopv(vas)
1780     {
1781         vtxarray *va = vas[i];
1782         if(prevvfc >= VFC_NOT_VISIBLE) va->curvfc = prevvfc;
1783         if(va->curvfc == VFC_FOGGED || va->curvfc == PVS_FOGGED || va->o.z+va->size <= reflectz || isfoggedcube(va->o, va->size)) continue;
1784         bool render = true;
1785         if(va->curvfc == VFC_FULL_VISIBLE)
1786         {
1787             if(va->occluded >= OCCLUDE_BB) continue;
1788             if(va->occluded >= OCCLUDE_GEOM) render = false;
1789         }
1790         else if(va->curvfc == PVS_FULL_VISIBLE) continue;
1791         if(render)
1792         {
1793             if(va->curvfc >= VFC_NOT_VISIBLE) va->distance = (int)vadist(va, camera1->o);
1794             vtxarray **vprev = &reflectedva, *vcur = reflectedva;
1795             while(vcur && va->distance > vcur->distance)
1796             {
1797                 vprev = &vcur->rnext;
1798                 vcur = vcur->rnext;
1799             }
1800             va->rnext = *vprev;
1801             *vprev = va;
1802         }
1803         if(va->children.length()) findreflectedvas(va->children, va->curvfc);
1804     }
1805 }
1806 
renderreflectedgeom(bool causticspass,bool fogpass)1807 void renderreflectedgeom(bool causticspass, bool fogpass)
1808 {
1809     if(reflecting)
1810     {
1811         reflectedva = NULL;
1812         findreflectedvas(varoot);
1813         rendergeom(causticspass ? 1 : 0, fogpass);
1814     }
1815     else rendergeom(causticspass ? 1 : 0, fogpass);
1816 }
1817 
1818 static vtxarray *prevskyva = NULL;
1819 
renderskyva(vtxarray * va,bool explicitonly=false)1820 void renderskyva(vtxarray *va, bool explicitonly = false)
1821 {
1822     if(!prevskyva || va->vbuf != prevskyva->vbuf)
1823     {
1824         gle::bindvbo(va->vbuf);
1825         gle::bindebo(va->skybuf);
1826         const vertex *ptr = 0;
1827         gle::vertexpointer(sizeof(vertex), ptr->pos.v);
1828         if(!prevskyva) gle::enablevertex();
1829     }
1830 
1831     drawvatris(va, explicitonly ? va->explicitsky : va->sky+va->explicitsky, explicitonly ? va->skydata+va->sky : va->skydata);
1832 
1833     if(!explicitonly) xtraverts += va->sky/3;
1834     xtraverts += va->explicitsky/3;
1835 
1836     prevskyva = va;
1837 }
1838 
1839 int renderedsky = 0, renderedexplicitsky = 0, renderedskyfaces = 0, renderedskyclip = INT_MAX;
1840 
updateskystats(vtxarray * va)1841 static inline void updateskystats(vtxarray *va)
1842 {
1843     renderedsky += va->sky;
1844     renderedexplicitsky += va->explicitsky;
1845     renderedskyfaces |= va->skyfaces&0x3F;
1846     if(!(va->skyfaces&0x1F) || camera1->o.z < va->skyclip) renderedskyclip = min(renderedskyclip, va->skyclip);
1847     else renderedskyclip = 0;
1848 }
1849 
renderreflectedskyvas(vector<vtxarray * > & vas,int prevvfc=VFC_PART_VISIBLE)1850 void renderreflectedskyvas(vector<vtxarray *> &vas, int prevvfc = VFC_PART_VISIBLE)
1851 {
1852     loopv(vas)
1853     {
1854         vtxarray *va = vas[i];
1855         if(prevvfc >= VFC_NOT_VISIBLE) va->curvfc = prevvfc;
1856         if((va->curvfc == VFC_FULL_VISIBLE && va->occluded >= OCCLUDE_BB) || va->curvfc==PVS_FULL_VISIBLE) continue;
1857         if(va->o.z+va->size <= reflectz || ishiddencube(va->o, va->size)) continue;
1858         if(va->sky+va->explicitsky)
1859         {
1860             updateskystats(va);
1861             renderskyva(va);
1862         }
1863         if(va->children.length()) renderreflectedskyvas(va->children, va->curvfc);
1864     }
1865 }
1866 
rendersky(bool explicitonly)1867 bool rendersky(bool explicitonly)
1868 {
1869     prevskyva = NULL;
1870     renderedsky = renderedexplicitsky = renderedskyfaces = 0;
1871     renderedskyclip = INT_MAX;
1872 
1873     if(reflecting)
1874     {
1875         renderreflectedskyvas(varoot);
1876     }
1877     else for(vtxarray *va = visibleva; va; va = va->next)
1878     {
1879         if((va->occluded >= OCCLUDE_BB && va->skyfaces&0x80) || !(va->sky+va->explicitsky)) continue;
1880 
1881         // count possibly visible sky even if not actually rendered
1882         updateskystats(va);
1883         if(explicitonly && !va->explicitsky) continue;
1884         renderskyva(va, explicitonly);
1885     }
1886 
1887     if(prevskyva)
1888     {
1889         gle::disablevertex();
1890         gle::clearvbo();
1891         gle::clearebo();
1892     }
1893 
1894     return renderedsky+renderedexplicitsky > 0;
1895 }
1896 
1897