Line data Source code
1 : // renderva.cpp: handles the occlusion and rendering of vertex arrays
2 :
3 : #include "../libprimis-headers/cube.h"
4 : #include "../../shared/geomexts.h"
5 : #include "../../shared/glemu.h"
6 : #include "../../shared/glexts.h"
7 :
8 : #include <memory>
9 : #include <optional>
10 :
11 : #include "csm.h"
12 : #include "grass.h"
13 : #include "octarender.h"
14 : #include "radiancehints.h"
15 : #include "rendergl.h"
16 : #include "renderlights.h"
17 : #include "rendermodel.h"
18 : #include "renderva.h"
19 : #include "renderwindow.h"
20 : #include "rendersky.h"
21 : #include "shaderparam.h"
22 : #include "shader.h"
23 : #include "texture.h"
24 :
25 : #include "interface/control.h"
26 :
27 : #include "world/entities.h"
28 : #include "world/light.h"
29 : #include "world/octaedit.h"
30 : #include "world/octaworld.h"
31 : #include "world/raycube.h"
32 : #include "world/bih.h"
33 : #include "world/world.h"
34 :
35 :
36 : #include "model/model.h"
37 :
38 : //oqfrags, outlinecolor are both extern vars
39 : VAR(oqfrags, 0, 8, 64); //occlusion query fragments
40 0 : CVARP(outlinecolor, 0); //color of edit mode outlines
41 :
42 : float shadowradius = 0,
43 : shadowbias = 0;
44 : size_t shadowside = 0;
45 : int shadowspot = 0;
46 : vec shadoworigin(0, 0, 0),
47 : shadowdir(0, 0, 0);
48 :
49 : vtxarray *visibleva = nullptr;
50 : vfc view;
51 :
52 : int deferquery = 0;
53 :
54 : struct shadowmesh final
55 : {
56 : vec origin;
57 : float radius;
58 : vec spotloc;
59 : int spotangle;
60 : int type;
61 : std::array<int, 6> draws;
62 : };
63 :
64 : Occluder occlusionengine;
65 :
66 : /* internally relevant functionality */
67 : ///////////////////////////////////////
68 :
69 : namespace
70 : {
71 0 : void drawtris(GLsizei numindices, const GLvoid *indices, GLuint minvert, GLuint maxvert)
72 : {
73 0 : glDrawRangeElements(GL_TRIANGLES, minvert, maxvert, numindices, GL_UNSIGNED_SHORT, indices);
74 0 : glde++;
75 0 : }
76 :
77 0 : void drawvatris(const vtxarray &va, GLsizei numindices, int offset)
78 : {
79 0 : drawtris(numindices, static_cast<ushort *>(nullptr) + va.eoffset + offset, va.minvert, va.maxvert);
80 0 : }
81 :
82 0 : void drawvaskytris(const vtxarray &va)
83 : {
84 0 : drawtris(va.sky, static_cast<ushort *>(nullptr) + va.skyoffset, va.minvert, va.maxvert);
85 0 : }
86 :
87 : ///////// view frustrum culling ///////////////////////
88 :
89 :
90 : /**
91 : * @brief Returns distance to the specified vertex array.
92 : *
93 : * @param va the vertex array to query
94 : * @param p the location to query the distance from
95 : */
96 0 : float vadist(const vtxarray &va, const vec &p)
97 : {
98 0 : return p.dist_to_bb(va.bbmin, va.bbmax);
99 : }
100 :
101 0 : void addvisibleva(vtxarray *va, std::array<vtxarray *, vasortsize> &vasort)
102 : {
103 0 : float dist = vadist(*va, camera1->o);
104 0 : va->distance = static_cast<int>(dist); /*cv.dist(camera1->o) - va->size*SQRT3/2*/
105 :
106 0 : int hash = std::clamp(static_cast<int>(dist*vasortsize/rootworld.mapsize()), 0, vasortsize-1);
107 0 : vtxarray **prev = &vasort[hash],
108 0 : *cur = vasort[hash];
109 :
110 0 : while(cur && va->distance >= cur->distance)
111 : {
112 0 : prev = &cur->next;
113 0 : cur = cur->next;
114 : }
115 :
116 0 : va->next = cur;
117 0 : *prev = va;
118 0 : }
119 :
120 0 : void sortvisiblevas(const std::array<vtxarray *, vasortsize> &vasort)
121 : {
122 0 : visibleva = nullptr;
123 0 : vtxarray **last = &visibleva;
124 0 : for(vtxarray *i : vasort)
125 : {
126 0 : if(i)
127 : {
128 0 : vtxarray *va = i;
129 0 : *last = va;
130 0 : while(va->next)
131 : {
132 0 : va = va->next;
133 : }
134 0 : last = &va->next;
135 : }
136 : }
137 0 : }
138 :
139 : template<bool fullvis, bool resetocclude>
140 0 : void findvisiblevas(std::vector<vtxarray *> &vas, std::array<vtxarray *, vasortsize> &vasort)
141 : {
142 0 : for(size_t i = 0; i < vas.size(); i++)
143 : {
144 0 : vtxarray &v = *vas[i];
145 0 : int prevvfc = v.curvfc;
146 0 : v.curvfc = fullvis ? ViewFrustumCull_FullyVisible : view.isvisiblecube(v.o, v.size);
147 0 : if(v.curvfc != ViewFrustumCull_NotVisible)
148 : {
149 0 : bool resetchildren = prevvfc >= ViewFrustumCull_NotVisible || resetocclude;
150 0 : if(resetchildren)
151 : {
152 0 : v.occluded = !v.texs ? Occlude_Geom : Occlude_Nothing;
153 0 : v.query = nullptr;
154 : }
155 0 : addvisibleva(&v, vasort);
156 0 : if(v.children.size())
157 : {
158 0 : if(fullvis || v.curvfc == ViewFrustumCull_FullyVisible)
159 : {
160 0 : if(resetchildren)
161 : {
162 0 : findvisiblevas<true, true>(v.children, vasort);
163 : }
164 : else
165 : {
166 0 : findvisiblevas<true, false>(v.children, vasort);
167 : }
168 : }
169 0 : else if(resetchildren)
170 : {
171 0 : findvisiblevas<false, true>(v.children, vasort);
172 : }
173 : else
174 : {
175 0 : findvisiblevas<false, false>(v.children, vasort);
176 : }
177 : }
178 : }
179 : }
180 0 : }
181 :
182 0 : void findvisiblevas()
183 : {
184 : std::array<vtxarray *, vasortsize> vasort;
185 0 : vasort.fill(nullptr);
186 0 : findvisiblevas<false, false>(varoot, vasort);
187 0 : sortvisiblevas(vasort);
188 0 : }
189 :
190 : ///////// occlusion queries /////////////
191 :
192 0 : VARF(oqany, 0, 0, 2, occlusionengine.clearqueries()); //occlusion query settings: 0: GL_SAMPLES_PASSED, 1: GL_ANY_SAMPLES_PASSED, 2: GL_ANY_SAMPLES_PASSED_CONSERVATIVE
193 : VAR(oqwait, 0, 1, 1);
194 :
195 : /**
196 : * @brief Returns query target
197 : *
198 : * GL_SAMPLES_PASSED if oqany is 0
199 : * GL_ANY_SAMPLES_PASSED if oqany is 1 or ES3 is not present
200 : * GL_ANY_SAMPLES_PASSED if oqany is 2 and ES3 is present
201 : */
202 0 : GLenum querytarget()
203 : {
204 0 : return oqany ?
205 0 : (oqany > 1 && hasES3 ?
206 : GL_ANY_SAMPLES_PASSED_CONSERVATIVE :
207 : GL_ANY_SAMPLES_PASSED) :
208 0 : GL_SAMPLES_PASSED;
209 : }
210 :
211 : GLuint bbvbo = 0,
212 : bbebo = 0;
213 :
214 0 : void setupbb()
215 : {
216 0 : if(!bbvbo)
217 : {
218 0 : glGenBuffers(1, &bbvbo);
219 0 : gle::bindvbo(bbvbo);
220 0 : std::array<vec, 8> verts;
221 0 : for(size_t i = 0; i < verts.size(); ++i)
222 : {
223 0 : verts[i] = vec(i&1, (i>>1)&1, (i>>2)&1);
224 : }
225 0 : glBufferData(GL_ARRAY_BUFFER, verts.size(), verts.data(), GL_STATIC_DRAW);
226 0 : gle::clearvbo();
227 : }
228 0 : if(!bbebo)
229 : {
230 0 : glGenBuffers(1, &bbebo);
231 0 : gle::bindebo(bbebo);
232 : std::array<ushort, 3*2*6> tris;
233 : //======================================== GENFACEVERT GENFACEORIENT
234 : #define GENFACEORIENT(orient, v0, v1, v2, v3) do { \
235 : int offset = orient*3*2; \
236 : tris[offset + 0] = v0; \
237 : tris[offset + 1] = v1; \
238 : tris[offset + 2] = v2; \
239 : tris[offset + 3] = v0; \
240 : tris[offset + 4] = v2; \
241 : tris[offset + 5] = v3; \
242 : } while(0);
243 : #define GENFACEVERT(orient, vert, ox,oy,oz, rx,ry,rz) (ox | oy | oz)
244 0 : GENFACEVERTS(0, 1, 0, 2, 0, 4, , , , , , )
245 : #undef GENFACEORIENT
246 : #undef GENFACEVERT
247 : //==================================================================
248 0 : glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ushort)*tris.size(), tris.data(), GL_STATIC_DRAW);
249 0 : gle::clearebo();
250 : }
251 0 : }
252 :
253 0 : void cleanupbb()
254 : {
255 0 : if(bbvbo)
256 : {
257 0 : glDeleteBuffers(1, &bbvbo);
258 0 : bbvbo = 0;
259 : }
260 0 : if(bbebo)
261 : {
262 0 : glDeleteBuffers(1, &bbebo);
263 0 : bbebo = 0;
264 : }
265 0 : }
266 :
267 : octaentities *visiblemms,
268 : **lastvisiblemms;
269 :
270 0 : void findvisiblemms(const std::vector<extentity *> &ents, bool doquery)
271 : {
272 0 : visiblemms = nullptr;
273 0 : lastvisiblemms = &visiblemms;
274 0 : for(vtxarray *va = visibleva; va; va = va->next)
275 : {
276 0 : if(va->occluded < Occlude_BB && va->curvfc < ViewFrustumCull_Fogged)
277 : {
278 0 : for(octaentities *oe : va->mapmodels)
279 : {
280 0 : if(view.isfoggedcube(oe->o, oe->size))
281 : {
282 0 : continue;
283 : }
284 0 : bool occluded = doquery && oe->query && oe->query->owner == oe && occlusionengine.checkquery(oe->query);
285 0 : if(occluded)
286 : {
287 0 : oe->distance = -1;
288 0 : oe->next = nullptr;
289 0 : *lastvisiblemms = oe;
290 0 : lastvisiblemms = &oe->next;
291 : }
292 : else
293 : {
294 0 : int visible = 0;
295 0 : for(const int &i : oe->mapmodels)
296 : {
297 0 : extentity &e = *ents[i];
298 0 : if(e.flags&EntFlag_NoVis)
299 : {
300 0 : continue;
301 : }
302 0 : e.flags |= EntFlag_Render;
303 0 : ++visible;
304 : }
305 0 : if(!visible)
306 : {
307 0 : continue;
308 : }
309 0 : oe->distance = static_cast<int>(camera1->o.dist_to_bb(oe->o, ivec(oe->o).add(oe->size)));
310 :
311 0 : octaentities **prev = &visiblemms;
312 0 : octaentities *cur = visiblemms;
313 0 : while(cur && cur->distance >= 0 && oe->distance > cur->distance)
314 : {
315 0 : prev = &cur->next;
316 0 : cur = cur->next;
317 : }
318 :
319 0 : if(*prev == nullptr)
320 : {
321 0 : lastvisiblemms = &oe->next;
322 : }
323 0 : oe->next = *prev;
324 0 : *prev = oe;
325 : }
326 : }
327 : }
328 : }
329 0 : }
330 :
331 : VAR(oqmm, 0, 4, 8); //`o`cclusion `q`uery `m`ap `m`odel
332 :
333 0 : void rendermapmodel(const extentity &e)
334 : {
335 0 : int anim = +Anim_Mapmodel | +Anim_Loop, //unary plus to promote to an integer, c++20 deprecates arithmetic conversion on enums (see C++ document P2864R2)
336 0 : basetime = 0;
337 0 : rendermapmodel(e.attr1, anim, e.o, e.attr2, e.attr3, e.attr4, Model_CullVFC | Model_CullDist, basetime, e.attr5 > 0 ? e.attr5/100.0f : 1.0f);
338 0 : }
339 :
340 0 : bool bbinsideva(const ivec &bo, const ivec &br, const vtxarray &va)
341 : {
342 0 : return bo.x >= va.bbmin.x && bo.y >= va.bbmin.y && bo.z >= va.bbmin.z &&
343 0 : br.x <= va.bbmax.x && br.y <= va.bbmax.y && br.z <= va.bbmax.z;
344 : }
345 :
346 0 : bool bboccluded(const ivec &bo, const ivec &br, const std::array<cube, 8> &c, const ivec &o, int size)
347 : {
348 0 : LOOP_OCTA_BOX(o, size, bo, br)
349 : {
350 0 : const ivec co(i, o, size);
351 0 : if(c[i].ext && c[i].ext->va)
352 : {
353 0 : const vtxarray *va = c[i].ext->va;
354 0 : if(va->curvfc >= ViewFrustumCull_Fogged || (va->occluded >= Occlude_BB && bbinsideva(bo, br, *va)))
355 : {
356 0 : continue;
357 : }
358 : }
359 0 : if(c[i].children && bboccluded(bo, br, *(c[i].children), co, size>>1))
360 : {
361 0 : continue;
362 : }
363 0 : return false;
364 : }
365 0 : return true;
366 : }
367 :
368 : VAR(dtoutline, 0, 1, 1); //`d`epth `t`est `outline`s
369 :
370 0 : int calcbbsidemask(const ivec &bbmin, const ivec &bbmax, const vec &lightpos, float lightradius, float bias)
371 : {
372 0 : vec pmin = vec(bbmin).sub(lightpos).div(lightradius),
373 0 : pmax = vec(bbmax).sub(lightpos).div(lightradius);
374 0 : int mask = 0x3F;
375 0 : float dp1 = pmax.x + pmax.y,
376 0 : dn1 = pmax.x - pmin.y,
377 0 : ap1 = std::fabs(dp1),
378 0 : an1 = std::fabs(dn1),
379 0 : dp2 = pmin.x + pmin.y,
380 0 : dn2 = pmin.x - pmax.y,
381 0 : ap2 = std::fabs(dp2),
382 0 : an2 = std::fabs(dn2);
383 0 : if(ap1 > bias*an1 && ap2 > bias*an2)
384 : {
385 0 : mask &= (3<<4)
386 0 : | (dp1 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2))
387 0 : | (dp2 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2));
388 : }
389 0 : if(an1 > bias*ap1 && an2 > bias*ap2)
390 : {
391 0 : mask &= (3<<4)
392 0 : | (dn1 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2))
393 0 : | (dn2 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2));
394 : }
395 0 : dp1 = pmax.y + pmax.z,
396 0 : dn1 = pmax.y - pmin.z,
397 0 : ap1 = std::fabs(dp1),
398 0 : an1 = std::fabs(dn1),
399 0 : dp2 = pmin.y + pmin.z,
400 0 : dn2 = pmin.y - pmax.z,
401 0 : ap2 = std::fabs(dp2),
402 0 : an2 = std::fabs(dn2);
403 0 : if(ap1 > bias*an1 && ap2 > bias*an2)
404 : {
405 0 : mask &= (3<<0)
406 0 : | (dp1 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4))
407 0 : | (dp2 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4));
408 : }
409 0 : if(an1 > bias*ap1 && an2 > bias*ap2)
410 : {
411 0 : mask &= (3<<0)
412 0 : | (dn1 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4))
413 0 : | (dn2 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4));
414 : }
415 0 : dp1 = pmax.z + pmax.x,
416 0 : dn1 = pmax.z - pmin.x,
417 0 : ap1 = std::fabs(dp1),
418 0 : an1 = std::fabs(dn1),
419 0 : dp2 = pmin.z + pmin.x,
420 0 : dn2 = pmin.z - pmax.x,
421 0 : ap2 = std::fabs(dp2),
422 0 : an2 = std::fabs(dn2);
423 0 : if(ap1 > bias*an1 && ap2 > bias*an2)
424 : {
425 0 : mask &= (3<<2)
426 0 : | (dp1 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0))
427 0 : | (dp2 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0));
428 : }
429 0 : if(an1 > bias*ap1 && an2 > bias*ap2)
430 : {
431 0 : mask &= (3<<2)
432 0 : | (dn1 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0))
433 0 : | (dn2 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0));
434 : }
435 0 : return mask;
436 : }
437 :
438 : VAR(smbbcull, 0, 1, 1);
439 : VAR(smdistcull, 0, 1, 1);
440 : VAR(smnodraw, 0, 0, 1);
441 :
442 : vtxarray *shadowva = nullptr;
443 :
444 0 : void addshadowva(vtxarray * const va, float dist, std::array<vtxarray *, vasortsize> &vasort)
445 : {
446 0 : va->rdistance = static_cast<int>(dist);
447 :
448 0 : int hash = std::clamp(static_cast<int>(dist*vasortsize/shadowradius), 0, vasortsize-1);
449 0 : vtxarray **prev = &vasort[hash],
450 0 : *cur = vasort[hash];
451 :
452 0 : while(cur && va->rdistance > cur->rdistance)
453 : {
454 0 : prev = &cur->rnext;
455 0 : cur = cur->rnext;
456 : }
457 :
458 0 : va->rnext = cur;
459 0 : *prev = va;
460 0 : }
461 :
462 0 : void sortshadowvas(std::array<vtxarray *, vasortsize> &vasort)
463 : {
464 0 : shadowva = nullptr;
465 0 : vtxarray **last = &shadowva;
466 0 : for(vtxarray *i : vasort)
467 : {
468 0 : if(i)
469 : {
470 0 : vtxarray *va = i;
471 0 : *last = va;
472 0 : while(va->rnext)
473 : {
474 0 : va = va->rnext;
475 : }
476 0 : last = &va->rnext;
477 : }
478 : }
479 0 : }
480 :
481 : octaentities *shadowmms = nullptr;
482 :
483 : struct geombatch final
484 : {
485 : const ElementSet &es;
486 : VSlot &vslot;
487 : int offset;
488 : const vtxarray * const va;
489 : int next, batch;
490 :
491 0 : geombatch(const ElementSet &es, int offset, const vtxarray *va)
492 0 : : es(es), vslot(lookupvslot(es.texture)), offset(offset), va(va),
493 0 : next(-1), batch(-1)
494 0 : {}
495 :
496 : void renderbatch() const;
497 :
498 0 : int compare(const geombatch &b) const
499 : {
500 0 : if(va->vbuf < b.va->vbuf)
501 : {
502 0 : return -1;
503 : }
504 0 : if(va->vbuf > b.va->vbuf)
505 : {
506 0 : return 1;
507 : }
508 0 : if(es.attrs.layer&BlendLayer_Bottom)
509 : {
510 0 : if(!(b.es.attrs.layer&BlendLayer_Bottom))
511 : {
512 0 : return 1;
513 : }
514 0 : int x1 = va->o.x&~0xFFF,
515 0 : x2 = b.va->o.x&~0xFFF;
516 0 : if(x1 < x2)
517 : {
518 0 : return -1;
519 : }
520 0 : if(x1 > x2)
521 : {
522 0 : return 1;
523 : }
524 0 : int y1 = va->o.y&~0xFFF,
525 0 : y2 = b.va->o.y&~0xFFF;
526 0 : if(y1 < y2)
527 : {
528 0 : return -1;
529 : }
530 0 : if(y1 > y2)
531 : {
532 0 : return 1;
533 : }
534 : }
535 0 : else if(b.es.attrs.layer&BlendLayer_Bottom)
536 : {
537 0 : return -1;
538 : }
539 0 : if(vslot.slot->shader < b.vslot.slot->shader)
540 : {
541 0 : return -1;
542 : }
543 0 : if(vslot.slot->shader > b.vslot.slot->shader)
544 : {
545 0 : return 1;
546 : }
547 0 : if(es.texture < b.es.texture)
548 : {
549 0 : return -1;
550 : }
551 0 : if(es.texture > b.es.texture)
552 : {
553 0 : return 1;
554 : }
555 0 : if(vslot.slot->params.size() < b.vslot.slot->params.size())
556 : {
557 0 : return -1;
558 : }
559 0 : if(vslot.slot->params.size() > b.vslot.slot->params.size())
560 : {
561 0 : return 1;
562 : }
563 0 : if(es.attrs.orient < b.es.attrs.orient)
564 : {
565 0 : return -1;
566 : }
567 0 : if(es.attrs.orient > b.es.attrs.orient)
568 : {
569 0 : return 1;
570 : }
571 0 : return 0;
572 : }
573 : };
574 :
575 : class RenderState final
576 : {
577 : public:
578 : bool colormask, depthmask;
579 : int alphaing;
580 : GLuint vbuf;
581 : bool vattribs, vquery;
582 : int globals;
583 :
584 : void disablevquery();
585 : void disablevbuf();
586 : void enablevquery();
587 : void cleanupgeom();
588 : void enablevattribs(bool all = true);
589 : void disablevattribs(bool all = true);
590 : void renderbatches(int pass);
591 : void renderzpass(const vtxarray &va);
592 : void invalidatetexgenorient();
593 : void invalidatealphascale();
594 : void cleartexgenmillis();
595 :
596 0 : RenderState() : colormask(true), depthmask(true), alphaing(0), vbuf(0), vattribs(false),
597 0 : vquery(false), globals(-1), alphascale(0), texgenorient(-1),
598 0 : texgenmillis(lastmillis), tmu(-1), colorscale(1, 1, 1),
599 0 : vslot(nullptr), texgenslot(nullptr), texgenvslot(nullptr),
600 0 : texgenscroll(0, 0), refractscale(0), refractcolor(1, 1, 1)
601 : {
602 0 : for(int k = 0; k < 7; ++k)
603 : {
604 0 : textures[k] = 0;
605 : }
606 0 : }
607 : private:
608 :
609 : float alphascale;
610 : int texgenorient, texgenmillis;
611 : int tmu;
612 : std::array<GLuint, 7> textures;
613 : vec colorscale;
614 : const VSlot *vslot;
615 : const Slot *texgenslot;
616 : const VSlot *texgenvslot;
617 : vec2 texgenscroll;
618 : float refractscale;
619 : vec refractcolor;
620 :
621 : void changetexgen(int orient, Slot &slot, VSlot &vslot);
622 : void changebatchtmus();
623 : void changeslottmus(int pass, Slot &newslot, VSlot &newvslot);
624 : void bindslottex(int type, const Texture *tex, GLenum target = GL_TEXTURE_2D);
625 : void changeshader(int pass, const geombatch &b);
626 :
627 : };
628 :
629 0 : void RenderState::invalidatetexgenorient()
630 : {
631 0 : texgenorient = -1;
632 0 : }
633 :
634 0 : void RenderState::invalidatealphascale()
635 : {
636 0 : alphascale = -1;
637 0 : }
638 :
639 0 : void RenderState::cleartexgenmillis()
640 : {
641 0 : texgenmillis = 0;
642 0 : }
643 :
644 0 : void RenderState::disablevbuf()
645 : {
646 0 : gle::clearvbo();
647 0 : gle::clearebo();
648 0 : vbuf = 0;
649 0 : }
650 :
651 0 : void RenderState::enablevquery()
652 : {
653 0 : if(colormask)
654 : {
655 0 : colormask = false;
656 0 : glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
657 : }
658 0 : if(depthmask)
659 : {
660 0 : depthmask = false;
661 0 : glDepthMask(GL_FALSE);
662 : }
663 0 : startbb(false);
664 0 : vquery = true;
665 0 : }
666 :
667 0 : void RenderState::disablevquery()
668 : {
669 0 : endbb(false);
670 0 : vquery = false;
671 0 : }
672 :
673 0 : void renderquery(RenderState &cur, const occludequery &query, const vtxarray &va, bool full = true)
674 : {
675 0 : if(!cur.vquery)
676 : {
677 0 : cur.enablevquery();
678 : }
679 0 : query.startquery();
680 0 : if(full)
681 : {
682 0 : drawbb(ivec(va.bbmin).sub(1), ivec(va.bbmax).sub(va.bbmin).add(2));
683 : }
684 : else
685 : {
686 0 : drawbb(va.geommin, ivec(va.geommax).sub(va.geommin));
687 : }
688 0 : occlusionengine.endquery();
689 0 : }
690 :
691 : enum RenderPass
692 : {
693 : RenderPass_GBuffer = 0,
694 : RenderPass_Z,
695 : RenderPass_Caustics,
696 : RenderPass_GBufferBlend,
697 : RenderPass_ReflectiveShadowMap,
698 : RenderPass_ReflectiveShadowMapBlend
699 : };
700 :
701 : std::vector<geombatch> geombatches;
702 : int firstbatch = -1,
703 : numbatches = 0;
704 :
705 0 : void mergetexs(const RenderState &cur, const vtxarray &va, ElementSet *texs = nullptr, int offset = 0)
706 : {
707 0 : int numtexs = 0;
708 0 : if(!texs)
709 : {
710 0 : texs = va.texelems;
711 0 : numtexs = va.texs;
712 0 : if(cur.alphaing)
713 : {
714 0 : texs += va.texs;
715 0 : offset += 3*(va.tris);
716 0 : numtexs = va.alphaback;
717 0 : if(cur.alphaing > 1)
718 : {
719 0 : numtexs += va.alphafront + va.refract;
720 : }
721 : }
722 : }
723 :
724 0 : if(firstbatch < 0)
725 : {
726 0 : firstbatch = geombatches.size();
727 0 : numbatches = numtexs;
728 0 : for(int i = 0; i < numtexs-1; ++i)
729 : {
730 0 : geombatches.emplace_back(texs[i], offset, &va);
731 0 : geombatches.back().next = i+1;
732 0 : offset += texs[i].length;
733 : }
734 0 : geombatches.emplace_back(texs[numtexs-1], offset, &va);
735 0 : return;
736 : }
737 :
738 0 : int prevbatch = -1,
739 0 : curbatch = firstbatch,
740 0 : curtex = 0;
741 : do
742 : {
743 0 : geombatches.emplace_back(texs[curtex], offset, &va);
744 0 : geombatch &b = geombatches.back();
745 0 : offset += texs[curtex].length;
746 0 : int dir = -1;
747 0 : while(curbatch >= 0)
748 : {
749 0 : dir = b.compare(geombatches[curbatch]);
750 0 : if(dir <= 0)
751 : {
752 0 : break;
753 : }
754 0 : prevbatch = curbatch;
755 0 : curbatch = geombatches[curbatch].next;
756 : }
757 0 : if(!dir)
758 : {
759 0 : int last = curbatch, next;
760 : for(;;)
761 : {
762 0 : next = geombatches[last].batch;
763 0 : if(next < 0)
764 : {
765 0 : break;
766 : }
767 0 : last = next;
768 : }
769 0 : if(last==curbatch)
770 : {
771 0 : b.batch = curbatch;
772 0 : b.next = geombatches[curbatch].next;
773 0 : if(prevbatch < 0)
774 : {
775 0 : firstbatch = geombatches.size()-1;
776 : }
777 : else
778 : {
779 0 : geombatches[prevbatch].next = geombatches.size()-1;
780 : }
781 0 : curbatch = geombatches.size()-1;
782 : }
783 : else
784 : {
785 0 : b.batch = next;
786 0 : geombatches[last].batch = geombatches.size()-1;
787 : }
788 : }
789 : else
790 : {
791 0 : numbatches++;
792 0 : b.next = curbatch;
793 0 : if(prevbatch < 0)
794 : {
795 0 : firstbatch = geombatches.size()-1;
796 : }
797 : else
798 : {
799 0 : geombatches[prevbatch].next = geombatches.size()-1;
800 : }
801 0 : prevbatch = geombatches.size()-1;
802 : }
803 0 : } while(++curtex < numtexs);
804 : }
805 :
806 0 : void RenderState::enablevattribs(bool all)
807 : {
808 0 : gle::enablevertex();
809 0 : if(all)
810 : {
811 0 : gle::enabletexcoord0();
812 0 : gle::enablenormal();
813 0 : gle::enabletangent();
814 : }
815 0 : vattribs = true;
816 0 : }
817 :
818 0 : void RenderState::disablevattribs(bool all)
819 : {
820 0 : gle::disablevertex();
821 0 : if(all)
822 : {
823 0 : gle::disabletexcoord0();
824 0 : gle::disablenormal();
825 0 : gle::disabletangent();
826 : }
827 0 : vattribs = false;
828 0 : }
829 :
830 0 : void changevbuf(RenderState &cur, int pass, const vtxarray &va)
831 : {
832 0 : gle::bindvbo(va.vbuf);
833 0 : gle::bindebo(va.ebuf);
834 0 : cur.vbuf = va.vbuf;
835 :
836 0 : vertex *vdata = nullptr;
837 0 : gle::vertexpointer(sizeof(vertex), vdata->pos.data());
838 0 : gle::vertexpointer(sizeof(vertex), vdata->pos.data());
839 :
840 0 : if(pass==RenderPass_GBuffer || pass==RenderPass_ReflectiveShadowMap)
841 : {
842 0 : gle::normalpointer(sizeof(vertex), vdata->norm.data(), GL_BYTE);
843 0 : gle::texcoord0pointer(sizeof(vertex), vdata->tc.data());
844 0 : gle::tangentpointer(sizeof(vertex), vdata->tangent.data(), GL_BYTE);
845 : }
846 0 : }
847 :
848 0 : void RenderState::changebatchtmus()
849 : {
850 0 : if(tmu != 0)
851 : {
852 0 : tmu = 0;
853 0 : glActiveTexture(GL_TEXTURE0);
854 : }
855 0 : }
856 :
857 0 : void RenderState::bindslottex(int type, const Texture *tex, GLenum target)
858 : {
859 0 : if(textures[type] != tex->id)
860 : {
861 0 : if(tmu != type)
862 : {
863 0 : tmu = type;
864 0 : glActiveTexture(GL_TEXTURE0 + type);
865 : }
866 0 : glBindTexture(target, textures[type] = tex->id);
867 : }
868 0 : }
869 :
870 0 : void RenderState::changeslottmus(int pass, Slot &newslot, VSlot &newvslot)
871 : {
872 0 : Texture *diffuse = newslot.sts.empty() ? notexture : newslot.sts[0].t;
873 0 : if(pass==RenderPass_GBuffer || pass==RenderPass_ReflectiveShadowMap)
874 : {
875 0 : bindslottex(Tex_Diffuse, diffuse);
876 :
877 0 : if(pass == RenderPass_GBuffer)
878 : {
879 0 : if(msaasamples)
880 : {
881 0 : GLOBALPARAMF(hashid, newvslot.index);
882 : }
883 0 : if(newslot.shader->type & Shader_Triplanar)
884 : {
885 0 : float scale = defaulttexscale/newvslot.scale;
886 0 : GLOBALPARAMF(texgenscale, scale/diffuse->xs, scale/diffuse->ys);
887 : }
888 : }
889 : }
890 :
891 0 : if(alphaing)
892 : {
893 0 : float alpha = alphaing > 1 ? newvslot.alphafront : newvslot.alphaback;
894 0 : if(alphascale != alpha)
895 : {
896 0 : alphascale = alpha;
897 0 : refractscale = 0;
898 0 : goto changecolorparams; //also run next if statement
899 : }
900 0 : if(colorscale != newvslot.colorscale)
901 : {
902 0 : changecolorparams:
903 0 : colorscale = newvslot.colorscale;
904 0 : GLOBALPARAMF(colorparams,
905 : alpha*newvslot.colorscale.x,
906 : alpha*newvslot.colorscale.y,
907 : alpha*newvslot.colorscale.z,
908 : alpha);
909 : }
910 0 : if(alphaing > 1 && newvslot.refractscale > 0 &&
911 0 : (refractscale != newvslot.refractscale || refractcolor != newvslot.refractcolor))
912 : {
913 0 : refractscale = newvslot.refractscale;
914 0 : refractcolor = newvslot.refractcolor;
915 0 : float refractscale = 0.5f/ldrscale*(1-alpha);
916 0 : GLOBALPARAMF(refractparams,
917 : newvslot.refractcolor.x*refractscale,
918 : newvslot.refractcolor.y*refractscale,
919 : newvslot.refractcolor.z*refractscale,
920 : newvslot.refractscale*viewh);
921 : }
922 : }
923 0 : else if(colorscale != newvslot.colorscale)
924 : {
925 0 : colorscale = newvslot.colorscale;
926 0 : GLOBALPARAMF(colorparams, newvslot.colorscale.x, newvslot.colorscale.y, newvslot.colorscale.z, 1);
927 : }
928 :
929 0 : for(const Slot::Tex &t : newslot.sts)
930 : {
931 0 : switch(t.type)
932 : {
933 0 : case Tex_Normal:
934 : case Tex_Glow:
935 : {
936 0 : bindslottex(t.type, t.t);
937 0 : break;
938 : }
939 : }
940 : }
941 0 : GLOBALPARAM(rotate, vec(newvslot.angle.y, newvslot.angle.z, diffuse->ratio()));
942 0 : if(tmu != 0)
943 : {
944 0 : tmu = 0;
945 0 : glActiveTexture(GL_TEXTURE0);
946 : }
947 0 : vslot = &newvslot;
948 0 : }
949 :
950 0 : void RenderState::changetexgen(int orient, Slot &slot, VSlot &vslot)
951 : {
952 0 : if(texgenslot != &slot || texgenvslot != &vslot)
953 : {
954 0 : const Texture *curtex = !texgenslot || texgenslot->sts.empty() ? notexture : texgenslot->sts[0].t;
955 0 : const Texture *tex = slot.sts.empty() ? notexture : slot.sts[0].t;
956 0 : if(!texgenvslot || slot.sts.empty() ||
957 0 : (curtex->xs != tex->xs || curtex->ys != tex->ys ||
958 0 : texgenvslot->rotation != vslot.rotation || texgenvslot->scale != vslot.scale ||
959 0 : texgenvslot->offset != vslot.offset || texgenvslot->scroll != vslot.scroll) ||
960 0 : texgenvslot->angle != vslot.angle)
961 : {
962 0 : const TexRotation &r = texrotations[vslot.rotation];
963 0 : float xs = r.flipx ? -tex->xs : tex->xs,
964 0 : ys = r.flipy ? -tex->ys : tex->ys;
965 0 : vec2 scroll(vslot.scroll);
966 0 : if(r.swapxy)
967 : {
968 0 : std::swap(scroll.x, scroll.y);
969 : }
970 0 : scroll.x *= texgenmillis*tex->xs/xs;
971 0 : scroll.y *= texgenmillis*tex->ys/ys;
972 0 : if(texgenscroll != scroll)
973 : {
974 0 : texgenscroll = scroll;
975 0 : texgenorient = -1;
976 : }
977 : }
978 0 : texgenslot = &slot;
979 0 : texgenvslot = &vslot;
980 : }
981 :
982 0 : if(texgenorient == orient)
983 : {
984 0 : return;
985 : }
986 0 : GLOBALPARAM(texgenscroll, texgenscroll);
987 :
988 0 : texgenorient = orient;
989 : }
990 :
991 0 : void RenderState::changeshader(int pass, const geombatch &b)
992 : {
993 0 : VSlot &vslot = b.vslot;
994 0 : Slot &slot = *vslot.slot;
995 0 : if(pass == RenderPass_ReflectiveShadowMap)
996 : {
997 0 : if(b.es.attrs.layer&BlendLayer_Bottom)
998 : {
999 0 : rsmworldshader->setvariant(0, 0, slot, vslot);
1000 : }
1001 : else
1002 : {
1003 0 : rsmworldshader->set(slot, vslot);
1004 : }
1005 : }
1006 0 : else if(alphaing)
1007 : {
1008 0 : slot.shader->setvariant(alphaing > 1 && vslot.refractscale > 0 ? 1 : 0, 1, slot, vslot);
1009 : }
1010 0 : else if(b.es.attrs.layer&BlendLayer_Bottom)
1011 : {
1012 0 : slot.shader->setvariant(0, 0, slot, vslot);
1013 : }
1014 : else
1015 : {
1016 0 : slot.shader->set(slot, vslot);
1017 : }
1018 0 : globals = GlobalShaderParamState::nextversion;
1019 0 : }
1020 :
1021 : template<class T>
1022 0 : void updateshader(T &cur)
1023 : {
1024 0 : if(cur.globals != GlobalShaderParamState::nextversion)
1025 : {
1026 0 : if(Shader::lastshader)
1027 : {
1028 0 : Shader::lastshader->flushparams();
1029 : }
1030 0 : cur.globals = GlobalShaderParamState::nextversion;
1031 : }
1032 0 : }
1033 :
1034 0 : void geombatch::renderbatch() const
1035 : {
1036 0 : gbatches++;
1037 0 : for(const geombatch *curbatch = this;; curbatch = &geombatches[curbatch->batch])
1038 : {
1039 0 : ushort len = curbatch->es.length;
1040 0 : if(len)
1041 : {
1042 0 : drawtris(len, static_cast<ushort *>(nullptr) + curbatch->va->eoffset + curbatch->offset, curbatch->es.minvert, curbatch->es.maxvert);
1043 0 : vtris += len/3;
1044 : }
1045 0 : if(curbatch->batch < 0)
1046 : {
1047 0 : break;
1048 : }
1049 0 : }
1050 0 : }
1051 :
1052 0 : void resetbatches()
1053 : {
1054 0 : geombatches.clear();
1055 0 : firstbatch = -1;
1056 0 : numbatches = 0;
1057 0 : }
1058 :
1059 0 : void RenderState::renderbatches(int pass)
1060 : {
1061 0 : vslot = nullptr;
1062 0 : int curbatch = firstbatch;
1063 0 : if(curbatch >= 0)
1064 : {
1065 0 : if(!depthmask)
1066 : {
1067 0 : depthmask = true;
1068 0 : glDepthMask(GL_TRUE);
1069 : }
1070 0 : if(!colormask)
1071 : {
1072 0 : colormask = true;
1073 0 : glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1074 : }
1075 0 : if(!vattribs)
1076 : {
1077 0 : if(vquery)
1078 : {
1079 0 : disablevquery();
1080 : }
1081 0 : enablevattribs();
1082 : }
1083 : }
1084 0 : while(curbatch >= 0)
1085 : {
1086 0 : const geombatch &b = geombatches[curbatch];
1087 0 : curbatch = b.next;
1088 :
1089 0 : if(vbuf != b.va->vbuf)
1090 : {
1091 0 : changevbuf(*this, pass, *b.va);
1092 : }
1093 0 : if(pass == RenderPass_GBuffer || pass == RenderPass_ReflectiveShadowMap)
1094 : {
1095 0 : changebatchtmus();
1096 : }
1097 0 : if(vslot != &b.vslot)
1098 : {
1099 0 : changeslottmus(pass, *b.vslot.slot, b.vslot);
1100 0 : if(texgenorient != b.es.attrs.orient || (texgenorient < Orient_Any && texgenvslot != &b.vslot))
1101 : {
1102 0 : changetexgen(b.es.attrs.orient, *b.vslot.slot, b.vslot);
1103 : }
1104 0 : changeshader(pass, b);
1105 : }
1106 : else
1107 : {
1108 0 : if(texgenorient != b.es.attrs.orient)
1109 : {
1110 0 : changetexgen(b.es.attrs.orient, *b.vslot.slot, b.vslot);
1111 : }
1112 0 : updateshader(*this);
1113 : }
1114 :
1115 0 : b.renderbatch();
1116 : }
1117 :
1118 0 : resetbatches();
1119 0 : }
1120 :
1121 0 : void RenderState::renderzpass(const vtxarray &va)
1122 : {
1123 0 : if(!vattribs)
1124 : {
1125 0 : if(vquery)
1126 : {
1127 0 : disablevquery();
1128 : }
1129 0 : enablevattribs(false);
1130 : }
1131 0 : if(vbuf!=va.vbuf)
1132 : {
1133 0 : changevbuf(*this, RenderPass_Z, va);
1134 : }
1135 0 : if(!depthmask)
1136 : {
1137 0 : depthmask = true;
1138 0 : glDepthMask(GL_TRUE);
1139 : }
1140 0 : if(colormask)
1141 : {
1142 0 : colormask = false;
1143 0 : glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1144 : }
1145 0 : int firsttex = 0,
1146 0 : numtris = va.tris,
1147 0 : offset = 0;
1148 0 : if(alphaing)
1149 : {
1150 0 : firsttex += va.texs;
1151 0 : offset += 3*(va.tris);
1152 0 : numtris = va.alphabacktris + va.alphafronttris + va.refracttris;
1153 0 : xtravertsva += 3*numtris;
1154 : }
1155 : else
1156 : {
1157 0 : xtravertsva += va.verts;
1158 : }
1159 0 : nocolorshader->set();
1160 0 : drawvatris(va, 3*numtris, offset);
1161 0 : }
1162 :
1163 : VAR(batchgeom, 0, 1, 1);
1164 :
1165 0 : void renderva(RenderState &cur, const vtxarray &va, int pass = RenderPass_GBuffer, bool doquery = false)
1166 : {
1167 0 : switch(pass)
1168 : {
1169 0 : case RenderPass_GBuffer:
1170 0 : if(!cur.alphaing)
1171 : {
1172 0 : vverts += va.verts;
1173 : }
1174 0 : if(doquery && va.query)
1175 : {
1176 0 : if(geombatches.size())
1177 : {
1178 0 : cur.renderbatches(pass);
1179 : }
1180 0 : va.query->startquery();
1181 : }
1182 0 : mergetexs(cur, va);
1183 0 : if(doquery)
1184 : {
1185 0 : if(va.query)
1186 : {
1187 0 : if(geombatches.size())
1188 : {
1189 0 : cur.renderbatches(pass);
1190 : }
1191 0 : occlusionengine.endquery();
1192 : }
1193 : }
1194 0 : else if(!batchgeom && geombatches.size())
1195 : {
1196 0 : cur.renderbatches(pass);
1197 : }
1198 0 : break;
1199 :
1200 0 : case RenderPass_GBufferBlend:
1201 0 : if(doquery && va.query)
1202 : {
1203 0 : if(geombatches.size())
1204 : {
1205 0 : cur.renderbatches(RenderPass_GBuffer);
1206 : }
1207 0 : va.query->startquery();
1208 : }
1209 0 : mergetexs(cur, va, &va.texelems[va.texs], 3*va.tris);
1210 0 : if(doquery)
1211 : {
1212 0 : if(va.query)
1213 : {
1214 0 : if(geombatches.size())
1215 : {
1216 0 : cur.renderbatches(RenderPass_GBuffer);
1217 : }
1218 0 : occlusionengine.endquery();
1219 : }
1220 : }
1221 0 : else if(!batchgeom && geombatches.size())
1222 : {
1223 0 : cur.renderbatches(RenderPass_GBuffer);
1224 : }
1225 0 : break;
1226 :
1227 0 : case RenderPass_Caustics:
1228 0 : if(!cur.vattribs)
1229 : {
1230 0 : cur.enablevattribs(false);
1231 : }
1232 0 : if(cur.vbuf!=va.vbuf)
1233 : {
1234 0 : changevbuf(cur, pass, va);
1235 : }
1236 0 : drawvatris(va, 3*va.tris, 0);
1237 0 : xtravertsva += va.verts;
1238 0 : break;
1239 :
1240 0 : case RenderPass_Z:
1241 0 : if(doquery && va.query)
1242 : {
1243 0 : va.query->startquery();
1244 : }
1245 0 : cur.renderzpass(va);
1246 0 : if(doquery && va.query)
1247 : {
1248 0 : occlusionengine.endquery();
1249 : }
1250 0 : break;
1251 :
1252 0 : case RenderPass_ReflectiveShadowMap:
1253 0 : mergetexs(cur, va);
1254 0 : if(!batchgeom && geombatches.size())
1255 : {
1256 0 : cur.renderbatches(pass);
1257 : }
1258 0 : break;
1259 :
1260 0 : case RenderPass_ReflectiveShadowMapBlend:
1261 0 : mergetexs(cur, va, &va.texelems[va.texs], 3*va.tris);
1262 0 : if(!batchgeom && geombatches.size())
1263 : {
1264 0 : cur.renderbatches(RenderPass_ReflectiveShadowMap);
1265 : }
1266 0 : break;
1267 : }
1268 0 : }
1269 :
1270 0 : void setupgeom()
1271 : {
1272 0 : glActiveTexture(GL_TEXTURE0);
1273 0 : GLOBALPARAMF(colorparams, 1, 1, 1, 1);
1274 0 : }
1275 :
1276 0 : void RenderState::cleanupgeom()
1277 : {
1278 0 : if(vattribs)
1279 : {
1280 0 : disablevattribs();
1281 : }
1282 0 : if(vbuf)
1283 : {
1284 0 : disablevbuf();
1285 : }
1286 0 : }
1287 :
1288 : VAR(oqgeom, 0, 1, 1); //occlusion query geometry
1289 :
1290 : std::vector<const vtxarray *> alphavas;
1291 :
1292 0 : CVARP(explicitskycolor, 0x800080);
1293 :
1294 : struct DecalBatch final
1295 : {
1296 : const ElementSet &es;
1297 : DecalSlot &slot;
1298 : int offset;
1299 : const vtxarray &va;
1300 : int next, batch;
1301 :
1302 0 : DecalBatch(const ElementSet &es, int offset, const vtxarray &va)
1303 0 : : es(es), slot(lookupdecalslot(es.texture)), offset(offset), va(va),
1304 0 : next(-1), batch(-1)
1305 0 : {}
1306 :
1307 : void renderdecalbatch();
1308 :
1309 : /**
1310 : * @brief Compares this DecalBatch to another.
1311 : *
1312 : * Compares va vbuf, then slot shader, then elementset texture, then number
1313 : * of slot parameters, then reuse. If all of these are equal, they return
1314 : * equal (0).
1315 : *
1316 : * @param b the batch to compare to
1317 : *
1318 : * @return -1 if this object compares smaller to b, +1 if this object compares larger, 0 otherwise
1319 : */
1320 0 : int compare(const DecalBatch &b) const
1321 : {
1322 0 : if(va.vbuf < b.va.vbuf)
1323 : {
1324 0 : return -1;
1325 : }
1326 0 : if(va.vbuf > b.va.vbuf)
1327 : {
1328 0 : return 1;
1329 : }
1330 0 : if(slot.shader < b.slot.shader)
1331 : {
1332 0 : return -1;
1333 : }
1334 0 : if(slot.shader > b.slot.shader)
1335 : {
1336 0 : return 1;
1337 : }
1338 0 : if(es.texture < b.es.texture)
1339 : {
1340 0 : return -1;
1341 : }
1342 0 : if(es.texture > b.es.texture)
1343 : {
1344 0 : return 1;
1345 : }
1346 0 : if(slot.Slot::params.size() < b.slot.Slot::params.size())
1347 : {
1348 0 : return -1;
1349 : }
1350 0 : if(slot.Slot::params.size() > b.slot.Slot::params.size())
1351 : {
1352 0 : return 1;
1353 : }
1354 0 : if(es.reuse < b.es.reuse)
1355 : {
1356 0 : return -1;
1357 : }
1358 0 : if(es.reuse > b.es.reuse)
1359 : {
1360 0 : return 1;
1361 : }
1362 0 : return 0;
1363 : }
1364 : };
1365 :
1366 : std::vector<DecalBatch> decalbatches;
1367 :
1368 : class decalrenderer final
1369 : {
1370 : public:
1371 : GLuint vbuf;
1372 : int globals;
1373 :
1374 : void renderdecalbatches(int pass);
1375 :
1376 0 : decalrenderer() : vbuf(0), globals(-1), colorscale(1, 1, 1), tmu(-1), slot(nullptr)
1377 : {
1378 0 : for(int i = 0; i < 7; ++i)
1379 : {
1380 0 : textures[i] = 0;
1381 : }
1382 0 : }
1383 : private:
1384 : vec colorscale;
1385 : int tmu;
1386 : std::array<GLuint, 7> textures;
1387 : DecalSlot *slot;
1388 :
1389 : void changebatchtmus();
1390 : void bindslottex(int type, const Texture *tex, GLenum target = GL_TEXTURE_2D);
1391 : void changeslottmus(DecalSlot &slot);
1392 : void changeshader(int pass, const DecalBatch &b);
1393 : };
1394 :
1395 0 : void mergedecals(const vtxarray &va)
1396 : {
1397 0 : ElementSet *texs = va.decalelems;
1398 0 : int numtexs = va.decaltexs,
1399 0 : offset = 0;
1400 :
1401 0 : if(firstbatch < 0)
1402 : {
1403 0 : firstbatch = decalbatches.size();
1404 0 : numbatches = numtexs;
1405 0 : for(int i = 0; i < numtexs-1; ++i)
1406 : {
1407 0 : decalbatches.emplace_back(DecalBatch(texs[i], offset, va));
1408 0 : decalbatches.back().next = i+1;
1409 0 : offset += texs[i].length;
1410 : }
1411 0 : decalbatches.emplace_back(DecalBatch(texs[numtexs-1], offset, va));
1412 0 : return;
1413 : }
1414 :
1415 0 : int prevbatch = -1,
1416 0 : curbatch = firstbatch,
1417 0 : curtex = 0;
1418 : do
1419 : {
1420 0 : DecalBatch b = DecalBatch(texs[curtex], offset, va);
1421 0 : offset += texs[curtex].length;
1422 0 : int dir = -1;
1423 0 : while(curbatch >= 0)
1424 : {
1425 0 : dir = b.compare(decalbatches[curbatch]);
1426 0 : if(dir <= 0)
1427 : {
1428 0 : break;
1429 : }
1430 0 : prevbatch = curbatch;
1431 0 : curbatch = decalbatches[curbatch].next;
1432 : }
1433 0 : if(!dir)
1434 : {
1435 0 : int last = curbatch, next;
1436 : for(;;)
1437 : {
1438 0 : next = decalbatches[last].batch;
1439 0 : if(next < 0)
1440 : {
1441 0 : break;
1442 : }
1443 0 : last = next;
1444 : }
1445 0 : if(last==curbatch)
1446 : {
1447 0 : b.batch = curbatch;
1448 0 : b.next = decalbatches[curbatch].next;
1449 0 : if(prevbatch < 0)
1450 : {
1451 0 : firstbatch = decalbatches.size()-1;
1452 : }
1453 : else
1454 : {
1455 0 : decalbatches[prevbatch].next = decalbatches.size()-1;
1456 : }
1457 0 : curbatch = decalbatches.size()-1;
1458 : }
1459 : else
1460 : {
1461 0 : b.batch = next;
1462 0 : decalbatches[last].batch = decalbatches.size()-1;
1463 : }
1464 : }
1465 : else
1466 : {
1467 0 : numbatches++;
1468 0 : b.next = curbatch;
1469 0 : if(prevbatch < 0)
1470 : {
1471 0 : firstbatch = decalbatches.size()-1;
1472 : }
1473 : else
1474 : {
1475 0 : decalbatches[prevbatch].next = decalbatches.size()-1;
1476 : }
1477 0 : prevbatch = decalbatches.size()-1;
1478 : }
1479 0 : decalbatches.push_back(b);
1480 0 : } while(++curtex < numtexs);
1481 : }
1482 :
1483 0 : void resetdecalbatches()
1484 : {
1485 0 : decalbatches.clear();
1486 0 : firstbatch = -1;
1487 0 : numbatches = 0;
1488 0 : }
1489 :
1490 0 : void changevbuf(decalrenderer &cur, const vtxarray &va)
1491 : {
1492 0 : gle::bindvbo(va.vbuf);
1493 0 : gle::bindebo(va.decalbuf);
1494 0 : cur.vbuf = va.vbuf;
1495 0 : const vertex *vdata = nullptr;
1496 : //note inane bikeshedding: use of offset from dereferenced null ptr (aka 0)
1497 0 : gle::vertexpointer(sizeof(vertex), vdata->pos.data());
1498 0 : gle::normalpointer(sizeof(vertex), vdata->norm.data(), GL_BYTE, 4);
1499 0 : gle::texcoord0pointer(sizeof(vertex), vdata->tc.data(), GL_FLOAT, 3);
1500 0 : gle::tangentpointer(sizeof(vertex), vdata->tangent.data(), GL_BYTE);
1501 0 : }
1502 :
1503 0 : void decalrenderer::changebatchtmus()
1504 : {
1505 0 : if(tmu != 0)
1506 : {
1507 0 : tmu = 0;
1508 0 : glActiveTexture(GL_TEXTURE0);
1509 : }
1510 0 : }
1511 :
1512 0 : void decalrenderer::bindslottex(int type, const Texture *tex, GLenum target)
1513 : {
1514 0 : if(textures[type] != tex->id)
1515 : {
1516 0 : if(tmu != type)
1517 : {
1518 0 : tmu = type;
1519 0 : glActiveTexture(GL_TEXTURE0 + type);
1520 : }
1521 0 : glBindTexture(target, textures[type] = tex->id);
1522 : }
1523 0 : }
1524 :
1525 0 : void decalrenderer::changeslottmus(DecalSlot &dslot)
1526 : {
1527 0 : const Texture *diffuse = dslot.sts.empty() ? notexture : dslot.sts[0].t;
1528 0 : bindslottex(Tex_Diffuse, diffuse);
1529 0 : for(const Slot::Tex &t : dslot.sts)
1530 : {
1531 0 : switch(t.type)
1532 : {
1533 0 : case Tex_Normal:
1534 : case Tex_Glow:
1535 : {
1536 0 : bindslottex(t.type, t.t);
1537 0 : break;
1538 : }
1539 0 : case Tex_Spec:
1540 : {
1541 0 : if(t.combined < 0)
1542 : {
1543 0 : bindslottex(Tex_Glow, t.t);
1544 : }
1545 0 : break;
1546 : }
1547 : }
1548 : }
1549 0 : if(tmu != 0)
1550 : {
1551 0 : tmu = 0;
1552 0 : glActiveTexture(GL_TEXTURE0);
1553 : }
1554 0 : if(colorscale != dslot.colorscale)
1555 : {
1556 0 : colorscale = dslot.colorscale;
1557 0 : GLOBALPARAMF(colorparams, dslot.colorscale.x, dslot.colorscale.y, dslot.colorscale.z, 1);
1558 : }
1559 0 : slot = &dslot;
1560 0 : }
1561 :
1562 0 : void decalrenderer::changeshader(int pass, const DecalBatch &b)
1563 : {
1564 0 : DecalSlot &slot = b.slot;
1565 0 : if(b.es.reuse)
1566 : {
1567 0 : VSlot &reuse = lookupvslot(b.es.reuse);
1568 0 : if(pass)
1569 : {
1570 0 : slot.shader->setvariant(0, 0, slot, reuse);
1571 : }
1572 : else
1573 : {
1574 0 : slot.shader->set(slot, reuse);
1575 : }
1576 : }
1577 0 : else if(pass)
1578 : {
1579 0 : slot.shader->setvariantandslot(0, 0);
1580 : }
1581 : else
1582 : {
1583 0 : slot.shader->setslot();
1584 : }
1585 0 : globals = GlobalShaderParamState::nextversion;
1586 0 : }
1587 :
1588 0 : void DecalBatch::renderdecalbatch()
1589 : {
1590 0 : gbatches++;
1591 0 : for(DecalBatch *curbatch = this;; curbatch = &decalbatches[curbatch->batch])
1592 : {
1593 0 : ushort len = curbatch->es.length;
1594 0 : if(len)
1595 : {
1596 0 : drawtris(len, reinterpret_cast<ushort *>(curbatch->va.decaloffset) + curbatch->offset, curbatch->es.minvert, curbatch->es.maxvert);
1597 0 : vtris += len/3;
1598 : }
1599 0 : if(curbatch->batch < 0)
1600 : {
1601 0 : break;
1602 : }
1603 0 : }
1604 0 : }
1605 :
1606 0 : void decalrenderer::renderdecalbatches(int pass)
1607 : {
1608 0 : slot = nullptr;
1609 0 : int curbatch = firstbatch;
1610 0 : while(curbatch >= 0)
1611 : {
1612 0 : DecalBatch &b = decalbatches[curbatch];
1613 0 : curbatch = b.next;
1614 :
1615 0 : if(pass && !b.slot.shader->numvariants(0))
1616 : {
1617 0 : continue;
1618 : }
1619 0 : if(vbuf != b.va.vbuf)
1620 : {
1621 0 : changevbuf(*this, b.va);
1622 : }
1623 0 : changebatchtmus();
1624 0 : if(slot != &b.slot)
1625 : {
1626 0 : changeslottmus(b.slot);
1627 0 : changeshader(pass, b);
1628 : }
1629 : else
1630 : {
1631 0 : updateshader(*this);
1632 : }
1633 :
1634 0 : b.renderdecalbatch();
1635 : }
1636 :
1637 0 : resetdecalbatches();
1638 0 : }
1639 :
1640 0 : void setupdecals()
1641 : {
1642 0 : gle::enablevertex();
1643 0 : gle::enablenormal();
1644 0 : gle::enabletexcoord0();
1645 0 : gle::enabletangent();
1646 :
1647 0 : glDepthMask(GL_FALSE);
1648 0 : glEnable(GL_BLEND);
1649 0 : enablepolygonoffset(GL_POLYGON_OFFSET_FILL);
1650 :
1651 0 : GLOBALPARAMF(colorparams, 1, 1, 1, 1);
1652 0 : }
1653 :
1654 0 : void cleanupdecals()
1655 : {
1656 0 : disablepolygonoffset(GL_POLYGON_OFFSET_FILL);
1657 0 : glDisable(GL_BLEND);
1658 0 : glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1659 0 : glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1660 0 : glDepthMask(GL_TRUE);
1661 0 : maskgbuffer("cnd");
1662 :
1663 0 : gle::disablevertex();
1664 0 : gle::disablenormal();
1665 0 : gle::disabletexcoord0();
1666 0 : gle::disabletangent();
1667 :
1668 0 : gle::clearvbo();
1669 0 : gle::clearebo();
1670 0 : }
1671 :
1672 : VAR(batchdecals, 0, 1, 1);
1673 :
1674 : struct shadowdraw final
1675 : {
1676 : GLuint ebuf, vbuf;
1677 : int offset, tris, next;
1678 : GLuint minvert, maxvert;
1679 : };
1680 :
1681 : struct shadowverts final
1682 : {
1683 : static constexpr int tablesize = 1<<13;
1684 : std::array<int, tablesize> table;
1685 : std::vector<vec> verts;
1686 : std::vector<int> chain;
1687 :
1688 1 : shadowverts() { clear(); }
1689 :
1690 1 : void clear()
1691 : {
1692 1 : table.fill(-1);
1693 1 : chain.clear();
1694 1 : verts.clear();
1695 1 : }
1696 :
1697 0 : int add(const vec &v)
1698 : {
1699 : const auto vechash = std::hash<vec>();
1700 0 : size_t h = vechash(v)&(tablesize-1);
1701 0 : for(int i = table[h]; i>=0; i = chain[i])
1702 : {
1703 0 : if(verts[i] == v)
1704 : {
1705 0 : return i;
1706 : }
1707 : }
1708 0 : if(verts.size() >= USHRT_MAX)
1709 : {
1710 0 : return -1;
1711 : }
1712 0 : verts.push_back(v);
1713 0 : chain.emplace_back(table[h]);
1714 0 : return table[h] = verts.size()-1;
1715 : }
1716 : } shadowverts;
1717 : std::array<std::vector<GLuint>, 6> shadowtris;
1718 : std::vector<GLuint> shadowvbos;
1719 : std::unordered_map<int, shadowmesh> shadowmeshes;
1720 : std::vector<shadowdraw> shadowdraws;
1721 :
1722 : struct shadowdrawinfo final
1723 : {
1724 : int last;
1725 : GLuint minvert, maxvert;
1726 :
1727 0 : shadowdrawinfo() : last(-1)
1728 : {
1729 0 : reset();
1730 0 : }
1731 :
1732 0 : void reset()
1733 : {
1734 0 : minvert = USHRT_MAX;
1735 0 : maxvert = 0;
1736 0 : }
1737 : };
1738 :
1739 0 : void flushshadowmeshdraws(shadowmesh &m, int sides, std::array<shadowdrawinfo, 6> &draws)
1740 : {
1741 0 : int numindexes = 0;
1742 0 : for(int i = 0; i < sides; ++i)
1743 : {
1744 0 : numindexes += shadowtris[i].size();
1745 : }
1746 0 : if(!numindexes)
1747 : {
1748 0 : return;
1749 : }
1750 :
1751 0 : GLuint ebuf = 0,
1752 0 : vbuf = 0;
1753 0 : glGenBuffers(1, &ebuf);
1754 0 : glGenBuffers(1, &vbuf);
1755 0 : ushort *indexes = new ushort[numindexes];
1756 0 : int offset = 0;
1757 0 : for(int i = 0; i < sides; ++i)
1758 : {
1759 0 : if(shadowtris[i].size())
1760 : {
1761 0 : if(draws[i].last < 0)
1762 : {
1763 0 : m.draws[i] = shadowdraws.size();
1764 : }
1765 : else
1766 : {
1767 0 : shadowdraws[draws[i].last].next = shadowdraws.size();
1768 : }
1769 0 : draws[i].last = shadowdraws.size();
1770 :
1771 : shadowdraw d;
1772 0 : d.ebuf = ebuf;
1773 0 : d.vbuf = vbuf;
1774 0 : d.offset = offset;
1775 0 : d.tris = shadowtris[i].size()/3;
1776 0 : d.minvert = draws[i].minvert;
1777 0 : d.maxvert = draws[i].maxvert;
1778 0 : d.next = -1;
1779 0 : shadowdraws.push_back(d);
1780 :
1781 0 : std::memcpy(indexes + offset, shadowtris[i].data(), shadowtris[i].size()*sizeof(ushort));
1782 0 : offset += shadowtris[i].size();
1783 :
1784 0 : shadowtris[i].clear();
1785 0 : draws[i].reset();
1786 : }
1787 : }
1788 :
1789 0 : gle::bindebo(ebuf);
1790 0 : glBufferData(GL_ELEMENT_ARRAY_BUFFER, numindexes*sizeof(ushort), indexes, GL_STATIC_DRAW);
1791 0 : gle::clearebo();
1792 0 : delete[] indexes;
1793 :
1794 0 : gle::bindvbo(vbuf);
1795 0 : glBufferData(GL_ARRAY_BUFFER, shadowverts.verts.size()*sizeof(vec), shadowverts.verts.data(), GL_STATIC_DRAW);
1796 0 : gle::clearvbo();
1797 0 : shadowverts.clear();
1798 :
1799 0 : shadowvbos.push_back(ebuf);
1800 0 : shadowvbos.push_back(vbuf);
1801 : }
1802 :
1803 0 : int calctrisidemask(const vec &p1, const vec &p2, const vec &p3, float bias)
1804 : {
1805 : // p1, p2, p3 are in the cubemap's local coordinate system
1806 : // bias = border/(size - border)
1807 0 : int mask = 0x3F;
1808 0 : float dp1 = p1.x + p1.y,
1809 0 : dn1 = p1.x - p1.y,
1810 0 : ap1 = std::fabs(dp1),
1811 0 : an1 = std::fabs(dn1),
1812 0 : dp2 = p2.x + p2.y,
1813 0 : dn2 = p2.x - p2.y,
1814 0 : ap2 = std::fabs(dp2),
1815 0 : an2 = std::fabs(dn2),
1816 0 : dp3 = p3.x + p3.y,
1817 0 : dn3 = p3.x - p3.y,
1818 0 : ap3 = std::fabs(dp3),
1819 0 : an3 = std::fabs(dn3);
1820 0 : if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3)
1821 : {
1822 0 : mask &= (3<<4)
1823 0 : | (dp1 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2))
1824 0 : | (dp2 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2))
1825 0 : | (dp3 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2));
1826 : }
1827 0 : if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3)
1828 0 : mask &= (3<<4)
1829 0 : | (dn1 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2))
1830 0 : | (dn2 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2))
1831 0 : | (dn3 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2));
1832 0 : dp1 = p1.y + p1.z,
1833 0 : dn1 = p1.y - p1.z,
1834 0 : ap1 = std::fabs(dp1),
1835 0 : an1 = std::fabs(dn1),
1836 0 : dp2 = p2.y + p2.z,
1837 0 : dn2 = p2.y - p2.z,
1838 0 : ap2 = std::fabs(dp2),
1839 0 : an2 = std::fabs(dn2),
1840 0 : dp3 = p3.y + p3.z,
1841 0 : dn3 = p3.y - p3.z,
1842 0 : ap3 = std::fabs(dp3),
1843 0 : an3 = std::fabs(dn3);
1844 0 : if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3)
1845 : {
1846 0 : mask &= (3<<0)
1847 0 : | (dp1 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4))
1848 0 : | (dp2 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4))
1849 0 : | (dp3 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4));
1850 : }
1851 0 : if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3)
1852 : {
1853 0 : mask &= (3<<0)
1854 0 : | (dn1 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4))
1855 0 : | (dn2 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4))
1856 0 : | (dn3 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4));
1857 : }
1858 0 : dp1 = p1.z + p1.x,
1859 0 : dn1 = p1.z - p1.x,
1860 0 : ap1 = std::fabs(dp1),
1861 0 : an1 = std::fabs(dn1),
1862 0 : dp2 = p2.z + p2.x,
1863 0 : dn2 = p2.z - p2.x,
1864 0 : ap2 = std::fabs(dp2),
1865 0 : an2 = std::fabs(dn2),
1866 0 : dp3 = p3.z + p3.x,
1867 0 : dn3 = p3.z - p3.x,
1868 0 : ap3 = std::fabs(dp3),
1869 0 : an3 = std::fabs(dn3);
1870 0 : if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3)
1871 : {
1872 0 : mask &= (3<<2)
1873 0 : | (dp1 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0))
1874 0 : | (dp2 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0))
1875 0 : | (dp3 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0));
1876 : }
1877 0 : if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3)
1878 : {
1879 0 : mask &= (3<<2)
1880 0 : | (dn1 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0))
1881 0 : | (dn2 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0))
1882 0 : | (dn3 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0));
1883 : }
1884 0 : return mask;
1885 : }
1886 :
1887 0 : void addshadowmeshtri(shadowmesh &m, int sides, std::array<shadowdrawinfo, 6> &draws, const vec &v0, const vec &v1, const vec &v2)
1888 : {
1889 0 : vec l0 = vec(v0).sub(shadoworigin);
1890 0 : float side = l0.scalartriple(vec(v1).sub(v0), vec(v2).sub(v0));
1891 0 : if(smcullside ? side > 0 : side < 0)
1892 : {
1893 0 : return;
1894 : }
1895 0 : vec l1 = vec(v1).sub(shadoworigin),
1896 0 : l2 = vec(v2).sub(shadoworigin);
1897 0 : if(l0.squaredlen() > shadowradius*shadowradius && l1.squaredlen() > shadowradius*shadowradius && l2.squaredlen() > shadowradius*shadowradius)
1898 : {
1899 0 : return;
1900 : }
1901 0 : int sidemask = 0;
1902 0 : switch(m.type)
1903 : {
1904 0 : case ShadowMap_Spot:
1905 : {
1906 0 : sidemask = bbinsidespot(shadoworigin, shadowdir, shadowspot, ivec(vec(v0).min(v1).min(v2)), ivec(vec(v0).max(v1).max(v2).add(1))) ? 1 : 0;
1907 0 : break;
1908 : }
1909 0 : case ShadowMap_CubeMap:
1910 : {
1911 0 : sidemask = calctrisidemask(l0.div(shadowradius), l1.div(shadowradius), l2.div(shadowradius), shadowbias);
1912 0 : break;
1913 : }
1914 : }
1915 0 : if(!sidemask)
1916 : {
1917 0 : return;
1918 : }
1919 0 : if(shadowverts.verts.size() + 3 >= USHRT_MAX)
1920 : {
1921 0 : flushshadowmeshdraws(m, sides, draws);
1922 : }
1923 0 : int i0 = shadowverts.add(v0),
1924 0 : i1 = shadowverts.add(v1),
1925 0 : i2 = shadowverts.add(v2);
1926 0 : GLuint minvert = std::min(i0, std::min(i1, i2)),
1927 0 : maxvert = std::max(i0, std::max(i1, i2));
1928 0 : for(int k = 0; k < sides; ++k)
1929 : {
1930 0 : if(sidemask&(1<<k))
1931 : {
1932 0 : shadowdrawinfo &d = draws[k];
1933 0 : d.minvert = std::min(d.minvert, minvert);
1934 0 : d.maxvert = std::max(d.maxvert, maxvert);
1935 0 : shadowtris[k].push_back(i0);
1936 0 : shadowtris[k].push_back(i1);
1937 0 : shadowtris[k].push_back(i2);
1938 : }
1939 : }
1940 : }
1941 :
1942 0 : void genshadowmeshtris(shadowmesh &m, int sides, std::array<shadowdrawinfo, 6> &draws, ushort *edata, int numtris, vertex *vdata)
1943 : {
1944 0 : for(int j = 0; j < 3*numtris; j += 3)
1945 : {
1946 0 : addshadowmeshtri(m, sides, draws, vdata[edata[j]].pos, vdata[edata[j+1]].pos, vdata[edata[j+2]].pos);
1947 : }
1948 0 : }
1949 :
1950 0 : void genshadowmeshmapmodels(shadowmesh &m, int sides, std::array<shadowdrawinfo, 6> &draws)
1951 : {
1952 0 : const std::vector<extentity *> &ents = entities::getents();
1953 0 : for(octaentities *oe = shadowmms; oe; oe = oe->rnext)
1954 : {
1955 0 : for(size_t k = 0; k < oe->mapmodels.size(); k++)
1956 : {
1957 0 : extentity &e = *ents[oe->mapmodels[k]];
1958 0 : if(e.flags&(EntFlag_NoVis|EntFlag_NoShadow))
1959 : {
1960 0 : continue;
1961 : }
1962 0 : e.flags |= EntFlag_Render;
1963 : }
1964 : }
1965 0 : std::vector<triangle> tris;
1966 0 : for(octaentities *oe = shadowmms; oe; oe = oe->rnext)
1967 : {
1968 0 : for(const int &j : oe->mapmodels)
1969 : {
1970 0 : extentity &e = *ents[j];
1971 0 : if(!(e.flags&EntFlag_Render))
1972 : {
1973 0 : continue;
1974 : }
1975 0 : e.flags &= ~EntFlag_Render;
1976 0 : model *mm = loadmapmodel(e.attr1);
1977 0 : if(!mm || !mm->shadow || mm->animated() || (mm->alphashadow && mm->alphatested()))
1978 : {
1979 0 : continue;
1980 : }
1981 0 : matrix4x3 orient;
1982 0 : orient.identity();
1983 0 : if(e.attr2)
1984 : {
1985 0 : orient.rotate_around_z(sincosmod360(e.attr2));
1986 : }
1987 0 : if(e.attr3)
1988 : {
1989 0 : orient.rotate_around_x(sincosmod360(e.attr3));
1990 : }
1991 0 : if(e.attr4)
1992 : {
1993 0 : orient.rotate_around_y(sincosmod360(-e.attr4));
1994 : }
1995 0 : if(e.attr5 > 0)
1996 : {
1997 0 : orient.scale(e.attr5/100.0f);
1998 : }
1999 0 : orient.settranslation(e.o);
2000 0 : tris.clear();
2001 0 : mm->genshadowmesh(tris, orient);
2002 :
2003 0 : for(size_t i = 0; i < tris.size(); i++)
2004 : {
2005 0 : const triangle &t = tris[i];
2006 0 : addshadowmeshtri(m, sides, draws, t.a, t.b, t.c);
2007 : }
2008 :
2009 0 : e.flags |= EntFlag_ShadowMesh;
2010 : }
2011 : }
2012 0 : }
2013 :
2014 0 : void genshadowmesh(int idx, const extentity &e)
2015 : {
2016 0 : shadowmesh m;
2017 0 : m.type = calcshadowinfo(e, m.origin, m.radius, m.spotloc, m.spotangle, shadowbias);
2018 0 : if(!m.type)
2019 : {
2020 0 : return;
2021 : }
2022 0 : m.draws.fill(-1);
2023 :
2024 0 : shadowmapping = m.type;
2025 0 : shadoworigin = m.origin;
2026 0 : shadowradius = m.radius;
2027 0 : shadowdir = m.type == ShadowMap_Spot ? vec(m.spotloc).sub(m.origin).normalize() : vec(0, 0, 0);
2028 0 : shadowspot = m.spotangle;
2029 :
2030 0 : findshadowvas();
2031 0 : findshadowmms();
2032 :
2033 0 : int sides = m.type == ShadowMap_Spot ? 1 : 6;
2034 0 : std::array<shadowdrawinfo, 6> draws;
2035 0 : for(vtxarray *va = shadowva; va; va = va->rnext)
2036 : {
2037 0 : if(va->shadowmask)
2038 : {
2039 0 : if(va->tris)
2040 : {
2041 0 : genshadowmeshtris(m, sides, draws, va->edata + va->eoffset, va->tris, va->vdata);
2042 : }
2043 0 : if(skyshadow && va->sky)
2044 : {
2045 0 : genshadowmeshtris(m, sides, draws, va->skydata + va->skyoffset, va->sky/3, va->vdata);
2046 : }
2047 : }
2048 : }
2049 0 : if(shadowmms)
2050 : {
2051 0 : genshadowmeshmapmodels(m, sides, draws);
2052 : }
2053 0 : flushshadowmeshdraws(m, sides, draws);
2054 :
2055 0 : shadowmeshes[idx] = m;
2056 :
2057 0 : shadowmapping = 0;
2058 : }
2059 :
2060 : // toggles whether to generate shadow meshes in genshadowmeshes()
2061 0 : VARF(smmesh, 0, 1, 1,
2062 : {
2063 : if(!smmesh)
2064 : {
2065 : clearshadowmeshes();
2066 : }
2067 : });
2068 : }
2069 :
2070 : /* externally relevant functionality */
2071 : ///////////////////////////////////////
2072 :
2073 : // vfc - view frustum culling
2074 :
2075 0 : int vfc::isfoggedcube(const ivec &o, int size) const
2076 : {
2077 0 : for(int i = 0; i < 4; ++i)
2078 : {
2079 0 : if(o.dist(vfcP[i]) < -vfcDfar[i]*size)
2080 : {
2081 0 : return true;
2082 : }
2083 : }
2084 0 : float dist = o.dist(vfcP[4]);
2085 0 : return dist < -vfcDfar[4]*size || dist > vfcDfog - vfcDnear[4]*size;
2086 : }
2087 :
2088 0 : int vfc::isvisiblecube(const ivec &o, int size) const
2089 : {
2090 0 : int v = ViewFrustumCull_FullyVisible;
2091 : float dist;
2092 :
2093 0 : for(int i = 0; i < 5; ++i)
2094 : {
2095 0 : dist = o.dist(vfcP[i]);
2096 0 : if(dist < -vfcDfar[i]*size)
2097 : {
2098 0 : return ViewFrustumCull_NotVisible;
2099 : }
2100 0 : if(dist < -vfcDnear[i]*size)
2101 : {
2102 0 : v = ViewFrustumCull_PartlyVisible;
2103 : }
2104 : }
2105 :
2106 0 : dist -= vfcDfog;
2107 0 : if(dist > -vfcDnear[4]*size)
2108 : {
2109 0 : return ViewFrustumCull_Fogged;
2110 : }
2111 0 : if(dist > -vfcDfar[4]*size)
2112 : {
2113 0 : v = ViewFrustumCull_PartlyVisible;
2114 : }
2115 :
2116 0 : return v;
2117 : }
2118 :
2119 0 : void vfc::calcvfcD()
2120 : {
2121 0 : for(int i = 0; i < 5; ++i)
2122 : {
2123 0 : const plane &p = vfcP[i];
2124 0 : vfcDnear[i] = vfcDfar[i] = 0;
2125 0 : for(int k = 0; k < 3; ++k)
2126 : {
2127 0 : if(p[k] > 0)
2128 : {
2129 0 : vfcDfar[i] += p[k];
2130 : }
2131 : else
2132 : {
2133 0 : vfcDnear[i] += p[k];
2134 : }
2135 : }
2136 : }
2137 0 : }
2138 :
2139 0 : void vfc::visiblecubes(bool cull)
2140 : {
2141 0 : if(cull)
2142 : {
2143 0 : setvfcP();
2144 0 : findvisiblevas();
2145 : }
2146 : else
2147 : {
2148 0 : for(int i = 0; i < 5; ++i)
2149 : {
2150 0 : vfcP[i].x = vfcP[i].y = vfcP[i].z = vfcP[i].offset = 0;
2151 : };
2152 0 : vfcDfog = farplane;
2153 0 : vfcDnear.fill(0);
2154 0 : vfcDfar.fill(0);
2155 0 : visibleva = nullptr;
2156 0 : for(size_t i = 0; i < valist.size(); i++)
2157 : {
2158 0 : vtxarray *va = valist[i];
2159 0 : va->distance = 0;
2160 0 : va->curvfc = ViewFrustumCull_FullyVisible;
2161 0 : va->occluded = !va->texs ? Occlude_Geom : Occlude_Nothing;
2162 0 : va->query = nullptr;
2163 0 : va->next = visibleva;
2164 0 : visibleva = va;
2165 : }
2166 : }
2167 0 : }
2168 :
2169 0 : bool vfc::isfoggedsphere(float rad, const vec &cv) const
2170 : {
2171 0 : for(int i = 0; i < 4; ++i)
2172 : {
2173 0 : if(vfcP[i].dist(cv) < -rad)
2174 : {
2175 0 : return true;
2176 : }
2177 : }
2178 0 : float dist = vfcP[4].dist(cv);
2179 0 : return dist < -rad || dist > vfcDfog + rad; // true if abs(dist) is large
2180 : }
2181 :
2182 0 : int vfc::isvisiblesphere(float rad, const vec &cv) const
2183 : {
2184 0 : int v = ViewFrustumCull_FullyVisible;
2185 : float dist;
2186 :
2187 0 : for(int i = 0; i < 5; ++i)
2188 : {
2189 0 : dist = vfcP[i].dist(cv);
2190 0 : if(dist < -rad)
2191 : {
2192 0 : return ViewFrustumCull_NotVisible;
2193 : }
2194 0 : if(dist < rad)
2195 : {
2196 0 : v = ViewFrustumCull_PartlyVisible;
2197 : }
2198 : }
2199 :
2200 0 : dist -= vfcDfog;
2201 0 : if(dist > rad)
2202 : {
2203 0 : return ViewFrustumCull_Fogged; //ViewFrustumCull_NotVisible; // culling when fog is closer than size of world results in HOM
2204 : }
2205 0 : if(dist > -rad)
2206 : {
2207 0 : v = ViewFrustumCull_PartlyVisible;
2208 : }
2209 0 : return v;
2210 : }
2211 :
2212 0 : int vfc::isvisiblebb(const ivec &bo, const ivec &br) const
2213 : {
2214 0 : int v = ViewFrustumCull_FullyVisible;
2215 : float dnear, dfar;
2216 :
2217 0 : for(int i = 0; i < 5; ++i)
2218 : {
2219 0 : const plane &p = vfcP[i];
2220 0 : dnear = dfar = bo.dist(p);
2221 0 : if(p.x > 0)
2222 : {
2223 0 : dfar += p.x*br.x;
2224 : }
2225 : else
2226 : {
2227 0 : dnear += p.x*br.x;
2228 : }
2229 0 : if(p.y > 0)
2230 : {
2231 0 : dfar += p.y*br.y;
2232 : }
2233 : else
2234 : {
2235 0 : dnear += p.y*br.y;
2236 : }
2237 0 : if(p.z > 0)
2238 : {
2239 0 : dfar += p.z*br.z;
2240 : }
2241 : else
2242 : {
2243 0 : dnear += p.z*br.z;
2244 : }
2245 0 : if(dfar < 0)
2246 : {
2247 0 : return ViewFrustumCull_NotVisible;
2248 : }
2249 0 : if(dnear < 0)
2250 : {
2251 0 : v = ViewFrustumCull_PartlyVisible;
2252 : }
2253 : }
2254 :
2255 0 : if(dnear > vfcDfog)
2256 : {
2257 0 : return ViewFrustumCull_Fogged;
2258 : }
2259 0 : if(dfar > vfcDfog)
2260 : {
2261 0 : v = ViewFrustumCull_PartlyVisible;
2262 : }
2263 0 : return v;
2264 : }
2265 :
2266 0 : bool cubeworld::bboccluded(const ivec &bo, const ivec &br) const
2267 : {
2268 0 : int diff = (bo.x^br.x) | (bo.y^br.y) | (bo.z^br.z);
2269 0 : if(diff&~((1<<worldscale)-1))
2270 : {
2271 0 : return false;
2272 : }
2273 0 : int scale = worldscale-1;
2274 0 : if(diff&(1<<scale))
2275 : {
2276 0 : return ::bboccluded(bo, br, *worldroot, ivec(0, 0, 0), 1<<scale);
2277 : }
2278 0 : const cube *c = &(*worldroot)[OCTA_STEP(bo.x, bo.y, bo.z, scale)];
2279 0 : if(c->ext && c->ext->va)
2280 : {
2281 0 : const vtxarray *va = c->ext->va;
2282 0 : if(va->curvfc >= ViewFrustumCull_Fogged || (va->occluded >= Occlude_BB && bbinsideva(bo, br, *va)))
2283 : {
2284 0 : return true;
2285 : }
2286 : }
2287 0 : scale--;
2288 0 : while(c->children && !(diff&(1<<scale)))
2289 : {
2290 0 : c = &(*c->children)[OCTA_STEP(bo.x, bo.y, bo.z, scale)];
2291 0 : if(c->ext && c->ext->va)
2292 : {
2293 0 : const vtxarray *va = c->ext->va;
2294 0 : if(va->curvfc >= ViewFrustumCull_Fogged || (va->occluded >= Occlude_BB && bbinsideva(bo, br, *va)))
2295 : {
2296 0 : return true;
2297 : }
2298 : }
2299 0 : scale--;
2300 : }
2301 0 : if(c->children)
2302 : {
2303 0 : return ::bboccluded(bo, br, *(c->children), ivec(bo).mask(~((2<<scale)-1)), 1<<scale);
2304 : }
2305 0 : return false;
2306 : }
2307 :
2308 0 : void startbb(bool mask)
2309 : {
2310 0 : setupbb();
2311 0 : gle::bindvbo(bbvbo);
2312 0 : gle::bindebo(bbebo);
2313 0 : gle::vertexpointer(sizeof(vec), nullptr);
2314 0 : gle::enablevertex();
2315 0 : SETSHADER(bbquery);
2316 0 : if(mask)
2317 : {
2318 0 : glDepthMask(GL_FALSE);
2319 0 : glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2320 : }
2321 0 : }
2322 :
2323 0 : void endbb(bool mask)
2324 : {
2325 0 : gle::disablevertex();
2326 0 : gle::clearvbo();
2327 0 : gle::clearebo();
2328 0 : if(mask)
2329 : {
2330 0 : glDepthMask(GL_TRUE);
2331 0 : glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2332 : }
2333 0 : }
2334 :
2335 0 : void drawbb(const ivec &bo, const ivec &br)
2336 : {
2337 0 : LOCALPARAMF(bborigin, bo.x, bo.y, bo.z);
2338 0 : LOCALPARAMF(bbsize, br.x, br.y, br.z);
2339 0 : glDrawRangeElements(GL_TRIANGLES, 0, 8-1, 3*2*6, GL_UNSIGNED_SHORT, nullptr);
2340 0 : xtraverts += 8;
2341 0 : }
2342 :
2343 0 : void vfc::setvfcP(const vec &bbmin, const vec &bbmax)
2344 : {
2345 0 : vec4<float> px = camprojmatrix.rowx(),
2346 0 : py = camprojmatrix.rowy(),
2347 0 : pz = camprojmatrix.rowz(),
2348 0 : pw = camprojmatrix.roww();
2349 0 : vfcP[0] = plane(vec4<float>(pw).mul(-bbmin.x).add(px)).normalize(); // left plane
2350 0 : vfcP[1] = plane(vec4<float>(pw).mul(bbmax.x).sub(px)).normalize(); // right plane
2351 0 : vfcP[2] = plane(vec4<float>(pw).mul(-bbmin.y).add(py)).normalize(); // bottom plane
2352 0 : vfcP[3] = plane(vec4<float>(pw).mul(bbmax.y).sub(py)).normalize(); // top plane
2353 0 : vfcP[4] = plane(vec4<float>(pw).add(pz)).normalize(); // near/far planes
2354 :
2355 0 : vfcDfog = std::min(calcfogcull(), static_cast<float>(farplane));
2356 0 : view.calcvfcD();
2357 0 : }
2358 :
2359 : //oq
2360 :
2361 0 : void Occluder::clearqueries()
2362 : {
2363 0 : for(QueryFrame &i : queryframes)
2364 : {
2365 0 : i.cleanup();
2366 : }
2367 0 : }
2368 :
2369 0 : void Occluder::flipqueries()
2370 : {
2371 0 : flipquery = (flipquery + 1) % maxqueryframes;
2372 0 : queryframes[flipquery].flip();
2373 0 : }
2374 :
2375 0 : void Occluder::endquery() const
2376 : {
2377 0 : glEndQuery(querytarget());
2378 0 : }
2379 :
2380 0 : bool Occluder::checkquery(occludequery *query, bool nowait)
2381 : {
2382 0 : if(query->fragments < 0)
2383 : {
2384 0 : if(nowait || !oqwait)
2385 : {
2386 : GLint avail;
2387 0 : glGetQueryObjectiv(query->id, GL_QUERY_RESULT_AVAILABLE, &avail);
2388 0 : if(!avail)
2389 : {
2390 0 : return false;
2391 : }
2392 : }
2393 :
2394 : GLuint fragments;
2395 0 : glGetQueryObjectuiv(query->id, GL_QUERY_RESULT, &fragments);
2396 0 : query->fragments = querytarget() == GL_SAMPLES_PASSED || !fragments ? static_cast<int>(fragments) : oqfrags;
2397 : }
2398 0 : return query->fragments < oqfrags;
2399 : }
2400 :
2401 0 : void Occluder::resetqueries()
2402 : {
2403 0 : for(QueryFrame &i : queryframes)
2404 : {
2405 0 : i.reset();
2406 : }
2407 0 : }
2408 :
2409 1 : int Occluder::getnumqueries() const
2410 : {
2411 1 : return queryframes[flipquery].cur;
2412 : }
2413 :
2414 0 : occludequery *Occluder::newquery(const void *owner)
2415 : {
2416 0 : return queryframes[flipquery].newquery(owner);
2417 : }
2418 :
2419 0 : void Occluder::QueryFrame::flip()
2420 : {
2421 0 : for(int i = 0; i < cur; ++i)
2422 : {
2423 0 : queries[i].owner = nullptr;
2424 : }
2425 0 : for(; defer > 0 && max < maxquery; defer--)
2426 : {
2427 0 : queries[max].owner = nullptr;
2428 0 : queries[max].fragments = -1;
2429 0 : glGenQueries(1, &queries[max++].id);
2430 : }
2431 0 : cur = defer = 0;
2432 0 : }
2433 :
2434 0 : occludequery *Occluder::QueryFrame::newquery(const void *owner)
2435 : {
2436 0 : if(cur >= max)
2437 : {
2438 0 : if(max >= maxquery)
2439 : {
2440 0 : return nullptr;
2441 : }
2442 0 : if(deferquery)
2443 : {
2444 0 : if(max + defer < maxquery)
2445 : {
2446 0 : defer++;
2447 : }
2448 0 : return nullptr;
2449 : }
2450 0 : glGenQueries(1, &queries[max++].id);
2451 : }
2452 0 : occludequery *query = &queries[cur++];
2453 0 : query->owner = owner;
2454 0 : query->fragments = -1;
2455 0 : return query;
2456 : }
2457 :
2458 0 : void Occluder::QueryFrame::reset()
2459 : {
2460 0 : for(int i = 0; i < max; ++i)
2461 : {
2462 0 : queries[i].owner = nullptr;
2463 : }
2464 0 : }
2465 :
2466 0 : void Occluder::QueryFrame::cleanup()
2467 : {
2468 0 : for(int i = 0; i < max; ++i)
2469 : {
2470 0 : glDeleteQueries(1, &queries[i].id);
2471 0 : queries[i].owner = nullptr;
2472 : }
2473 0 : cur = max = defer = 0;
2474 0 : }
2475 :
2476 0 : void occludequery::startquery() const
2477 : {
2478 0 : glBeginQuery(querytarget(), this->id);
2479 0 : }
2480 :
2481 0 : void rendermapmodels()
2482 : {
2483 : static int skipoq = 0;
2484 0 : bool doquery = !drawtex && oqfrags && oqmm;
2485 0 : const std::vector<extentity *> &ents = entities::getents();
2486 0 : findvisiblemms(ents, doquery);
2487 :
2488 0 : for(octaentities *oe = visiblemms; oe; oe = oe->next)
2489 : {
2490 0 : if(oe->distance>=0)
2491 : {
2492 0 : bool rendered = false;
2493 0 : for(size_t i = 0; i < oe->mapmodels.size(); i++)
2494 : {
2495 0 : extentity &e = *ents[oe->mapmodels[i]];
2496 0 : if(!(e.flags&EntFlag_Render))
2497 : {
2498 0 : continue;
2499 : }
2500 0 : if(!rendered)
2501 : {
2502 0 : rendered = true;
2503 0 : oe->query = doquery && oe->distance>0 && !(++skipoq%oqmm) ? occlusionengine.newquery(oe) : nullptr;
2504 0 : if(oe->query)
2505 : {
2506 0 : occlusionengine.setupmodelquery(oe->query);
2507 : }
2508 : }
2509 0 : rendermapmodel(e);
2510 0 : e.flags &= ~EntFlag_Render;
2511 : }
2512 0 : if(rendered && oe->query)
2513 : {
2514 0 : occlusionengine.endmodelquery();
2515 : }
2516 : }
2517 : }
2518 0 : batching::rendermapmodelbatches();
2519 0 : batching::clearbatchedmapmodels();
2520 :
2521 0 : bool queried = false;
2522 0 : for(octaentities *oe = visiblemms; oe; oe = oe->next)
2523 : {
2524 0 : if(oe->distance<0)
2525 : {
2526 0 : oe->query = doquery && !camera1->o.insidebb(oe->bbmin, oe->bbmax, 1) ? occlusionengine.newquery(oe) : nullptr;
2527 0 : if(!oe->query)
2528 : {
2529 0 : continue;
2530 : }
2531 0 : if(!queried)
2532 : {
2533 0 : startbb();
2534 0 : queried = true;
2535 : }
2536 0 : oe->query->startquery();
2537 0 : drawbb(oe->bbmin, ivec(oe->bbmax).sub(oe->bbmin));
2538 0 : occlusionengine.endquery();
2539 : }
2540 : }
2541 0 : if(queried)
2542 : {
2543 0 : endbb();
2544 : }
2545 0 : }
2546 :
2547 0 : void renderoutline()
2548 : {
2549 0 : ldrnotextureshader->set();
2550 :
2551 0 : gle::enablevertex();
2552 :
2553 0 : glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2554 0 : gle::color(outlinecolor);
2555 :
2556 0 : enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
2557 :
2558 0 : if(!dtoutline)
2559 : {
2560 0 : glDisable(GL_DEPTH_TEST);
2561 : }
2562 0 : const vtxarray *prev = nullptr;
2563 0 : for(const vtxarray *va = visibleva; va; va = va->next)
2564 : {
2565 0 : if(va->occluded < Occlude_BB)
2566 : {
2567 0 : if((!va->texs || va->occluded >= Occlude_Geom) && !va->alphaback && !va->alphafront && !va->refracttris)
2568 : {
2569 0 : continue;
2570 : }
2571 0 : if(!prev || va->vbuf != prev->vbuf)
2572 : {
2573 0 : gle::bindvbo(va->vbuf);
2574 0 : gle::bindebo(va->ebuf);
2575 0 : const vertex *ptr = nullptr;
2576 0 : gle::vertexpointer(sizeof(vertex), ptr->pos.data()); //note: intentional dereference of null pointer
2577 : }
2578 0 : if(va->texs && va->occluded < Occlude_Geom)
2579 : {
2580 0 : drawvatris(*va, 3*va->tris, 0);
2581 0 : xtravertsva += va->verts;
2582 : }
2583 0 : if(va->alphaback || va->alphafront || va->refract)
2584 : {
2585 0 : drawvatris(*va, 3*(va->alphabacktris + va->alphafronttris + va->refracttris), 3*(va->tris));
2586 0 : xtravertsva += 3*(va->alphabacktris + va->alphafronttris + va->refracttris);
2587 : }
2588 0 : prev = va;
2589 : }
2590 : }
2591 0 : if(!dtoutline)
2592 : {
2593 0 : glEnable(GL_DEPTH_TEST);
2594 : }
2595 0 : disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
2596 0 : glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
2597 0 : gle::clearvbo();
2598 0 : gle::clearebo();
2599 0 : gle::disablevertex();
2600 0 : }
2601 :
2602 0 : bool renderexplicitsky(bool outline)
2603 : {
2604 0 : const vtxarray *prev = nullptr;
2605 0 : for(const vtxarray *va = visibleva; va; va = va->next)
2606 : {
2607 0 : if(va->sky && va->occluded < Occlude_BB &&
2608 0 : ((va->skymax.x >= 0 && view.isvisiblebb(va->skymin, ivec(va->skymax).sub(va->skymin)) != ViewFrustumCull_NotVisible) ||
2609 0 : !insideworld(camera1->o)))
2610 : {
2611 0 : if(!prev || va->vbuf != prev->vbuf)
2612 : {
2613 0 : if(!prev)
2614 : {
2615 0 : gle::enablevertex();
2616 0 : if(outline)
2617 : {
2618 0 : ldrnotextureshader->set();
2619 0 : gle::color(explicitskycolor);
2620 0 : glDepthMask(GL_FALSE);
2621 0 : enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
2622 0 : glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2623 : }
2624 0 : else if(editmode)
2625 : {
2626 0 : maskgbuffer("d");
2627 0 : SETSHADER(depth);
2628 : }
2629 : else
2630 : {
2631 0 : nocolorshader->set();
2632 0 : glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2633 : }
2634 : }
2635 0 : gle::bindvbo(va->vbuf);
2636 0 : gle::bindebo(va->skybuf);
2637 0 : const vertex *ptr = nullptr;
2638 0 : gle::vertexpointer(sizeof(vertex), ptr->pos.data());
2639 : }
2640 0 : drawvaskytris(*va);
2641 0 : xtraverts += va->sky;
2642 0 : prev = va;
2643 : }
2644 : }
2645 0 : if(!prev)
2646 : {
2647 0 : return false;
2648 : }
2649 0 : if(outline)
2650 : {
2651 0 : glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
2652 0 : disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
2653 0 : glDepthMask(GL_TRUE);
2654 : }
2655 0 : else if(editmode)
2656 : {
2657 0 : maskgbuffer("cnd");
2658 : }
2659 : else
2660 : {
2661 0 : glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2662 : }
2663 0 : gle::disablevertex();
2664 0 : gle::clearvbo();
2665 0 : gle::clearebo();
2666 0 : return true;
2667 : }
2668 :
2669 0 : void cubeworld::cleanupva()
2670 : {
2671 0 : clearvas(*worldroot);
2672 0 : occlusionengine.clearqueries();
2673 0 : cleanupbb();
2674 0 : cleanupgrass();
2675 0 : }
2676 :
2677 0 : GBuffer::AlphaInfo GBuffer::findalphavas()
2678 : {
2679 0 : alphavas.clear();
2680 : AlphaInfo a;
2681 0 : a.alphafrontsx1 = a.alphafrontsy1 = a.alphabacksx1 = a.alphabacksy1 = a.alpharefractsx1 = a.alpharefractsy1 = 1;
2682 0 : a.alphafrontsx2 = a.alphafrontsy2 = a.alphabacksx2 = a.alphabacksy2 = a.alpharefractsx2 = a.alpharefractsy2 = -1;
2683 0 : int alphabackvas = 0,
2684 0 : alpharefractvas = 0;
2685 0 : alphatiles.fill(0);
2686 0 : for(const vtxarray *va = visibleva; va; va = va->next)
2687 : {
2688 0 : if(va->alphabacktris || va->alphafronttris || va->refracttris)
2689 : {
2690 0 : if(va->occluded >= Occlude_BB)
2691 : {
2692 0 : continue;
2693 : }
2694 0 : if(va->curvfc==ViewFrustumCull_Fogged)
2695 : {
2696 0 : continue;
2697 : }
2698 0 : float sx1 = -1,
2699 0 : sx2 = 1,
2700 0 : sy1 = -1,
2701 0 : sy2 = 1;
2702 0 : if(!calcbbscissor(va->alphamin, va->alphamax, sx1, sy1, sx2, sy2))
2703 : {
2704 0 : continue;
2705 : }
2706 0 : alphavas.push_back(va);
2707 0 : masktiles(alphatiles.data(), sx1, sy1, sx2, sy2);
2708 0 : a.alphafrontsx1 = std::min(a.alphafrontsx1, sx1);
2709 0 : a.alphafrontsy1 = std::min(a.alphafrontsy1, sy1);
2710 0 : a.alphafrontsx2 = std::max(a.alphafrontsx2, sx2);
2711 0 : a.alphafrontsy2 = std::max(a.alphafrontsy2, sy2);
2712 0 : if(va->alphabacktris)
2713 : {
2714 0 : alphabackvas++;
2715 0 : a.alphabacksx1 = std::min(a.alphabacksx1, sx1);
2716 0 : a.alphabacksy1 = std::min(a.alphabacksy1, sy1);
2717 0 : a.alphabacksx2 = std::max(a.alphabacksx2, sx2);
2718 0 : a.alphabacksy2 = std::max(a.alphabacksy2, sy2);
2719 : }
2720 0 : if(va->refracttris)
2721 : {
2722 0 : if(!calcbbscissor(va->refractmin, va->refractmax, sx1, sy1, sx2, sy2))
2723 : {
2724 0 : continue;
2725 : }
2726 0 : alpharefractvas++;
2727 0 : a.alpharefractsx1 = std::min(a.alpharefractsx1, sx1);
2728 0 : a.alpharefractsy1 = std::min(a.alpharefractsy1, sy1);
2729 0 : a.alpharefractsx2 = std::max(a.alpharefractsx2, sx2);
2730 0 : a.alpharefractsy2 = std::max(a.alpharefractsy2, sy2);
2731 : }
2732 : }
2733 : }
2734 0 : a.hasalphavas = (alpharefractvas ? 4 : 0) | (alphavas.size() ? 2 : 0) | (alphabackvas ? 1 : 0);
2735 0 : return a;
2736 : }
2737 :
2738 0 : void renderrefractmask()
2739 : {
2740 0 : gle::enablevertex();
2741 :
2742 0 : const vtxarray *prev = nullptr;
2743 0 : for(const vtxarray *va : alphavas)
2744 : {
2745 0 : if(!va->refracttris)
2746 : {
2747 0 : continue;
2748 : }
2749 0 : if(!prev || va->vbuf != prev->vbuf)
2750 : {
2751 0 : gle::bindvbo(va->vbuf);
2752 0 : gle::bindebo(va->ebuf);
2753 0 : const vertex *ptr = nullptr;
2754 0 : gle::vertexpointer(sizeof(vertex), ptr->pos.data()); //note: intentional deref of null pointer
2755 : }
2756 0 : drawvatris(*va, 3*va->refracttris, 3*(va->tris + va->alphabacktris + va->alphafronttris));
2757 0 : xtravertsva += 3*va->refracttris;
2758 0 : prev = va;
2759 : }
2760 :
2761 0 : gle::clearvbo();
2762 0 : gle::clearebo();
2763 0 : gle::disablevertex();
2764 0 : }
2765 :
2766 0 : void renderalphageom(int side)
2767 : {
2768 0 : resetbatches();
2769 :
2770 0 : RenderState cur;
2771 0 : cur.alphaing = side;
2772 0 : cur.invalidatealphascale();
2773 :
2774 0 : setupgeom();
2775 :
2776 0 : if(side == 2)
2777 : {
2778 0 : for(const vtxarray *i : alphavas)
2779 : {
2780 0 : renderva(cur, *i, RenderPass_GBuffer);
2781 : }
2782 0 : if(geombatches.size())
2783 : {
2784 0 : cur.renderbatches(RenderPass_GBuffer);
2785 : }
2786 : }
2787 : else
2788 : {
2789 0 : glCullFace(GL_FRONT);
2790 0 : for(const vtxarray *i : alphavas)
2791 : {
2792 0 : if(i->alphabacktris)
2793 : {
2794 0 : renderva(cur, *i, RenderPass_GBuffer);
2795 : }
2796 : }
2797 0 : if(geombatches.size())
2798 : {
2799 0 : cur.renderbatches(RenderPass_GBuffer);
2800 : }
2801 0 : glCullFace(GL_BACK);
2802 : }
2803 :
2804 0 : cur.cleanupgeom();
2805 0 : }
2806 :
2807 0 : void GBuffer::rendergeom()
2808 : {
2809 0 : bool doOQ = oqfrags && oqgeom && !drawtex,
2810 0 : multipassing = false;
2811 0 : RenderState cur;
2812 :
2813 0 : if(doOQ)
2814 : {
2815 0 : for(vtxarray *va = visibleva; va; va = va->next)
2816 : {
2817 0 : if(va->texs)
2818 : {
2819 0 : if(!camera1->o.insidebb(va->o, va->size, 2))
2820 : {
2821 0 : if(va->parent && va->parent->occluded >= Occlude_BB)
2822 : {
2823 0 : va->query = nullptr;
2824 0 : va->occluded = Occlude_Parent;
2825 0 : continue;
2826 : }
2827 0 : va->occluded = va->query && va->query->owner == va && occlusionengine.checkquery(va->query) ?
2828 0 : std::min(va->occluded+1, static_cast<int>(Occlude_BB)) :
2829 : Occlude_Nothing;
2830 0 : va->query = occlusionengine.newquery(va);
2831 0 : if(!va->query || !va->occluded)
2832 : {
2833 0 : va->occluded = Occlude_Nothing;
2834 : }
2835 0 : if(va->occluded >= Occlude_Geom)
2836 : {
2837 0 : if(va->query)
2838 : {
2839 0 : if(cur.vattribs)
2840 : {
2841 0 : cur.disablevattribs(false);
2842 : }
2843 0 : if(cur.vbuf)
2844 : {
2845 0 : cur.disablevbuf();
2846 : }
2847 0 : renderquery(cur, *va->query, *va);
2848 : }
2849 0 : continue;
2850 : }
2851 : }
2852 : else
2853 : {
2854 0 : va->query = nullptr;
2855 0 : va->occluded = Occlude_Nothing;
2856 0 : if(va->occluded >= Occlude_Geom)
2857 : {
2858 0 : continue;
2859 : }
2860 : }
2861 0 : renderva(cur, *va, RenderPass_Z, true);
2862 : }
2863 : }
2864 :
2865 0 : if(cur.vquery)
2866 : {
2867 0 : cur.disablevquery();
2868 : }
2869 0 : if(cur.vattribs)
2870 : {
2871 0 : cur.disablevattribs(false);
2872 : }
2873 0 : if(cur.vbuf)
2874 : {
2875 0 : cur.disablevbuf();
2876 : }
2877 0 : glFlush();
2878 0 : if(cur.colormask)
2879 : {
2880 0 : cur.colormask = false;
2881 0 : glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2882 : }
2883 0 : if(cur.depthmask)
2884 : {
2885 0 : cur.depthmask = false;
2886 0 : glDepthMask(GL_FALSE);
2887 : }
2888 0 : workinoq();
2889 0 : if(!cur.colormask)
2890 : {
2891 0 : cur.colormask = true;
2892 0 : glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2893 : }
2894 0 : if(!cur.depthmask)
2895 : {
2896 0 : cur.depthmask = true;
2897 0 : glDepthMask(GL_TRUE);
2898 : }
2899 0 : if(!multipassing)
2900 : {
2901 0 : multipassing = true;
2902 0 : glDepthFunc(GL_LEQUAL);
2903 : }
2904 0 : cur.invalidatetexgenorient();
2905 0 : setupgeom();
2906 0 : resetbatches();
2907 0 : for(const vtxarray *va = visibleva; va; va = va->next)
2908 : {
2909 0 : if(va->texs && va->occluded < Occlude_Geom)
2910 : {
2911 0 : renderva(cur, *va, RenderPass_GBuffer);
2912 : }
2913 : }
2914 0 : if(geombatches.size())
2915 : {
2916 0 : cur.renderbatches(RenderPass_GBuffer);
2917 0 : glFlush();
2918 : }
2919 0 : for(vtxarray *va = visibleva; va; va = va->next)
2920 : {
2921 0 : if(va->texs && va->occluded >= Occlude_Geom)
2922 : {
2923 0 : if((va->parent && va->parent->occluded >= Occlude_BB) || (va->query && occlusionengine.checkquery(va->query)))
2924 : {
2925 0 : va->occluded = Occlude_BB;
2926 0 : continue;
2927 : }
2928 : else
2929 : {
2930 0 : va->occluded = Occlude_Nothing;
2931 0 : if(va->occluded >= Occlude_Geom)
2932 : {
2933 0 : continue;
2934 : }
2935 : }
2936 :
2937 0 : renderva(cur, *va, RenderPass_GBuffer);
2938 : }
2939 : }
2940 0 : if(geombatches.size())
2941 : {
2942 0 : cur.renderbatches(RenderPass_GBuffer);
2943 : }
2944 : }
2945 : else
2946 : {
2947 0 : setupgeom();
2948 0 : resetbatches();
2949 0 : for(vtxarray *va = visibleva; va; va = va->next)
2950 : {
2951 0 : if(va->texs)
2952 : {
2953 0 : va->query = nullptr;
2954 0 : va->occluded = Occlude_Nothing;
2955 0 : if(va->occluded >= Occlude_Geom)
2956 : {
2957 0 : continue;
2958 : }
2959 0 : renderva(cur, *va, RenderPass_GBuffer);
2960 : }
2961 : }
2962 0 : if(geombatches.size())
2963 : {
2964 0 : cur.renderbatches(RenderPass_GBuffer);
2965 : }
2966 : }
2967 0 : if(multipassing)
2968 : {
2969 0 : glDepthFunc(GL_LESS);
2970 : }
2971 0 : cur.cleanupgeom();
2972 0 : if(!doOQ)
2973 : {
2974 0 : glFlush();
2975 0 : if(cur.colormask)
2976 : {
2977 0 : cur.colormask = false;
2978 0 : glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2979 : }
2980 0 : if(cur.depthmask)
2981 : {
2982 0 : cur.depthmask = false;
2983 0 : glDepthMask(GL_FALSE);
2984 : }
2985 0 : workinoq();
2986 0 : if(!cur.colormask)
2987 : {
2988 0 : cur.colormask = true;
2989 0 : glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2990 : }
2991 0 : if(!cur.depthmask)
2992 : {
2993 0 : cur.depthmask = true;
2994 0 : glDepthMask(GL_TRUE);
2995 : }
2996 : }
2997 0 : }
2998 :
2999 0 : void renderdecals()
3000 : {
3001 : const vtxarray *decalva;
3002 0 : for(decalva = visibleva; decalva; decalva = decalva->next)
3003 : {
3004 0 : if(decalva->decaltris && decalva->occluded < Occlude_BB)
3005 : {
3006 0 : break;
3007 : }
3008 : }
3009 0 : if(!decalva)
3010 : {
3011 0 : return;
3012 : }
3013 0 : decalrenderer cur;
3014 :
3015 0 : setupdecals();
3016 0 : resetdecalbatches();
3017 :
3018 0 : if(maxdualdrawbufs)
3019 : {
3020 0 : glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC1_ALPHA);
3021 0 : maskgbuffer("c");
3022 0 : for(const vtxarray *va = decalva; va; va = va->next)
3023 : {
3024 0 : if(va->decaltris && va->occluded < Occlude_BB)
3025 : {
3026 0 : mergedecals(*va);
3027 0 : if(!batchdecals && decalbatches.size())
3028 : {
3029 0 : cur.renderdecalbatches(0);
3030 : }
3031 : }
3032 : }
3033 0 : if(decalbatches.size())
3034 : {
3035 0 : cur.renderdecalbatches(0);
3036 : }
3037 0 : if(usepacknorm())
3038 : {
3039 0 : glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3040 0 : glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
3041 : }
3042 : else
3043 : {
3044 0 : glBlendFunc(GL_SRC1_ALPHA, GL_ONE_MINUS_SRC1_ALPHA);
3045 : }
3046 0 : maskgbuffer("n");
3047 0 : cur.vbuf = 0;
3048 0 : for(const vtxarray *va = decalva; va; va = va->next)
3049 : {
3050 0 : if(va->decaltris && va->occluded < Occlude_BB)
3051 : {
3052 0 : mergedecals(*va);
3053 0 : if(!batchdecals && decalbatches.size())
3054 : {
3055 0 : cur.renderdecalbatches(1);
3056 : }
3057 : }
3058 : }
3059 0 : if(decalbatches.size())
3060 : {
3061 0 : cur.renderdecalbatches(1);
3062 : }
3063 : }
3064 : else
3065 : {
3066 0 : glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
3067 0 : glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
3068 0 : maskgbuffer("cn");
3069 0 : for(const vtxarray *va = decalva; va; va = va->next)
3070 : {
3071 0 : if(va->decaltris && va->occluded < Occlude_BB)
3072 : {
3073 0 : mergedecals(*va);
3074 0 : if(!batchdecals && decalbatches.size())
3075 : {
3076 0 : cur.renderdecalbatches(0);
3077 : }
3078 : }
3079 : }
3080 0 : if(decalbatches.size())
3081 : {
3082 0 : cur.renderdecalbatches(0);
3083 : }
3084 : }
3085 0 : cleanupdecals();
3086 : }
3087 :
3088 : //shadowmeshes
3089 :
3090 1 : void clearshadowmeshes()
3091 : {
3092 1 : if(shadowvbos.size())
3093 : {
3094 0 : glDeleteBuffers(shadowvbos.size(), shadowvbos.data());
3095 0 : shadowvbos.clear();
3096 : }
3097 1 : if(shadowmeshes.size())
3098 : {
3099 0 : std::vector<extentity *> &ents = entities::getents();
3100 0 : for(size_t i = 0; i < ents.size(); i++)
3101 : {
3102 0 : extentity &e = *ents[i];
3103 0 : if(e.flags&EntFlag_ShadowMesh)
3104 : {
3105 0 : e.flags &= ~EntFlag_ShadowMesh;
3106 : }
3107 : }
3108 : }
3109 1 : shadowmeshes.clear();
3110 1 : shadowdraws.clear();
3111 1 : }
3112 :
3113 0 : void genshadowmeshes()
3114 : {
3115 0 : clearshadowmeshes();
3116 :
3117 0 : if(!smmesh)
3118 : {
3119 0 : return;
3120 : }
3121 0 : renderprogress(0, "generating shadow meshes..");
3122 :
3123 0 : std::vector<extentity *> &ents = entities::getents();
3124 0 : for(size_t i = 0; i < ents.size(); i++)
3125 : {
3126 0 : const extentity &e = *ents[i];
3127 0 : if(e.type != EngineEnt_Light)
3128 : {
3129 0 : continue;
3130 : }
3131 0 : genshadowmesh(i, e);
3132 : }
3133 : }
3134 :
3135 : /**
3136 : * @brief Attemps to find a shadow mesh corresponding to the given index.
3137 : *
3138 : * The second parameter is ignored if the shadowmesh is not associated with a spotlight.
3139 : *
3140 : * @param idx the index associated with the shadowmesh
3141 : * @param e the associated extentity with the shadowmesh (if the shadowmesh is a spotlight)
3142 : */
3143 0 : shadowmesh *findshadowmesh(int idx, const extentity &e)
3144 : {
3145 0 : std::unordered_map<int, shadowmesh>::iterator itr = shadowmeshes.find(idx);
3146 0 : if(itr == shadowmeshes.end()
3147 0 : || (*itr).second.type != shadowmapping
3148 0 : || (*itr).second.origin != shadoworigin
3149 0 : || (*itr).second.radius < shadowradius)
3150 : {
3151 0 : return nullptr;
3152 : }
3153 0 : switch((*itr).second.type)
3154 : {
3155 0 : case ShadowMap_Spot:
3156 : {
3157 0 : if(!e.attached || e.attached->type != EngineEnt_Spotlight || (*itr).second.spotloc != e.attached->o || (*itr).second.spotangle < std::clamp(static_cast<int>(e.attached->attr1), 1, 89))
3158 : {
3159 0 : return nullptr;
3160 : }
3161 0 : break;
3162 : }
3163 : }
3164 0 : return &(*itr).second;
3165 : }
3166 :
3167 0 : void rendershadowmesh(const shadowmesh *m)
3168 : {
3169 0 : int draw = m->draws[shadowside];
3170 0 : if(draw < 0)
3171 : {
3172 0 : return;
3173 : }
3174 0 : SETSHADER(shadowmapworld);
3175 0 : gle::enablevertex();
3176 0 : GLuint ebuf = 0,
3177 0 : vbuf = 0;
3178 0 : while(draw >= 0)
3179 : {
3180 0 : shadowdraw &d = shadowdraws[draw];
3181 0 : if(ebuf != d.ebuf)
3182 : {
3183 0 : gle::bindebo(d.ebuf);
3184 0 : ebuf = d.ebuf;
3185 : }
3186 0 : if(vbuf != d.vbuf)
3187 : {
3188 0 : gle::bindvbo(d.vbuf);
3189 0 : vbuf = d.vbuf;
3190 0 : gle::vertexpointer(sizeof(vec), 0);
3191 : }
3192 0 : drawtris(3*d.tris, static_cast<ushort *>(nullptr) + d.offset, d.minvert, d.maxvert);
3193 0 : xtravertsva += 3*d.tris;
3194 0 : draw = d.next;
3195 : }
3196 0 : gle::disablevertex();
3197 0 : gle::clearebo();
3198 0 : gle::clearvbo();
3199 : }
3200 :
3201 : //external api functions
3202 0 : int calcspheresidemask(const vec &p, float radius, float bias)
3203 : {
3204 : // p is in the cubemap's local coordinate system
3205 : // bias = border/(size - border)
3206 0 : float dxyp = p.x + p.y,
3207 0 : dxyn = p.x - p.y,
3208 0 : axyp = std::fabs(dxyp),
3209 0 : axyn = std::fabs(dxyn),
3210 0 : dyzp = p.y + p.z,
3211 0 : dyzn = p.y - p.z,
3212 0 : ayzp = std::fabs(dyzp),
3213 0 : ayzn = std::fabs(dyzn),
3214 0 : dzxp = p.z + p.x,
3215 0 : dzxn = p.z - p.x,
3216 0 : azxp = std::fabs(dzxp),
3217 0 : azxn = std::fabs(dzxn);
3218 0 : int mask = 0x3F;
3219 0 : radius *= SQRT2;
3220 0 : if(axyp > bias*axyn + radius)
3221 : {
3222 0 : mask &= dxyp < 0 ? ~((1<<0)|(1<<2)) : ~((2<<0)|(2<<2));
3223 : }
3224 0 : if(axyn > bias*axyp + radius)
3225 : {
3226 0 : mask &= dxyn < 0 ? ~((1<<0)|(2<<2)) : ~((2<<0)|(1<<2));
3227 : }
3228 0 : if(ayzp > bias*ayzn + radius)
3229 : {
3230 0 : mask &= dyzp < 0 ? ~((1<<2)|(1<<4)) : ~((2<<2)|(2<<4));
3231 : }
3232 0 : if(ayzn > bias*ayzp + radius)
3233 : {
3234 0 : mask &= dyzn < 0 ? ~((1<<2)|(2<<4)) : ~((2<<2)|(1<<4));
3235 : }
3236 0 : if(azxp > bias*azxn + radius)
3237 : {
3238 0 : mask &= dzxp < 0 ? ~((1<<4)|(1<<0)) : ~((2<<4)|(2<<0));
3239 : }
3240 0 : if(azxn > bias*azxp + radius)
3241 : {
3242 0 : mask &= dzxn < 0 ? ~((1<<4)|(2<<0)) : ~((2<<4)|(1<<0));
3243 : }
3244 0 : return mask;
3245 : }
3246 :
3247 0 : int vfc::cullfrustumsides(const vec &lightpos, float lightradius, float size, float border)
3248 : {
3249 0 : int sides = 0x3F;
3250 0 : std::array<int, 6> masks = {{ 3<<4, 3<<4, 3<<0, 3<<0, 3<<2, 3<<2 }};
3251 0 : float scale = (size - 2*border)/size,
3252 0 : bias = border / static_cast<float>(size - border);
3253 : // check if cone enclosing side would cross frustum plane
3254 0 : scale = 2 / (scale*scale + 2);
3255 0 : for(int i = 0; i < 5; ++i)
3256 : {
3257 0 : if(vfcP[i].dist(lightpos) <= -0.03125f)
3258 : {
3259 0 : vec n = vec(vfcP[i]).div(lightradius);
3260 0 : float len = scale*n.squaredlen();
3261 0 : if(n.x*n.x > len)
3262 : {
3263 0 : sides &= n.x < 0 ? ~(1<<0) : ~(2 << 0);
3264 : }
3265 0 : if(n.y*n.y > len)
3266 : {
3267 0 : sides &= n.y < 0 ? ~(1<<2) : ~(2 << 2);
3268 : }
3269 0 : if(n.z*n.z > len)
3270 : {
3271 0 : sides &= n.z < 0 ? ~(1<<4) : ~(2 << 4);
3272 : }
3273 : }
3274 : }
3275 0 : if (vfcP[4].dist(lightpos) >= vfcDfog + 0.03125f)
3276 : {
3277 0 : vec n = vec(vfcP[4]).div(lightradius);
3278 0 : float len = scale*n.squaredlen();
3279 0 : if(n.x*n.x > len)
3280 : {
3281 0 : sides &= n.x >= 0 ? ~(1<<0) : ~(2 << 0);
3282 : }
3283 0 : if(n.y*n.y > len)
3284 : {
3285 0 : sides &= n.y >= 0 ? ~(1<<2) : ~(2 << 2);
3286 : }
3287 0 : if(n.z*n.z > len)
3288 : {
3289 0 : sides &= n.z >= 0 ? ~(1<<4) : ~(2 << 4);
3290 : }
3291 : }
3292 : // this next test usually clips off more sides than the former, but occasionally clips fewer/different ones, so do both and combine results
3293 : // check if frustum corners/origin cross plane sides
3294 : // infinite version, assumes frustum corners merely give direction and extend to infinite distance
3295 0 : vec p = vec(camera1->o).sub(lightpos).div(lightradius);
3296 0 : float dp = p.x + p.y,
3297 0 : dn = p.x - p.y,
3298 0 : ap = std::fabs(dp),
3299 0 : an = std::fabs(dn);
3300 0 : masks[0] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2));
3301 0 : masks[1] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2));
3302 0 : dp = p.y + p.z, dn = p.y - p.z,
3303 0 : ap = std::fabs(dp),
3304 0 : an = std::fabs(dn);
3305 0 : masks[2] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4));
3306 0 : masks[3] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4));
3307 0 : dp = p.z + p.x,
3308 0 : dn = p.z - p.x,
3309 0 : ap = std::fabs(dp),
3310 0 : an = std::fabs(dn);
3311 0 : masks[4] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0));
3312 0 : masks[5] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0));
3313 0 : for(int i = 0; i < 4; ++i)
3314 : {
3315 0 : vec n;
3316 0 : switch(i)
3317 : {
3318 0 : case 0:
3319 : {
3320 0 : n.cross(vfcP[0], vfcP[2]);
3321 0 : break;
3322 : }
3323 0 : case 1:
3324 : {
3325 0 : n.cross(vfcP[3], vfcP[0]);
3326 0 : break;
3327 : }
3328 0 : case 2:
3329 : {
3330 0 : n.cross(vfcP[2], vfcP[1]);
3331 0 : break;
3332 : }
3333 0 : case 3:
3334 : {
3335 0 : n.cross(vfcP[1], vfcP[3]);
3336 0 : break;
3337 : }
3338 : }
3339 0 : dp = n.x + n.y,
3340 0 : dn = n.x - n.y,
3341 0 : ap = std::fabs(dp),
3342 0 : an = std::fabs(dn);
3343 0 : if(ap > 0)
3344 : {
3345 0 : masks[0] |= dp >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2);
3346 : }
3347 0 : if(an > 0)
3348 : {
3349 0 : masks[1] |= dn >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2);
3350 : }
3351 0 : dp = n.y + n.z,
3352 0 : dn = n.y - n.z,
3353 0 : ap = std::fabs(dp),
3354 0 : an = std::fabs(dn);
3355 0 : if(ap > 0)
3356 : {
3357 0 : masks[2] |= dp >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4);
3358 : }
3359 0 : if(an > 0)
3360 : {
3361 0 : masks[3] |= dn >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4);
3362 : }
3363 0 : dp = n.z + n.x,
3364 0 : dn = n.z - n.x,
3365 0 : ap = std::fabs(dp),
3366 0 : an = std::fabs(dn);
3367 0 : if(ap > 0)
3368 : {
3369 0 : masks[4] |= dp >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0);
3370 : }
3371 0 : if(an > 0)
3372 : {
3373 0 : masks[5] |= dn >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0);
3374 : }
3375 : }
3376 0 : return sides & masks[0] & masks[1] & masks[2] & masks[3] & masks[4] & masks[5];
3377 : }
3378 :
3379 0 : static void findshadowvas(std::vector<vtxarray *> &vas, std::array<vtxarray *, vasortsize> &vasort)
3380 : {
3381 0 : for(vtxarray *&v : vas)
3382 : {
3383 0 : float dist = vadist(*v, shadoworigin);
3384 0 : if(dist < shadowradius || !smdistcull)
3385 : {
3386 0 : v->shadowmask = !smbbcull ? 0x3F : (v->children.size() || v->mapmodels.size() ?
3387 0 : calcbbsidemask(v->bbmin, v->bbmax, shadoworigin, shadowradius, shadowbias) :
3388 0 : calcbbsidemask(v->geommin, v->geommax, shadoworigin, shadowradius, shadowbias));
3389 0 : addshadowva(v, dist, vasort);
3390 0 : if(v->children.size())
3391 : {
3392 0 : findshadowvas(v->children, vasort);
3393 : }
3394 : }
3395 : }
3396 0 : }
3397 :
3398 0 : void renderrsmgeom(bool dyntex)
3399 : {
3400 0 : RenderState cur;
3401 0 : if(!dyntex)
3402 : {
3403 0 : cur.cleartexgenmillis();
3404 : }
3405 0 : setupgeom();
3406 0 : if(skyshadow)
3407 : {
3408 0 : cur.enablevattribs(false);
3409 0 : SETSHADER(rsmsky);
3410 0 : vtxarray *prev = nullptr;
3411 0 : for(vtxarray *va = shadowva; va; va = va->rnext)
3412 : {
3413 0 : if(va->sky)
3414 : {
3415 0 : if(!prev || va->vbuf != prev->vbuf)
3416 : {
3417 0 : gle::bindvbo(va->vbuf);
3418 0 : gle::bindebo(va->skybuf);
3419 0 : const vertex *ptr = nullptr; //note: offset of nullptr is technically UB
3420 0 : gle::vertexpointer(sizeof(vertex), ptr->pos.data());
3421 : }
3422 0 : drawvaskytris(*va);
3423 0 : xtravertsva += va->sky/3;
3424 0 : prev = va;
3425 : }
3426 : }
3427 0 : if(cur.vattribs)
3428 : {
3429 0 : cur.disablevattribs(false);
3430 : }
3431 : }
3432 0 : resetbatches();
3433 0 : for(vtxarray *va = shadowva; va; va = va->rnext)
3434 : {
3435 0 : if(va->texs)
3436 : {
3437 0 : renderva(cur, *va, RenderPass_ReflectiveShadowMap);
3438 : }
3439 : }
3440 0 : if(geombatches.size())
3441 : {
3442 0 : cur.renderbatches(RenderPass_ReflectiveShadowMap);
3443 : }
3444 0 : cur.cleanupgeom();
3445 0 : }
3446 :
3447 0 : void dynamicshadowvabounds(int mask, vec &bbmin, vec &bbmax)
3448 : {
3449 0 : for(vtxarray *va = shadowva; va; va = va->rnext)
3450 : {
3451 0 : if(va->shadowmask&mask && va->dyntexs)
3452 : {
3453 0 : bbmin.min(vec(va->geommin));
3454 0 : bbmax.max(vec(va->geommax));
3455 : }
3456 : }
3457 0 : }
3458 :
3459 0 : void findshadowmms()
3460 : {
3461 0 : shadowmms = nullptr;
3462 0 : octaentities **lastmms = &shadowmms;
3463 0 : for(vtxarray *va = shadowva; va; va = va->rnext)
3464 : {
3465 0 : for(size_t j = 0; j < va->mapmodels.size(); j++)
3466 : {
3467 0 : octaentities *oe = va->mapmodels[j];
3468 0 : switch(shadowmapping)
3469 : {
3470 0 : case ShadowMap_Reflect:
3471 : {
3472 0 : break;
3473 : }
3474 0 : case ShadowMap_Cascade:
3475 : {
3476 0 : if(!csm.calcbbsplits(oe->bbmin, oe->bbmax))
3477 : {
3478 0 : continue;
3479 : }
3480 0 : break;
3481 : }
3482 0 : case ShadowMap_CubeMap:
3483 : {
3484 0 : if(smdistcull && shadoworigin.dist_to_bb(oe->bbmin, oe->bbmax) >= shadowradius)
3485 : {
3486 0 : continue;
3487 : }
3488 0 : break;
3489 : }
3490 0 : case ShadowMap_Spot:
3491 : {
3492 0 : if(smdistcull && shadoworigin.dist_to_bb(oe->bbmin, oe->bbmax) >= shadowradius)
3493 : {
3494 0 : continue;
3495 : }
3496 0 : if(smbbcull && !bbinsidespot(shadoworigin, shadowdir, shadowspot, oe->bbmin, oe->bbmax))
3497 : {
3498 0 : continue;
3499 : }
3500 0 : break;
3501 : }
3502 : }
3503 0 : oe->rnext = nullptr;
3504 0 : *lastmms = oe;
3505 0 : lastmms = &oe->rnext;
3506 : }
3507 : }
3508 0 : }
3509 :
3510 0 : void rendershadowmapworld()
3511 : {
3512 0 : SETSHADER(shadowmapworld);
3513 :
3514 0 : gle::enablevertex();
3515 :
3516 0 : vtxarray *prev = nullptr;
3517 0 : for(vtxarray *va = shadowva; va; va = va->rnext)
3518 : {
3519 0 : if(va->tris && va->shadowmask&(1<<shadowside))
3520 : {
3521 0 : if(!prev || va->vbuf != prev->vbuf)
3522 : {
3523 0 : gle::bindvbo(va->vbuf);
3524 0 : gle::bindebo(va->ebuf);
3525 0 : const vertex *ptr = nullptr;
3526 0 : gle::vertexpointer(sizeof(vertex), ptr->pos.data());
3527 : }
3528 0 : if(!smnodraw)
3529 : {
3530 0 : drawvatris(*va, 3*va->tris, 0);
3531 : }
3532 0 : xtravertsva += va->verts;
3533 0 : prev = va;
3534 : }
3535 : }
3536 0 : if(skyshadow)
3537 : {
3538 0 : prev = nullptr;
3539 0 : for(vtxarray *va = shadowva; va; va = va->rnext)
3540 : {
3541 0 : if(va->sky && va->shadowmask&(1<<shadowside))
3542 : {
3543 0 : if(!prev || va->vbuf != prev->vbuf)
3544 : {
3545 0 : gle::bindvbo(va->vbuf);
3546 0 : gle::bindebo(va->skybuf);
3547 0 : const vertex *ptr = nullptr;
3548 0 : gle::vertexpointer(sizeof(vertex), ptr->pos.data()); //note offset from nullptr
3549 : }
3550 0 : if(!smnodraw)
3551 : {
3552 0 : drawvaskytris(*va);
3553 : }
3554 0 : xtravertsva += va->sky/3;
3555 0 : prev = va;
3556 : }
3557 : }
3558 : }
3559 :
3560 0 : gle::clearvbo();
3561 0 : gle::clearebo();
3562 0 : gle::disablevertex();
3563 0 : }
3564 :
3565 0 : void batchshadowmapmodels(bool skipmesh)
3566 : {
3567 0 : if(!shadowmms)
3568 : {
3569 0 : return;
3570 : }
3571 0 : int nflags = EntFlag_NoVis|EntFlag_NoShadow;
3572 0 : if(skipmesh)
3573 : {
3574 0 : nflags |= EntFlag_ShadowMesh;
3575 : }
3576 0 : const std::vector<extentity *> &ents = entities::getents();
3577 0 : for(const octaentities *oe = shadowmms; oe; oe = oe->rnext)
3578 : {
3579 0 : for(const int &k : oe->mapmodels)
3580 : {
3581 0 : extentity &e = *ents[k];
3582 0 : if(e.flags&nflags)
3583 : {
3584 0 : continue;
3585 : }
3586 0 : e.flags |= EntFlag_Render;
3587 : }
3588 : }
3589 0 : for(const octaentities *oe = shadowmms; oe; oe = oe->rnext)
3590 : {
3591 0 : for(const int &j : oe->mapmodels)
3592 : {
3593 0 : extentity &e = *ents[j];
3594 0 : if(!(e.flags&EntFlag_Render))
3595 : {
3596 0 : continue;
3597 : }
3598 0 : rendermapmodel(e);
3599 0 : e.flags &= ~EntFlag_Render;
3600 : }
3601 : }
3602 : }
3603 :
3604 0 : void vtxarray::findrsmshadowvas(std::array<vtxarray *, vasortsize> &vasort)
3605 : {
3606 0 : ivec bbmin, bbmax;
3607 0 : if(children.size() || mapmodels.size())
3608 : {
3609 0 : bbmin = bbmin;
3610 0 : bbmax = bbmax;
3611 : }
3612 : else
3613 : {
3614 0 : bbmin = geommin;
3615 0 : bbmax = geommax;
3616 : }
3617 0 : shadowmask = calcbbrsmsplits(bbmin, bbmax);
3618 0 : if(shadowmask)
3619 : {
3620 0 : float dist = shadowdir.project_bb(bbmin, bbmax) - shadowbias;
3621 0 : addshadowva(this, dist, vasort);
3622 0 : for(vtxarray *child : children)
3623 : {
3624 0 : child->findrsmshadowvas(vasort);
3625 : }
3626 : }
3627 0 : }
3628 :
3629 0 : void vtxarray::findcsmshadowvas(std::array<vtxarray *, vasortsize> &vasort)
3630 : {
3631 0 : ivec bbmin, bbmax;
3632 0 : if(children.size() || mapmodels.size())
3633 : {
3634 0 : bbmin = bbmin;
3635 0 : bbmax = bbmax;
3636 : }
3637 : else
3638 : {
3639 0 : bbmin = geommin;
3640 0 : bbmax = geommax;
3641 : }
3642 0 : shadowmask = csm.calcbbsplits(bbmin, bbmax);
3643 0 : if(shadowmask)
3644 : {
3645 0 : float dist = shadowdir.project_bb(bbmin, bbmax) - shadowbias;
3646 0 : addshadowva(this, dist, vasort);
3647 0 : for(vtxarray *child : children)
3648 : {
3649 0 : child->findcsmshadowvas(vasort);
3650 : }
3651 : }
3652 0 : }
3653 :
3654 0 : void vtxarray::findspotshadowvas(std::array<vtxarray *, vasortsize> &vasort)
3655 : {
3656 0 : float dist = vadist(*this, shadoworigin);
3657 0 : if(dist < shadowradius || !smdistcull)
3658 : {
3659 0 : shadowmask = !smbbcull || (children.size() || mapmodels.size() ?
3660 0 : bbinsidespot(shadoworigin, shadowdir, shadowspot, bbmin, bbmax) :
3661 0 : bbinsidespot(shadoworigin, shadowdir, shadowspot, geommin, geommax)) ? 1 : 0;
3662 0 : addshadowva(this, dist, vasort);
3663 0 : for(vtxarray *child : children)
3664 : {
3665 0 : child->findspotshadowvas(vasort);
3666 : }
3667 : }
3668 0 : }
3669 :
3670 0 : void findshadowvas()
3671 : {
3672 : std::array<vtxarray *, vasortsize> vasort;
3673 0 : vasort.fill(nullptr);
3674 0 : switch(shadowmapping)
3675 : {
3676 0 : case ShadowMap_Reflect:
3677 : {
3678 0 : for(size_t i = 0; i < varoot.size(); ++i)
3679 : {
3680 0 : varoot[i]->findrsmshadowvas(vasort);
3681 : }
3682 0 : break;
3683 : }
3684 0 : case ShadowMap_CubeMap:
3685 : {
3686 0 : findshadowvas(varoot, vasort);
3687 0 : break;
3688 : }
3689 0 : case ShadowMap_Cascade:
3690 : {
3691 0 : for(size_t i = 0; i < varoot.size(); ++i)
3692 : {
3693 0 : varoot[i]->findcsmshadowvas(vasort);
3694 : }
3695 0 : break;
3696 : }
3697 0 : case ShadowMap_Spot:
3698 : {
3699 0 : for(size_t i = 0; i < varoot.size(); ++i)
3700 : {
3701 0 : varoot[i]->findspotshadowvas(vasort);
3702 : }
3703 0 : break;
3704 : }
3705 : }
3706 0 : sortshadowvas(vasort);
3707 0 : }
|