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