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