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 = min(int(dist*VASORTSIZE/hdr.worldsize), 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     memset(vasort, 0, sizeof(vasort));
188 
189     if(cull)
190     {
191         setvfcP();
192         findvisiblevas(varoot);
193         sortvisiblevas();
194     }
195     else
196     {
197         memset(vfcP, 0, sizeof(vfcP));
198         vfcDfog = 1000000;
199         memset(vfcDnear, 0, sizeof(vfcDnear));
200         memset(vfcDfar, 0, sizeof(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(0, oqfrags, 0, 8, 64);
292 VAR(0, 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.lastemit && e.spawned() && e.attrs[6]&MMT_HIDE) 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(0, oqmm, 0, 4, 8);
460 
461 VAR(0, mmanimoverride, -1, 0, ANIM_ALL);
462 
rendermapmodel(extentity & e)463 void rendermapmodel(extentity &e)
464 {
465     int anim = ANIM_MAPMODEL|ANIM_LOOP, basetime = 0, flags = MDL_CULL_VFC|MDL_CULL_DIST|MDL_DYNLIGHT;
466     if(e.lastemit)
467     {
468         if(e.attrs[6]&MMT_HIDE && e.spawned()) return;
469         anim = e.spawned() ? ANIM_TRIGGER_ON : ANIM_TRIGGER_OFF;
470         if(e.lastemit > 0 && lastmillis-e.lastemit < entities::triggertime(e)) basetime = e.lastemit;
471         else anim |= ANIM_END;
472     }
473     if(e.attrs[6]&MMT_NOSHADOW && !(e.attrs[6]&MMT_NODYNSHADOW)) flags |= e.lastemit ? MDL_DYNSHADOW : MDL_SHADOW;
474     if(mmanimoverride)
475     {
476         anim = (mmanimoverride<0 ? ANIM_ALL : mmanimoverride)|ANIM_LOOP;
477         basetime = 0;
478     }
479     mapmodelinfo *mmi = getmminfo(e.attrs[0]);
480     if(mmi)
481     {
482         if(e.attrs[8] || e.attrs[9])
483         {
484             vec r = game::getpalette(e.attrs[8], e.attrs[9]);
485             if(e.attrs[7]) r.mul(vec::hexcolor(e.attrs[7]));
486             e.light.material[0] = bvec::fromcolor(r);
487         }
488         else e.light.material[0] = e.attrs[7] ? bvec(e.attrs[7]) : bvec(255, 255, 255);
489         float yaw = e.attrs[1], pitch = e.attrs[2], roll = e.attrs[3];
490         if(e.attrs[10]) yaw += e.attrs[10]*lastmillis/1000.0f;
491         if(e.attrs[11]) pitch += e.attrs[11]*lastmillis/1000.0f;
492         if(e.attrs[12]) roll += e.attrs[12]*lastmillis/1000.0f;
493         rendermodel(&e.light, mmi->name, anim, e.o, yaw, pitch, roll, flags, NULL, NULL, basetime, 0, e.attrs[4] ? min(e.attrs[4]/100.f, 1.f) : 1.f, e.attrs[5] ? max(e.attrs[5]/100.f, 1e-3f) : 1.f);
494     }
495 }
496 
497 vtxarray *reflectedva;
498 
renderreflectedmapmodels()499 void renderreflectedmapmodels()
500 {
501     const vector<extentity *> &ents = entities::getents();
502 
503     octaentities *mms = visiblemms;
504     if(reflecting)
505     {
506         octaentities **lastmms = &mms;
507         for(vtxarray *va = reflectedva; va; va = va->rnext)
508         {
509             if(va->mapmodels.empty() || va->distance > reflectdist) continue;
510             loopv(va->mapmodels)
511             {
512                 octaentities *oe = va->mapmodels[i];
513                 *lastmms = oe;
514                 lastmms = &oe->rnext;
515             }
516         }
517         *lastmms = NULL;
518     }
519     for(octaentities *oe = mms; oe; oe = reflecting ? oe->rnext : oe->next) if(reflecting || oe->distance >= 0)
520     {
521         if(reflecting || refracting>0 ? oe->bbmax.z <= reflectz : oe->bbmin.z >= reflectz) continue;
522         if(isfoggedcube(oe->o, oe->size)) continue;
523         loopv(oe->mapmodels)
524         {
525            extentity &e = *ents[oe->mapmodels[i]];
526            if(e.flags&EF_RENDER || (e.lastemit && e.spawned() && e.attrs[6]&MMT_HIDE)) continue;
527            e.flags |= EF_RENDER;
528         }
529     }
530     if(mms)
531     {
532         startmodelbatches();
533         for(octaentities *oe = mms; oe; oe = reflecting ? oe->rnext : oe->next)
534         {
535             loopv(oe->mapmodels)
536             {
537                 extentity &e = *ents[oe->mapmodels[i]];
538                 if(!(e.flags&EF_RENDER)) continue;
539                 rendermapmodel(e);
540                 e.flags &= ~EF_RENDER;
541             }
542         }
543         endmodelbatches();
544     }
545 }
546 
rendermapmodels()547 void rendermapmodels()
548 {
549     static int skipoq = 0;
550     bool doquery = !drawtex && oqfrags && oqmm;
551     const vector<extentity *> &ents = entities::getents();
552     findvisiblemms(ents, doquery);
553 
554     startmodelbatches();
555     for(octaentities *oe = visiblemms; oe; oe = oe->next) if(oe->distance>=0)
556     {
557         bool rendered = false;
558         loopv(oe->mapmodels)
559         {
560             extentity &e = *ents[oe->mapmodels[i]];
561             if(!(e.flags&EF_RENDER)) continue;
562             if(!rendered)
563             {
564                 rendered = true;
565                 oe->query = doquery && oe->distance>0 && !(++skipoq%oqmm) ? newquery(oe) : NULL;
566                 if(oe->query) startmodelquery(oe->query);
567             }
568             rendermapmodel(e);
569             e.flags &= ~EF_RENDER;
570         }
571         if(rendered && oe->query) endmodelquery();
572     }
573     endmodelbatches();
574 
575     bool queried = false;
576     for(octaentities *oe = visiblemms; oe; oe = oe->next) if(oe->distance<0)
577     {
578         oe->query = doquery && !insideoe(oe, camera1->o) ? newquery(oe) : NULL;
579         if(!oe->query) continue;
580         if(!queried)
581         {
582             startbb();
583             queried = true;
584         }
585         startquery(oe->query);
586         drawbb(oe->bbmin, ivec(oe->bbmax).sub(oe->bbmin));
587         endquery(oe->query);
588     }
589     if(queried)
590     {
591         endbb();
592     }
593 }
594 
bbinsideva(const ivec & bo,const ivec & br,vtxarray * va)595 static inline bool bbinsideva(const ivec &bo, const ivec &br, vtxarray *va)
596 {
597     return bo.x >= va->bbmin.x && bo.y >= va->bbmin.y && bo.z >= va->bbmin.z &&
598         br.x <= va->bbmax.x && br.y <= va->bbmax.y && br.z <= va->bbmax.z;
599 }
600 
bboccluded(const ivec & bo,const ivec & br,cube * c,const ivec & o,int size)601 static inline bool bboccluded(const ivec &bo, const ivec &br, cube *c, const ivec &o, int size)
602 {
603     loopoctabox(o, size, bo, br)
604     {
605         ivec co(i, o, size);
606         if(c[i].ext && c[i].ext->va)
607         {
608             vtxarray *va = c[i].ext->va;
609             if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) continue;
610         }
611         if(c[i].children && bboccluded(bo, br, c[i].children, co, size>>1)) continue;
612         return false;
613     }
614     return true;
615 }
616 
bboccluded(const ivec & bo,const ivec & br)617 bool bboccluded(const ivec &bo, const ivec &br)
618 {
619     int diff = (bo.x^br.x) | (bo.y^br.y) | (bo.z^br.z);
620     if(diff&~((1<<worldscale)-1)) return false;
621     int scale = worldscale-1;
622     if(diff&(1<<scale)) return bboccluded(bo, br, worldroot, ivec(0, 0, 0), 1<<scale);
623     cube *c = &worldroot[octastep(bo.x, bo.y, bo.z, scale)];
624     if(c->ext && c->ext->va)
625     {
626         vtxarray *va = c->ext->va;
627         if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) return true;
628     }
629     scale--;
630     while(c->children && !(diff&(1<<scale)))
631     {
632         c = &c->children[octastep(bo.x, bo.y, bo.z, scale)];
633         if(c->ext && c->ext->va)
634         {
635             vtxarray *va = c->ext->va;
636             if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) return true;
637         }
638         scale--;
639     }
640     if(c->children) return bboccluded(bo, br, c->children, ivec(bo).mask(~((2<<scale)-1)), 1<<scale);
641     return false;
642 }
643 
644 VAR(IDF_PERSIST, outline, 0, 1, 1);
645 VAR(IDF_HEX|IDF_PERSIST, outlinecolour, 0, 0, 0xFFFFFF);
646 VAR(0, dtoutline, 0, 1, 1);
647 
renderoutline()648 void renderoutline()
649 {
650     notextureshader->set();
651 
652     gle::enablevertex();
653 
654     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
655     gle::color(vec::hexcolor(outlinecolour));
656 
657     enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
658 
659     if(!dtoutline) glDisable(GL_DEPTH_TEST);
660 
661     vtxarray *prev = NULL;
662     for(vtxarray *va = visibleva; va; va = va->next)
663     {
664         if(va->occluded >= OCCLUDE_BB) continue;
665         if(!va->alphaback && !va->alphafront && (!va->texs || va->occluded >= OCCLUDE_GEOM)) continue;
666 
667         if(!prev || va->vbuf != prev->vbuf)
668         {
669             gle::bindvbo(va->vbuf);
670             gle::bindebo(va->ebuf);
671             const vertex *ptr = 0;
672             gle::vertexpointer(sizeof(vertex), ptr->pos.v);
673         }
674 
675         if(va->texs && va->occluded < OCCLUDE_GEOM)
676         {
677             drawvatris(va, 3*va->tris, va->edata);
678             xtravertsva += va->verts;
679         }
680         if(va->alphaback || va->alphafront)
681         {
682             drawvatris(va, 3*(va->alphabacktris + va->alphafronttris), &va->edata[3*(va->tris + va->blendtris)]);
683             xtravertsva += 3*(va->alphabacktris + va->alphafronttris);
684         }
685 
686         prev = va;
687     }
688 
689     if(!dtoutline) glEnable(GL_DEPTH_TEST);
690 
691     disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
692 
693     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
694 
695     gle::clearvbo();
696     gle::clearebo();
697     gle::disablevertex();
698 }
699 
700 VAR(IDF_HEX, blendbrushcolor, 0, 0x0000C0, 0xFFFFFF);
701 
renderblendbrush(GLuint tex,float x,float y,float w,float h)702 void renderblendbrush(GLuint tex, float x, float y, float w, float h)
703 {
704     SETSHADER(blendbrush);
705 
706     gle::enablevertex();
707 
708     glDepthFunc(GL_LEQUAL);
709 
710     glEnable(GL_BLEND);
711     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
712 
713     glBindTexture(GL_TEXTURE_2D, tex);
714     gle::color(vec::hexcolor(blendbrushcolor), 0.25f);
715 
716     LOCALPARAMF(texgenS, 1.0f/w, 0, 0, -x/w);
717     LOCALPARAMF(texgenT, 0, 1.0f/h, 0, -y/h);
718 
719     vtxarray *prev = NULL;
720     for(vtxarray *va = visibleva; va; va = va->next)
721     {
722         if(!va->texs || va->occluded >= OCCLUDE_GEOM) continue;
723         if(va->o.x + va->size <= x || va->o.y + va->size <= y || va->o.x >= x + w || va->o.y >= y + h) continue;
724 
725         if(!prev || va->vbuf != prev->vbuf)
726         {
727             gle::bindvbo(va->vbuf);
728             gle::bindebo(va->ebuf);
729             const vertex *ptr = 0;
730             gle::vertexpointer(sizeof(vertex), ptr->pos.v);
731         }
732 
733         drawvatris(va, 3*va->tris, va->edata);
734         xtravertsva += va->verts;
735 
736         prev = va;
737     }
738 
739     glDisable(GL_BLEND);
740 
741     glDepthFunc(GL_LESS);
742 
743     gle::clearvbo();
744     gle::clearebo();
745     gle::disablevertex();
746 }
747 
rendershadowmapreceivers()748 void rendershadowmapreceivers()
749 {
750     SETSHADER(shadowmapreceiver);
751 
752     gle::enablevertex();
753 
754     glCullFace(GL_FRONT);
755     glDepthMask(GL_FALSE);
756     glDepthFunc(GL_GREATER);
757 
758     extern int ati_minmax_bug;
759     if(!ati_minmax_bug) glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
760 
761     glEnable(GL_BLEND);
762     glBlendEquation_(GL_MAX);
763     glBlendFunc(GL_ONE, GL_ONE);
764 
765     vtxarray *prev = NULL;
766     for(vtxarray *va = visibleva; va; va = va->next)
767     {
768         if(!va->texs || va->curvfc >= VFC_FOGGED || !isshadowmapreceiver(va)) continue;
769 
770         if(!prev || va->vbuf != prev->vbuf)
771         {
772             gle::bindvbo(va->vbuf);
773             gle::bindebo(va->ebuf);
774             const vertex *ptr = 0;
775             gle::vertexpointer(sizeof(vertex), ptr->pos.v);
776         }
777 
778         drawvatris(va, 3*va->tris, va->edata);
779         xtravertsva += va->verts;
780 
781         prev = va;
782     }
783 
784     glDisable(GL_BLEND);
785     glBlendEquation_(GL_FUNC_ADD);
786 
787     glCullFace(GL_BACK);
788     glDepthMask(GL_TRUE);
789     glDepthFunc(GL_LESS);
790 
791     if(!ati_minmax_bug) glColorMask(COLORMASK, GL_TRUE);
792 
793     gle::clearvbo();
794     gle::clearebo();
795     gle::disablevertex();
796 }
797 
renderdepthobstacles(const vec & bbmin,const vec & bbmax,float scale,float * ranges,int numranges)798 void renderdepthobstacles(const vec &bbmin, const vec &bbmax, float scale, float *ranges, int numranges)
799 {
800     float scales[4] = { 0, 0, 0, 0 }, offsets[4] = { 0, 0, 0, 0 };
801     if(numranges < 0)
802     {
803         SETSHADER(depthfxsplitworld);
804 
805         loopi(-numranges)
806         {
807             if(!i) scales[i] = 1.0f/scale;
808             else scales[i] = scales[i-1]*256;
809         }
810     }
811     else
812     {
813         SETSHADER(depthfxworld);
814 
815         if(!numranges) loopi(4) scales[i] = 1.0f/scale;
816         else loopi(numranges)
817         {
818             scales[i] = 1.0f/scale;
819             offsets[i] = -ranges[i]/scale;
820         }
821     }
822     LOCALPARAMF(depthscale, scales[0], scales[1], scales[2], scales[3]);
823     LOCALPARAMF(depthoffsets, offsets[0], offsets[1], offsets[2], offsets[3]);
824 
825     gle::enablevertex();
826 
827     vtxarray *prev = NULL;
828     for(vtxarray *va = visibleva; va; va = va->next)
829     {
830         if(!va->texs || va->occluded >= OCCLUDE_GEOM ||
831            va->o.x > bbmax.x || va->o.y > bbmax.y || va->o.z > bbmax.z ||
832            va->o.x + va->size < bbmin.x || va->o.y + va->size < bbmin.y || va->o.z + va->size < bbmin.z)
833            continue;
834 
835         if(!prev || va->vbuf != prev->vbuf)
836         {
837             gle::bindvbo(va->vbuf);
838             gle::bindebo(va->ebuf);
839             const vertex *ptr = 0;
840             gle::vertexpointer(sizeof(vertex), ptr->pos.v);
841         }
842 
843         drawvatris(va, 3*va->tris, va->edata);
844         xtravertsva += va->verts;
845         if(va->alphabacktris + va->alphafronttris > 0)
846         {
847             drawvatris(va, 3*(va->alphabacktris + va->alphafronttris), va->edata + 3*(va->tris + va->blendtris));
848             xtravertsva += 3*(va->alphabacktris + va->alphafronttris);
849         }
850 
851         prev = va;
852     }
853 
854     gle::clearvbo();
855     gle::clearebo();
856     gle::disablevertex();
857 }
858 
859 VAR(0, oqdist, 0, 256, 1024);
860 VAR(0, zpass, 0, 1, 1);
861 VAR(0, envpass, 0, 1, 1);
862 
863 struct renderstate
864 {
865     bool colormask, depthmask, blending;
866     int alphaing;
867     GLuint vbuf;
868     bool vattribs, vquery;
869     vec colorscale, lightcolor;
870     float alphascale;
871     GLuint textures[8];
872     Slot *slot, *texgenslot;
873     VSlot *vslot, *texgenvslot;
874     vec2 texgenscroll;
875     int texgendim;
876     int visibledynlights;
877     uint dynlightmask;
878 
renderstaterenderstate879     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)
880     {
881         loopk(8) textures[k] = 0;
882     }
883 };
884 
disablevbuf(renderstate & cur)885 static inline void disablevbuf(renderstate &cur)
886 {
887     gle::clearvbo();
888     gle::clearebo();
889     cur.vbuf = 0;
890 }
891 
enablevquery(renderstate & cur)892 static inline void enablevquery(renderstate &cur)
893 {
894     if(cur.colormask) { cur.colormask = false; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); }
895     if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
896     startbb(false);
897     cur.vquery = true;
898 }
899 
disablevquery(renderstate & cur)900 static inline void disablevquery(renderstate &cur)
901 {
902     endbb(false);
903     cur.vquery = false;
904 }
905 
renderquery(renderstate & cur,occludequery * query,vtxarray * va,bool full=true)906 static void renderquery(renderstate &cur, occludequery *query, vtxarray *va, bool full = true)
907 {
908     if(!cur.vquery) enablevquery(cur);
909 
910     startquery(query);
911 
912     if(full) drawbb(ivec(va->bbmin).sub(1), ivec(va->bbmax).sub(va->bbmin).add(2));
913     else drawbb(va->geommin, ivec(va->geommax).sub(va->geommin));
914 
915     endquery(query);
916 }
917 
918 enum
919 {
920     RENDERPASS_LIGHTMAP = 0,
921     RENDERPASS_Z,
922     RENDERPASS_CAUSTICS,
923     RENDERPASS_FOG,
924     RENDERPASS_LIGHTMAP_BLEND
925 };
926 
927 struct geombatch
928 {
929     const elementset &es;
930     VSlot &vslot;
931     ushort *edata;
932     vtxarray *va;
933     int next, batch;
934 
geombatchgeombatch935     geombatch(const elementset &es, ushort *edata, vtxarray *va)
936       : es(es), vslot(lookupvslot(es.texture)), edata(edata), va(va),
937         next(-1), batch(-1)
938     {}
939 
comparegeombatch940     int compare(const geombatch &b) const
941     {
942         if(va->vbuf < b.va->vbuf) return -1;
943         if(va->vbuf > b.va->vbuf) return 1;
944         if(va->dynlightmask < b.va->dynlightmask) return -1;
945         if(va->dynlightmask > b.va->dynlightmask) return 1;
946         if(vslot.slot->shader < b.vslot.slot->shader) return -1;
947         if(vslot.slot->shader > b.vslot.slot->shader) return 1;
948         if(vslot.slot->params.length() < b.vslot.slot->params.length()) return -1;
949         if(vslot.slot->params.length() > b.vslot.slot->params.length()) return 1;
950         if(es.texture < b.es.texture) return -1;
951         if(es.texture > b.es.texture) return 1;
952         if(es.lmid < b.es.lmid) return -1;
953         if(es.lmid > b.es.lmid) return 1;
954         if(es.envmap < b.es.envmap) return -1;
955         if(es.envmap > b.es.envmap) return 1;
956         if(es.dim < b.es.dim) return -1;
957         if(es.dim > b.es.dim) return 1;
958         return 0;
959     }
960 };
961 
962 static vector<geombatch> geombatches;
963 static int firstbatch = -1, numbatches = 0;
964 
mergetexs(renderstate & cur,vtxarray * va,elementset * texs=NULL,int numtexs=0,ushort * edata=NULL)965 static void mergetexs(renderstate &cur, vtxarray *va, elementset *texs = NULL, int numtexs = 0, ushort *edata = NULL)
966 {
967     if(!texs)
968     {
969         texs = va->eslist;
970         numtexs = va->texs;
971         edata = va->edata;
972         if(cur.alphaing)
973         {
974             texs += va->texs + va->blends;
975             edata += 3*(va->tris + va->blendtris);
976             numtexs = va->alphaback;
977             if(cur.alphaing > 1) numtexs += va->alphafront;
978         }
979     }
980 
981     if(firstbatch < 0)
982     {
983         firstbatch = geombatches.length();
984         numbatches = numtexs;
985         loopi(numtexs-1)
986         {
987             geombatches.add(geombatch(texs[i], edata, va)).next = i+1;
988             edata += texs[i].length[1];
989         }
990         geombatches.add(geombatch(texs[numtexs-1], edata, va));
991         return;
992     }
993 
994     int prevbatch = -1, curbatch = firstbatch, curtex = 0;
995     do
996     {
997         geombatch &b = geombatches.add(geombatch(texs[curtex], edata, va));
998         edata += texs[curtex].length[1];
999         int dir = -1;
1000         while(curbatch >= 0)
1001         {
1002             dir = b.compare(geombatches[curbatch]);
1003             if(dir <= 0) break;
1004             prevbatch = curbatch;
1005             curbatch = geombatches[curbatch].next;
1006         }
1007         if(!dir)
1008         {
1009             int last = curbatch, next;
1010             for(;;)
1011             {
1012                 next = geombatches[last].batch;
1013                 if(next < 0) break;
1014                 last = next;
1015             }
1016             if(last==curbatch)
1017             {
1018                 b.batch = curbatch;
1019                 b.next = geombatches[curbatch].next;
1020                 if(prevbatch < 0) firstbatch = geombatches.length()-1;
1021                 else geombatches[prevbatch].next = geombatches.length()-1;
1022                 curbatch = geombatches.length()-1;
1023             }
1024             else
1025             {
1026                 b.batch = next;
1027                 geombatches[last].batch = geombatches.length()-1;
1028             }
1029         }
1030         else
1031         {
1032             numbatches++;
1033             b.next = curbatch;
1034             if(prevbatch < 0) firstbatch = geombatches.length()-1;
1035             else geombatches[prevbatch].next = geombatches.length()-1;
1036             prevbatch = geombatches.length()-1;
1037         }
1038     }
1039     while(++curtex < numtexs);
1040 }
1041 
enablevattribs(renderstate & cur,bool all=true)1042 static inline void enablevattribs(renderstate &cur, bool all = true)
1043 {
1044     gle::enablevertex();
1045     if(all)
1046     {
1047         gle::enabletexcoord0();
1048         gle::enabletexcoord1();
1049         gle::enablenormal();
1050         gle::enabletangent();
1051     }
1052     cur.vattribs = true;
1053 }
1054 
disablevattribs(renderstate & cur,bool all=true)1055 static inline void disablevattribs(renderstate &cur, bool all = true)
1056 {
1057     gle::disablevertex();
1058     if(all)
1059     {
1060         gle::disabletexcoord0();
1061         gle::disabletexcoord1();
1062         gle::disablenormal();
1063         gle::disabletangent();
1064     }
1065     cur.vattribs = false;
1066 }
1067 
changevbuf(renderstate & cur,int pass,vtxarray * va)1068 static void changevbuf(renderstate &cur, int pass, vtxarray *va)
1069 {
1070     gle::bindvbo(va->vbuf);
1071     gle::bindebo(va->ebuf);
1072     cur.vbuf = va->vbuf;
1073 
1074     vertex *vdata = (vertex *)0;
1075     gle::vertexpointer(sizeof(vertex), vdata->pos.v);
1076 
1077     if(pass==RENDERPASS_LIGHTMAP)
1078     {
1079         gle::normalpointer(sizeof(vertex), vdata->norm.v, GL_BYTE);
1080         gle::texcoord0pointer(sizeof(vertex), vdata->tc.v);
1081         gle::texcoord1pointer(sizeof(vertex), vdata->lm.v, GL_SHORT);
1082         gle::tangentpointer(sizeof(vertex), vdata->tangent.v, GL_BYTE);
1083     }
1084 }
1085 
changebatchtmus(renderstate & cur,int pass,geombatch & b)1086 static void changebatchtmus(renderstate &cur, int pass, geombatch &b)
1087 {
1088     bool changed = false;
1089     extern bool brightengeom;
1090     int lmid = brightengeom && (b.es.lmid < LMID_RESERVED || (fullbright && editmode)) ? LMID_BRIGHT : b.es.lmid;
1091     if(cur.textures[1]!=lightmaptexs[lmid].id)
1092     {
1093         glActiveTexture_(GL_TEXTURE1);
1094         glBindTexture(GL_TEXTURE_2D, cur.textures[1] = lightmaptexs[lmid].id);
1095         changed = true;
1096     }
1097     int tmu = 2;
1098     if(b.vslot.slot->shader->type&SHADER_NORMALSLMS)
1099     {
1100         if(cur.textures[tmu]!=lightmaptexs[lmid+1].id)
1101         {
1102             glActiveTexture_(GL_TEXTURE0+tmu);
1103             glBindTexture(GL_TEXTURE_2D, cur.textures[tmu] = lightmaptexs[lmid+1].id);
1104             changed = true;
1105         }
1106         tmu++;
1107     }
1108     if(b.vslot.slot->shader->type&SHADER_ENVMAP && b.es.envmap!=EMID_CUSTOM)
1109     {
1110         GLuint emtex = lookupenvmap(b.es.envmap);
1111         if(cur.textures[tmu]!=emtex)
1112         {
1113             glActiveTexture_(GL_TEXTURE0+tmu);
1114             glBindTexture(GL_TEXTURE_CUBE_MAP, cur.textures[tmu] = emtex);
1115             changed = true;
1116         }
1117     }
1118     if(changed) glActiveTexture_(GL_TEXTURE0);
1119 
1120     if(cur.dynlightmask != b.va->dynlightmask)
1121     {
1122         cur.visibledynlights = setdynlights(b.va);
1123         cur.dynlightmask = b.va->dynlightmask;
1124     }
1125 }
1126 
1127 VAR(0, blankgeom, 0, 0, 1);
1128 vec lightmapcolor(0, 0, 0);
1129 VARF(IDF_HEX, lightmapcolour, 0, 0, 0xFFFFFF, lightmapcolor = vec::hexcolor(lightmapcolour));
1130 FVAR(0, lightmapintensity, 0, 1, 10);
1131 
changeslottmus(renderstate & cur,int pass,Slot & slot,VSlot & vslot)1132 static void changeslottmus(renderstate &cur, int pass, Slot &slot, VSlot &vslot)
1133 {
1134     vec colorscale = vslot.getcolorscale();
1135     if(pass==RENDERPASS_LIGHTMAP)
1136     {
1137         GLuint diffusetex = blankgeom ? blanktexture->id : (slot.sts.empty() ? notexture->id : slot.sts[0].t->id);
1138         if(cur.textures[0]!=diffusetex)
1139             glBindTexture(GL_TEXTURE_2D, cur.textures[0] = diffusetex);
1140 
1141         if(lightmapcolor != vec(0, 0, 0)) colorscale = lightmapcolor;
1142         if(lightmapintensity != 1.f) colorscale.mul(lightmapintensity);
1143     }
1144     if(cur.alphaing)
1145     {
1146         float alpha = cur.alphaing > 1 ? vslot.alphafront : vslot.alphaback;
1147         if(cur.colorscale != colorscale || cur.alphascale != alpha)
1148         {
1149             cur.colorscale = colorscale;
1150             cur.alphascale = alpha;
1151             GLOBALPARAMF(colorparams, 2*alpha*colorscale.x, 2*alpha*colorscale.y, 2*alpha*colorscale.z, alpha);
1152             setfogcolor(vec(curfogcolor).mul(alpha));
1153         }
1154     }
1155     else if(cur.colorscale != colorscale)
1156     {
1157         cur.colorscale = colorscale;
1158         GLOBALPARAMF(colorparams, 2*colorscale.x, 2*colorscale.y, 2*colorscale.z, 1);
1159     }
1160     int tmu = 2, envmaptmu = -1;
1161     if(slot.shader->type&SHADER_NORMALSLMS) tmu++;
1162     if(slot.shader->type&SHADER_ENVMAP) envmaptmu = tmu++;
1163     loopvj(slot.sts)
1164     {
1165         Slot::Tex &t = slot.sts[j];
1166         if(t.type==TEX_DIFFUSE || t.combined>=0) continue;
1167         if(t.type==TEX_ENVMAP)
1168         {
1169             if(envmaptmu>=0 && t.t && cur.textures[envmaptmu]!=t.t->id)
1170             {
1171                 glActiveTexture_(GL_TEXTURE0+envmaptmu);
1172                 glBindTexture(GL_TEXTURE_CUBE_MAP, cur.textures[envmaptmu] = t.t->id);
1173             }
1174         }
1175         else
1176         {
1177             if(cur.textures[tmu]!=t.t->id)
1178             {
1179                 glActiveTexture_(GL_TEXTURE0+tmu);
1180                 glBindTexture(GL_TEXTURE_2D, cur.textures[tmu] = t.t->id);
1181             }
1182             if(++tmu >= 8) break;
1183         }
1184     }
1185     glActiveTexture_(GL_TEXTURE0);
1186 
1187     cur.slot = &slot;
1188     cur.vslot = &vslot;
1189 }
1190 
changeshader(renderstate & cur,Shader * s,Slot & slot,VSlot & vslot,bool shadowed)1191 static void changeshader(renderstate &cur, Shader *s, Slot &slot, VSlot &vslot, bool shadowed)
1192 {
1193     if(glaring)
1194     {
1195         static Shader *noglareshader = NULL, *noglareblendshader = NULL, *noglarealphashader = NULL;
1196         Shader *fallback;
1197         if(cur.blending) { if(!noglareblendshader) noglareblendshader = lookupshaderbyname("noglareblendworld"); fallback = noglareblendshader; }
1198         else if(cur.alphaing) { if(!noglarealphashader) noglarealphashader = lookupshaderbyname("noglarealphaworld"); fallback = noglarealphashader; }
1199         else { if(!noglareshader) noglareshader = lookupshaderbyname("noglareworld"); fallback = noglareshader; }
1200         if(s->hasoption(4)) s->setvariant(cur.visibledynlights, 4, slot, vslot, fallback);
1201         else s->setvariant(cur.blending ? 1 : 0, 4, slot, vslot, fallback);
1202     }
1203     else if(fading && !cur.blending && !cur.alphaing)
1204     {
1205         if(shadowed) s->setvariant(cur.visibledynlights, 3, slot, vslot);
1206         else s->setvariant(cur.visibledynlights, 2, slot, vslot);
1207     }
1208     else if(shadowed) s->setvariant(cur.visibledynlights, 1, slot, vslot);
1209     else if(!cur.visibledynlights) s->set(slot, vslot);
1210     else s->setvariant(cur.visibledynlights-1, 0, slot, vslot);
1211 }
1212 
changetexgen(renderstate & cur,int dim,Slot & slot,VSlot & vslot)1213 static void changetexgen(renderstate &cur, int dim, Slot &slot, VSlot &vslot)
1214 {
1215     if(cur.texgenslot != &slot || cur.texgenvslot != &vslot)
1216     {
1217         Texture *curtex = !cur.texgenslot || cur.texgenslot->sts.empty() ? notexture : cur.texgenslot->sts[0].t,
1218                 *tex = slot.sts.empty() ? notexture : slot.sts[0].t;
1219         if(!cur.texgenvslot || slot.sts.empty() ||
1220             (curtex->xs != tex->xs || curtex->ys != tex->ys ||
1221              cur.texgenvslot->rotation != vslot.rotation || cur.texgenvslot->scale != vslot.scale ||
1222              cur.texgenvslot->offset != vslot.offset || cur.texgenvslot->scroll != vslot.scroll))
1223         {
1224             float xs = vslot.rotation>=2 && vslot.rotation<=4 ? -tex->xs : tex->xs,
1225                   ys = (vslot.rotation>=1 && vslot.rotation<=2) || vslot.rotation==5 ? -tex->ys : tex->ys;
1226             vec2 scroll(vslot.scroll);
1227             if((vslot.rotation&5)==1) swap(scroll.x, scroll.y);
1228             scroll.x *= lastmillis*tex->xs/xs;
1229             scroll.y *= lastmillis*tex->ys/ys;
1230             if(cur.texgenscroll != scroll)
1231             {
1232                 cur.texgenscroll = scroll;
1233                 cur.texgendim = -1;
1234             }
1235         }
1236         cur.texgenslot = &slot;
1237         cur.texgenvslot = &vslot;
1238     }
1239 
1240     if(cur.texgendim == dim) return;
1241     GLOBALPARAM(texgenscroll, cur.texgenscroll);
1242     cur.texgendim = dim;
1243 }
1244 
renderbatch(renderstate & cur,int pass,geombatch & b)1245 static void renderbatch(renderstate &cur, int pass, geombatch &b)
1246 {
1247     geombatch *shadowed = NULL;
1248     int rendered = -1;
1249     for(geombatch *curbatch = &b;; curbatch = &geombatches[curbatch->batch])
1250     {
1251         ushort len = curbatch->es.length[curbatch->va->shadowed ? 0 : 1];
1252         if(len)
1253         {
1254             if(rendered < 0)
1255             {
1256                 changeshader(cur, b.vslot.slot->shader, *b.vslot.slot, b.vslot, false);
1257                 rendered = 0;
1258                 gbatches++;
1259             }
1260             ushort minvert = curbatch->es.minvert[0], maxvert = curbatch->es.maxvert[0];
1261             if(!curbatch->va->shadowed) { minvert = min(minvert, curbatch->es.minvert[1]); maxvert = max(maxvert, curbatch->es.maxvert[1]); }
1262             drawtris(len, curbatch->edata, minvert, maxvert);
1263             vtris += len/3;
1264         }
1265         if(curbatch->es.length[1] > len && !shadowed) shadowed = curbatch;
1266         if(curbatch->batch < 0) break;
1267     }
1268     if(shadowed) for(geombatch *curbatch = shadowed;; curbatch = &geombatches[curbatch->batch])
1269     {
1270         if(curbatch->va->shadowed && curbatch->es.length[1] > curbatch->es.length[0])
1271         {
1272             if(rendered < 1)
1273             {
1274                 changeshader(cur, b.vslot.slot->shader, *b.vslot.slot, b.vslot, true);
1275                 rendered = 1;
1276                 gbatches++;
1277             }
1278             ushort len = curbatch->es.length[1] - curbatch->es.length[0];
1279             drawtris(len, curbatch->edata + curbatch->es.length[0], curbatch->es.minvert[1], curbatch->es.maxvert[1]);
1280             vtris += len/3;
1281         }
1282         if(curbatch->batch < 0) break;
1283     }
1284 }
1285 
resetbatches()1286 static void resetbatches()
1287 {
1288     geombatches.setsize(0);
1289     firstbatch = -1;
1290     numbatches = 0;
1291 }
1292 
renderbatches(renderstate & cur,int pass)1293 static void renderbatches(renderstate &cur, int pass)
1294 {
1295     cur.slot = NULL;
1296     cur.vslot = NULL;
1297     int curbatch = firstbatch;
1298     if(curbatch >= 0)
1299     {
1300         if(cur.alphaing)
1301         {
1302             if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
1303         }
1304         else if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
1305         if(!cur.colormask) { cur.colormask = true; glColorMask(COLORMASK, cur.alphaing ? GL_FALSE : GL_TRUE); }
1306         if(!cur.vattribs)
1307         {
1308             if(cur.vquery) disablevquery(cur);
1309             enablevattribs(cur);
1310         }
1311     }
1312     while(curbatch >= 0)
1313     {
1314         geombatch &b = geombatches[curbatch];
1315         curbatch = b.next;
1316 
1317         if(cur.vbuf != b.va->vbuf) changevbuf(cur, pass, b.va);
1318         if(cur.vslot != &b.vslot)
1319         {
1320             changeslottmus(cur, pass, *b.vslot.slot, b.vslot);
1321             if(cur.texgendim != b.es.dim || (cur.texgendim <= 2 && cur.texgenvslot != &b.vslot)) changetexgen(cur, b.es.dim, *b.vslot.slot, b.vslot);
1322         }
1323         else if(cur.texgendim != b.es.dim) changetexgen(cur, b.es.dim, *b.vslot.slot, b.vslot);
1324         if(pass == RENDERPASS_LIGHTMAP) changebatchtmus(cur, pass, b);
1325 
1326         renderbatch(cur, pass, b);
1327     }
1328 
1329     resetbatches();
1330 }
1331 
renderzpass(renderstate & cur,vtxarray * va)1332 void renderzpass(renderstate &cur, vtxarray *va)
1333 {
1334     if(!cur.vattribs)
1335     {
1336         if(cur.vquery) disablevquery(cur);
1337         enablevattribs(cur, false);
1338     }
1339     if(cur.vbuf!=va->vbuf) changevbuf(cur, RENDERPASS_Z, va);
1340     if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
1341     if(cur.colormask) { cur.colormask = false; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); }
1342     int firsttex = 0, numtris = va->tris;
1343     ushort *edata = va->edata;
1344     if(cur.alphaing)
1345     {
1346         firsttex += va->texs + va->blends;
1347         edata += 3*(va->tris + va->blendtris);
1348         numtris = va->alphabacktris + va->alphafronttris;
1349         xtravertsva += 3*numtris;
1350     }
1351     else xtravertsva += va->verts;
1352     nocolorshader->set();
1353     drawvatris(va, 3*numtris, edata);
1354 }
1355 
1356 vector<vtxarray *> foggedvas;
1357 
1358 #define startvaquery(va, flush) \
1359     do { \
1360         if(va->query) \
1361         { \
1362             flush; \
1363             startquery(va->query); \
1364         } \
1365     } while(0)
1366 
1367 
1368 #define endvaquery(va, flush) \
1369     do { \
1370         if(va->query) \
1371         { \
1372             flush; \
1373             endquery(va->query); \
1374         } \
1375     } while(0)
1376 
renderfoggedvas(renderstate & cur,bool doquery=false)1377 void renderfoggedvas(renderstate &cur, bool doquery = false)
1378 {
1379     static Shader *fogshader = NULL;
1380     if(!fogshader) fogshader = lookupshaderbyname("fogworld");
1381     if(fading) fogshader->setvariant(0, 2);
1382     else fogshader->set();
1383 
1384     if(!cur.vattribs) enablevattribs(cur, false);
1385 
1386     loopv(foggedvas)
1387     {
1388         vtxarray *va = foggedvas[i];
1389         if(cur.vbuf!=va->vbuf) changevbuf(cur, RENDERPASS_FOG, va);
1390 
1391         if(doquery) startvaquery(va, );
1392         drawvatris(va, 3*va->tris, va->edata);
1393         vtris += va->tris;
1394         if(doquery) endvaquery(va, );
1395     }
1396 
1397     foggedvas.setsize(0);
1398 }
1399 
1400 VAR(0, batchgeom, 0, 1, 1);
1401 
renderva(renderstate & cur,vtxarray * va,int pass=RENDERPASS_LIGHTMAP,bool fogpass=false,bool doquery=false)1402 void renderva(renderstate &cur, vtxarray *va, int pass = RENDERPASS_LIGHTMAP, bool fogpass = false, bool doquery = false)
1403 {
1404     switch(pass)
1405     {
1406         case RENDERPASS_LIGHTMAP:
1407             if(!cur.alphaing) vverts += va->verts;
1408             va->shadowed = false;
1409             va->dynlightmask = 0;
1410             if(fogpass ? va->geommax.z<=reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED)
1411             {
1412                 if(!cur.alphaing && !cur.blending) foggedvas.add(va);
1413                 break;
1414             }
1415             if(!drawtex && !glaring && !cur.alphaing)
1416             {
1417                 va->shadowed = isshadowmapreceiver(va);
1418                 calcdynlightmask(va);
1419             }
1420             if(doquery) startvaquery(va, { if(geombatches.length()) renderbatches(cur, pass); });
1421             mergetexs(cur, va);
1422             if(doquery) endvaquery(va, { if(geombatches.length()) renderbatches(cur, pass); });
1423             else if(!batchgeom && geombatches.length()) renderbatches(cur, pass);
1424             break;
1425 
1426         case RENDERPASS_LIGHTMAP_BLEND:
1427         {
1428             if(doquery) startvaquery(va, { if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP); });
1429             mergetexs(cur, va, &va->eslist[va->texs], va->blends, va->edata + 3*va->tris);
1430             if(doquery) endvaquery(va, { if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP); });
1431             else if(!batchgeom && geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1432             break;
1433         }
1434 
1435         case RENDERPASS_FOG:
1436             if(cur.vbuf!=va->vbuf) changevbuf(cur, pass, va);
1437             drawvatris(va, 3*va->tris, va->edata);
1438             xtravertsva += va->verts;
1439             break;
1440 
1441         case RENDERPASS_CAUSTICS:
1442             if(cur.vbuf!=va->vbuf) changevbuf(cur, pass, va);
1443             drawvatris(va, 3*va->tris, va->edata);
1444             xtravertsva += va->verts;
1445             break;
1446 
1447         case RENDERPASS_Z:
1448             if(doquery) startvaquery(va, );
1449             renderzpass(cur, va);
1450             if(doquery) endvaquery(va, );
1451             break;
1452     }
1453 }
1454 
1455 #define NUMCAUSTICS 32
1456 
1457 static Texture *caustictex[NUMCAUSTICS] = { NULL };
1458 
loadcaustics(bool force)1459 void loadcaustics(bool force)
1460 {
1461     static bool needcaustics = false;
1462     if(force) needcaustics = true;
1463     if(!caustics || !needcaustics) return;
1464     useshaderbyname("caustic");
1465     if(caustictex[0]) return;
1466     loopi(NUMCAUSTICS)
1467     {
1468         defformatstring(name, "<grey><noswizzle>caustics/caust%.2d.png", i);
1469         caustictex[i] = textureload(name);
1470     }
1471 }
1472 
cleanupva()1473 void cleanupva()
1474 {
1475     clearvas(worldroot);
1476     clearqueries();
1477     cleanupbb();
1478     cleanupgrass();
1479     loopi(NUMCAUSTICS) caustictex[i] = NULL;
1480 }
1481 
1482 VAR(IDF_WORLD, causticscale, 1, 50, 10000);
1483 VAR(IDF_WORLD, causticmillis, 1, 75, 1000);
1484 FVAR(IDF_WORLD, causticcontrast, 0, 0.6f, 1);
1485 VARF(IDF_PERSIST, caustics, 0, 1, 1, loadcaustics());
1486 
setupcaustics(float blend)1487 void setupcaustics(float blend)
1488 {
1489     if(!caustictex[0]) loadcaustics(true);
1490 
1491     vec s = vec(0.011f, 0, 0.0066f).mul(100.0f/causticscale), t = vec(0, 0.011f, 0.0066f).mul(100.0f/causticscale);
1492     int tex = (lastmillis/causticmillis)%NUMCAUSTICS;
1493     float frac = float(lastmillis%causticmillis)/causticmillis;
1494     loopi(2)
1495     {
1496         glActiveTexture_(GL_TEXTURE0+i);
1497         glBindTexture(GL_TEXTURE_2D, caustictex[(tex+i)%NUMCAUSTICS]->id);
1498     }
1499     glActiveTexture_(GL_TEXTURE0);
1500     SETSHADER(caustic);
1501     LOCALPARAM(texgenS, s);
1502     LOCALPARAM(texgenT, t);
1503     blend *= causticcontrast;
1504     LOCALPARAMF(frameblend, blend*(1-frac), blend*frac, blend, 1 - blend);
1505 }
1506 
setupgeom(renderstate & cur)1507 void setupgeom(renderstate &cur)
1508 {
1509     GLOBALPARAMF(colorparams, 2, 2, 2, 1);
1510     GLOBALPARAM(camera, camera1->o);
1511     GLOBALPARAMF(ambient, ambientcolor.x/255.0f, ambientcolor.y/255.0f, ambientcolor.z/255.0f);
1512     GLOBALPARAMF(millis, lastmillis/1000.0f);
1513 
1514     glActiveTexture_(GL_TEXTURE0);
1515 }
1516 
cleanupgeom(renderstate & cur)1517 void cleanupgeom(renderstate &cur)
1518 {
1519     if(cur.vattribs) disablevattribs(cur);
1520     if(cur.vbuf) disablevbuf(cur);
1521 }
1522 
1523 #define FIRSTVA (reflecting ? reflectedva : visibleva)
1524 #define NEXTVA (reflecting ? va->rnext : va->next)
1525 
rendergeommultipass(renderstate & cur,int pass,bool fogpass)1526 static void rendergeommultipass(renderstate &cur, int pass, bool fogpass)
1527 {
1528     if(cur.vbuf) disablevbuf(cur);
1529     if(!cur.vattribs) enablevattribs(cur, false);
1530     cur.texgendim = -1;
1531     for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
1532     {
1533         if(!va->texs) continue;
1534         if(refracting)
1535         {
1536             if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_GEOM) continue;
1537             if(ishiddencube(va->o, va->size)) continue;
1538         }
1539         else if(reflecting)
1540         {
1541             if(va->geommax.z <= reflectz) continue;
1542         }
1543         else if(va->occluded >= OCCLUDE_GEOM) continue;
1544         if(fogpass ? va->geommax.z <= reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED) continue;
1545         renderva(cur, va, pass, fogpass);
1546     }
1547     if(geombatches.length()) renderbatches(cur, pass);
1548 }
1549 
1550 VAR(0, oqgeom, 0, 1, 1);
1551 
rendergeom(float causticspass,bool fogpass)1552 void rendergeom(float causticspass, bool fogpass)
1553 {
1554     if(causticspass && !causticscale) causticspass = 0;
1555 
1556     bool mainpass = !reflecting && !refracting && !drawtex && !glaring,
1557          doOQ = oqfrags && oqgeom && mainpass,
1558          doZP = doOQ && zpass,
1559          doSM = shadowmap && !drawtex && !glaring;
1560     renderstate cur;
1561     if(mainpass)
1562     {
1563         flipqueries();
1564         vtris = vverts = 0;
1565     }
1566     if(!doZP)
1567     {
1568         if(shadowmap && mainpass) rendershadowmap();
1569         setupgeom(cur);
1570         if(doSM) pushshadowmap();
1571     }
1572 
1573     finddynlights();
1574 
1575     resetbatches();
1576 
1577     int blends = 0;
1578     for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
1579     {
1580         if(!va->texs) continue;
1581         if(refracting)
1582         {
1583             if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_GEOM) continue;
1584             if(ishiddencube(va->o, va->size)) continue;
1585         }
1586         else if(reflecting)
1587         {
1588             if(va->geommax.z <= reflectz) continue;
1589         }
1590         else if(doOQ && (zpass || va->distance > oqdist) && !insideva(va, camera1->o))
1591         {
1592             if(va->parent && va->parent->occluded >= OCCLUDE_BB)
1593             {
1594                 va->query = NULL;
1595                 va->occluded = OCCLUDE_PARENT;
1596                 continue;
1597             }
1598             va->occluded = va->query && va->query->owner == va && checkquery(va->query) ? min(va->occluded+1, int(OCCLUDE_BB)) : OCCLUDE_NOTHING;
1599             va->query = newquery(va);
1600             if((!va->query && zpass) || !va->occluded)
1601                 va->occluded = pvsoccluded(va->geommin, va->geommax) ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
1602             if(va->occluded >= OCCLUDE_GEOM)
1603             {
1604                 if(va->query)
1605                 {
1606                     if(!zpass && geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1607                     if(cur.vattribs) disablevattribs(cur, !doZP);
1608                     if(cur.vbuf) disablevbuf(cur);
1609                     renderquery(cur, va->query, va);
1610                 }
1611                 continue;
1612             }
1613         }
1614         else
1615         {
1616             va->query = NULL;
1617             va->occluded = pvsoccluded(va->geommin, va->geommax) ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
1618             if(va->occluded >= OCCLUDE_GEOM) continue;
1619         }
1620 
1621         if(!doZP) blends += va->blends;
1622         renderva(cur, va, doZP ? RENDERPASS_Z : RENDERPASS_LIGHTMAP, fogpass, doOQ);
1623     }
1624 
1625     if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1626 
1627     if(cur.vquery) disablevquery(cur);
1628     if(cur.vattribs) disablevattribs(cur, !doZP);
1629     if(cur.vbuf) disablevbuf(cur);
1630 
1631     if(!cur.colormask) { cur.colormask = true; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); }
1632     if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
1633 
1634     bool multipassing = false;
1635 
1636     if(doZP)
1637     {
1638         glFlush();
1639 
1640         if(shadowmap && mainpass) rendershadowmap();
1641         setupgeom(cur);
1642         if(doSM) pushshadowmap();
1643 
1644         if(!multipassing) { multipassing = true; glDepthFunc(GL_LEQUAL); }
1645         cur.texgendim = -1;
1646 
1647         for(vtxarray *va = visibleva; va; va = va->next)
1648         {
1649             if(!va->texs || va->occluded >= OCCLUDE_GEOM) continue;
1650             blends += va->blends;
1651             renderva(cur, va, RENDERPASS_LIGHTMAP, fogpass);
1652         }
1653         if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1654         for(vtxarray *va = visibleva; va; va = va->next)
1655         {
1656             if(!va->texs || va->occluded < OCCLUDE_GEOM) continue;
1657             else if((va->parent && va->parent->occluded >= OCCLUDE_BB) ||
1658                     (va->query && checkquery(va->query)))
1659             {
1660                 va->occluded = OCCLUDE_BB;
1661                 continue;
1662             }
1663             else
1664             {
1665                 va->occluded = pvsoccluded(va->geommin, va->geommax) ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
1666                 if(va->occluded >= OCCLUDE_GEOM) continue;
1667             }
1668 
1669             blends += va->blends;
1670             renderva(cur, va, RENDERPASS_LIGHTMAP, fogpass);
1671         }
1672         if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1673     }
1674 
1675     if(blends)
1676     {
1677         if(cur.vbuf) disablevbuf(cur);
1678 
1679         if(!multipassing) { multipassing = true; glDepthFunc(GL_LEQUAL); }
1680         glDepthMask(GL_FALSE);
1681         glEnable(GL_BLEND);
1682         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1683         glColorMask(COLORMASK, GL_FALSE);
1684 
1685         cur.texgendim = -1;
1686         cur.blending = true;
1687         for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
1688         {
1689             if(!va->blends) continue;
1690             if(refracting)
1691             {
1692                 if(refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) continue;
1693                 if(ishiddencube(va->o, va->size)) continue;
1694                 if(va->occluded >= OCCLUDE_GEOM) continue;
1695             }
1696             else if(reflecting)
1697             {
1698                 if(va->geommax.z <= reflectz) continue;
1699             }
1700             else if(va->occluded >= OCCLUDE_GEOM) continue;
1701             if(fogpass ? va->geommax.z <= reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED) continue;
1702             renderva(cur, va, RENDERPASS_LIGHTMAP_BLEND, fogpass);
1703         }
1704         if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1705         cur.blending = false;
1706 
1707         glColorMask(COLORMASK, GL_TRUE);
1708         glDisable(GL_BLEND);
1709         glDepthMask(GL_TRUE);
1710     }
1711 
1712     if(doSM) popshadowmap();
1713 
1714     if(cur.vattribs) disablevattribs(cur);
1715 
1716     if(foggedvas.length()) renderfoggedvas(cur, doOQ && !zpass);
1717 
1718     if(causticspass)
1719     {
1720         if(!multipassing) { multipassing = true; glDepthFunc(GL_LEQUAL); }
1721         glDepthMask(GL_FALSE);
1722         glEnable(GL_BLEND);
1723 
1724         setupcaustics(causticspass);
1725         glBlendFunc(GL_ZERO, GL_SRC_COLOR);
1726         if(fading) glColorMask(COLORMASK, GL_FALSE);
1727         rendergeommultipass(cur, RENDERPASS_CAUSTICS, fogpass);
1728         if(fading) glColorMask(COLORMASK, GL_TRUE);
1729 
1730         glDisable(GL_BLEND);
1731         glDepthMask(GL_TRUE);
1732     }
1733 
1734     if(multipassing) glDepthFunc(GL_LESS);
1735 
1736     cleanupgeom(cur);
1737 }
1738 
renderalphageom(bool fogpass)1739 void renderalphageom(bool fogpass)
1740 {
1741     static vector<vtxarray *> alphavas;
1742     alphavas.setsize(0);
1743     bool hasback = false;
1744     for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
1745     {
1746         if(!va->alphabacktris && !va->alphafronttris) continue;
1747         if(refracting)
1748         {
1749             if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_BB) continue;
1750             if(ishiddencube(va->o, va->size)) continue;
1751             if(va->occluded >= OCCLUDE_GEOM && pvsoccluded(va->geommin, va->geommax)) continue;
1752         }
1753         else if(reflecting)
1754         {
1755             if(va->geommax.z <= reflectz) continue;
1756         }
1757         else
1758         {
1759             if(va->occluded >= OCCLUDE_BB) continue;
1760             if(va->occluded >= OCCLUDE_GEOM && pvsoccluded(va->geommin, va->geommax)) continue;
1761         }
1762         if(fogpass ? va->geommax.z <= reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED) continue;
1763         alphavas.add(va);
1764         if(va->alphabacktris) hasback = true;
1765     }
1766     if(alphavas.empty()) return;
1767 
1768     resetbatches();
1769 
1770     renderstate cur;
1771     cur.alphaing = 1;
1772 
1773     loop(front, 2) if(front || hasback)
1774     {
1775         cur.alphaing = front+1;
1776         if(!front) glCullFace(GL_FRONT);
1777         cur.texgendim = -1;
1778         loopv(alphavas) renderva(cur, alphavas[i], RENDERPASS_Z);
1779         if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
1780         cur.colormask = true;
1781         glColorMask(COLORMASK, GL_FALSE);
1782 
1783         if(cur.vattribs) disablevattribs(cur, false);
1784         if(cur.vbuf) disablevbuf(cur);
1785 
1786         setupgeom(cur);
1787 
1788         glDepthFunc(GL_LEQUAL);
1789         glEnable(GL_BLEND);
1790         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1791         cur.texgendim = -1;
1792         cur.colorscale = vec(1, 1, 1);
1793         cur.alphascale = -1;
1794         loopv(alphavas) if(front || alphavas[i]->alphabacktris) renderva(cur, alphavas[i], RENDERPASS_LIGHTMAP, fogpass);
1795         if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1796 
1797         cleanupgeom(cur);
1798 
1799         resetfogcolor();
1800         if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
1801         glDisable(GL_BLEND);
1802         glDepthFunc(GL_LESS);
1803         if(!front) glCullFace(GL_BACK);
1804     }
1805 
1806     glColorMask(COLORMASK, fading ? GL_FALSE : GL_TRUE);
1807 
1808     cleanupgeom(cur);
1809 }
1810 
findreflectedvas(vector<vtxarray * > & vas,int prevvfc=VFC_PART_VISIBLE)1811 void findreflectedvas(vector<vtxarray *> &vas, int prevvfc = VFC_PART_VISIBLE)
1812 {
1813     loopv(vas)
1814     {
1815         vtxarray *va = vas[i];
1816         if(prevvfc >= VFC_NOT_VISIBLE) va->curvfc = prevvfc;
1817         if(va->curvfc == VFC_FOGGED || va->curvfc == PVS_FOGGED || va->o.z+va->size <= reflectz || isfoggedcube(va->o, va->size)) continue;
1818         bool render = true;
1819         if(va->curvfc == VFC_FULL_VISIBLE)
1820         {
1821             if(va->occluded >= OCCLUDE_BB) continue;
1822             if(va->occluded >= OCCLUDE_GEOM) render = false;
1823         }
1824         else if(va->curvfc == PVS_FULL_VISIBLE) continue;
1825         if(render)
1826         {
1827             if(va->curvfc >= VFC_NOT_VISIBLE) va->distance = (int)vadist(va, camera1->o);
1828             vtxarray **vprev = &reflectedva, *vcur = reflectedva;
1829             while(vcur && va->distance > vcur->distance)
1830             {
1831                 vprev = &vcur->rnext;
1832                 vcur = vcur->rnext;
1833             }
1834             va->rnext = *vprev;
1835             *vprev = va;
1836         }
1837         if(va->children.length()) findreflectedvas(va->children, va->curvfc);
1838     }
1839 }
1840 
renderreflectedgeom(bool causticspass,bool fogpass)1841 void renderreflectedgeom(bool causticspass, bool fogpass)
1842 {
1843     if(reflecting)
1844     {
1845         reflectedva = NULL;
1846         findreflectedvas(varoot);
1847         rendergeom(causticspass ? 1 : 0, fogpass);
1848     }
1849     else rendergeom(causticspass ? 1 : 0, fogpass);
1850 }
1851 
1852 static vtxarray *prevskyva = NULL;
1853 
renderskyva(vtxarray * va,bool explicitonly=false)1854 void renderskyva(vtxarray *va, bool explicitonly = false)
1855 {
1856     if(!prevskyva || va->vbuf != prevskyva->vbuf)
1857     {
1858         gle::bindvbo(va->vbuf);
1859         gle::bindebo(va->skybuf);
1860         const vertex *ptr = 0;
1861         gle::vertexpointer(sizeof(vertex), ptr->pos.v);
1862         if(!prevskyva) gle::enablevertex();
1863     }
1864 
1865     drawvatris(va, explicitonly ? va->explicitsky : va->sky+va->explicitsky, explicitonly ? va->skydata+va->sky : va->skydata);
1866 
1867     if(!explicitonly) xtraverts += va->sky/3;
1868     xtraverts += va->explicitsky/3;
1869 
1870     prevskyva = va;
1871 }
1872 
1873 int renderedsky = 0, renderedexplicitsky = 0, renderedskyfaces = 0, renderedskyclip = INT_MAX;
1874 
updateskystats(vtxarray * va)1875 static inline void updateskystats(vtxarray *va)
1876 {
1877     renderedsky += va->sky;
1878     renderedexplicitsky += va->explicitsky;
1879     renderedskyfaces |= va->skyfaces&0x3F;
1880     if(!(va->skyfaces&0x1F) || camera1->o.z < va->skyclip) renderedskyclip = min(renderedskyclip, va->skyclip);
1881     else renderedskyclip = 0;
1882 }
1883 
renderreflectedskyvas(vector<vtxarray * > & vas,int prevvfc=VFC_PART_VISIBLE)1884 void renderreflectedskyvas(vector<vtxarray *> &vas, int prevvfc = VFC_PART_VISIBLE)
1885 {
1886     loopv(vas)
1887     {
1888         vtxarray *va = vas[i];
1889         if(prevvfc >= VFC_NOT_VISIBLE) va->curvfc = prevvfc;
1890         if((va->curvfc == VFC_FULL_VISIBLE && va->occluded >= OCCLUDE_BB) || va->curvfc==PVS_FULL_VISIBLE) continue;
1891         if(va->o.z+va->size <= reflectz || ishiddencube(va->o, va->size)) continue;
1892         if(va->sky+va->explicitsky)
1893         {
1894             updateskystats(va);
1895             renderskyva(va);
1896         }
1897         if(va->children.length()) renderreflectedskyvas(va->children, va->curvfc);
1898     }
1899 }
1900 
rendersky(bool explicitonly)1901 bool rendersky(bool explicitonly)
1902 {
1903     prevskyva = NULL;
1904     renderedsky = renderedexplicitsky = renderedskyfaces = 0;
1905     renderedskyclip = INT_MAX;
1906 
1907     if(reflecting)
1908     {
1909         renderreflectedskyvas(varoot);
1910     }
1911     else for(vtxarray *va = visibleva; va; va = va->next)
1912     {
1913         if((va->occluded >= OCCLUDE_BB && va->skyfaces&0x80) || !(va->sky+va->explicitsky)) continue;
1914 
1915         // count possibly visible sky even if not actually rendered
1916         updateskystats(va);
1917         if(explicitonly && !va->explicitsky) continue;
1918         renderskyva(va, explicitonly);
1919     }
1920 
1921     if(prevskyva)
1922     {
1923         gle::disablevertex();
1924         gle::clearvbo();
1925         gle::clearebo();
1926     }
1927 
1928     return renderedsky+renderedexplicitsky > 0;
1929 }
1930 
1931