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