Line data Source code
1 : // octarender.cpp: fill vertex arrays with different cube surfaces.
2 : #include "../libprimis-headers/cube.h"
3 : #include "../../shared/geomexts.h"
4 : #include "../../shared/glemu.h"
5 : #include "../../shared/glexts.h"
6 :
7 : #include "grass.h"
8 : #include "octarender.h"
9 : #include "rendergl.h"
10 : #include "renderlights.h"
11 : #include "renderparticles.h"
12 : #include "rendersky.h"
13 : #include "renderva.h"
14 : #include "shader.h"
15 : #include "shaderparam.h"
16 : #include "texture.h"
17 : #include "vacollect.h"
18 :
19 : #include "interface/menus.h"
20 :
21 : #include "world/entities.h"
22 : #include "world/light.h"
23 : #include "world/material.h"
24 : #include "world/octaworld.h"
25 : #include "world/world.h"
26 :
27 :
28 : /* global variables */
29 : //////////////////////
30 :
31 : int allocva = 0,
32 : wtris = 0,
33 : wverts = 0,
34 : vtris = 0,
35 : vverts = 0,
36 : glde = 0,
37 : gbatches = 0;
38 :
39 :
40 : /* A vector that carries identically all elements also in the various varoot objects.
41 : * The entries in the vector will first be the children of varoot[0] followed by
42 : * varoot[0] itself, followed by the same for the other VAs in varoot. The last
43 : * element should always be `varoot[7]`.
44 : */
45 : std::vector<vtxarray *> valist;
46 :
47 : /*
48 : * A vector containing the highest-level vertex array objects.
49 : * There will always be at least eight VAs in varoot, corresponding to the eight
50 : * subdivisions of the worldroot cube.
51 : *
52 : * If the eight subdivisions of the worldroot cube are larger than `vamaxsize`,
53 : * these cubes will not host the root VA; children of these cubes will be assigned
54 : * the root VAs by dropping through child nodes until the cube is no larger than
55 : * `vamaxsize`. For a worldroot 4x the linear size of `vamaxsize` this will yield 64
56 : * entries in varoot; for a worldroot 8x the linear size, this will yield 512 entries
57 : * and so on.
58 : */
59 : std::vector<vtxarray *> varoot;
60 :
61 : ivec worldmin(0, 0, 0),
62 : worldmax(0, 0, 0);
63 :
64 : std::vector<tjoint> tjoints;
65 :
66 0 : VARFP(filltjoints, 0, 1, 1, rootworld.allchanged()); //eliminate "sparklies" by filling in geom t-joints
67 :
68 : /* internally relevant functionality */
69 : ///////////////////////////////////////
70 :
71 : //edgegroup: struct used for tjoint joining (to reduce sparklies between geom faces)
72 : struct edgegroup final
73 : {
74 : ivec slope, origin;
75 : int axis;
76 :
77 : edgegroup();
78 :
79 0 : bool operator==(const edgegroup &y) const
80 : {
81 0 : return slope==y.slope && origin==y.origin;
82 : }
83 : };
84 :
85 0 : edgegroup::edgegroup()
86 : {
87 0 : axis = 0;
88 0 : }
89 :
90 : template<>
91 : struct std::hash<edgegroup> final
92 : {
93 0 : size_t operator()(const edgegroup &g) const
94 : {
95 0 : return g.slope.x^g.slope.y^g.slope.z^g.origin.x^g.origin.y^g.origin.z;
96 : }
97 : };
98 :
99 : namespace
100 : {
101 : enum
102 : {
103 : CubeEdge_Start = 1<<0,
104 : CubeEdge_End = 1<<1,
105 : CubeEdge_Flip = 1<<2,
106 : CubeEdge_Dup = 1<<3
107 : };
108 :
109 : struct cubeedge final
110 : {
111 : cube *c;
112 : int next, offset;
113 : ushort size;
114 : uchar index, flags;
115 : };
116 :
117 : std::vector<cubeedge> cubeedges;
118 : std::unordered_map<edgegroup, int> edgegroups;
119 :
120 0 : void gencubeedges(cube &c, const ivec &co, int size)
121 : {
122 0 : std::array<ivec, Face_MaxVerts> pos;
123 : int vis;
124 0 : for(int i = 0; i < 6; ++i)
125 : {
126 0 : if((vis = visibletris(c, i, co, size)))
127 : {
128 0 : int numverts = c.ext ? c.ext->surfaces[i].numverts&Face_MaxVerts : 0;
129 0 : if(numverts)
130 : {
131 0 : const vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
132 0 : ivec vo = ivec(co).mask(~0xFFF).shl(3);
133 0 : for(int j = 0; j < numverts; ++j)
134 : {
135 0 : const vertinfo &v = verts[j];
136 0 : pos[j] = ivec(v.x, v.y, v.z).add(vo);
137 : }
138 : }
139 0 : else if(c.merged&(1<<i))
140 : {
141 0 : continue;
142 : }
143 : else
144 : {
145 0 : std::array <ivec, 4> v;
146 0 : genfaceverts(c, i, v);
147 0 : int order = vis&4 || (!flataxisface(c, i) && faceconvexity(v) < 0) ? 1 : 0;
148 0 : ivec vo = ivec(co).shl(3);
149 0 : pos[numverts++] = v[order].mul(size).add(vo);
150 0 : if(vis&1)
151 : {
152 0 : pos[numverts++] = v[order+1].mul(size).add(vo);
153 : }
154 0 : pos[numverts++] = v[order+2].mul(size).add(vo);
155 0 : if(vis&2)
156 : {
157 0 : pos[numverts++] = v[(order+3)&3].mul(size).add(vo);
158 : }
159 : }
160 0 : for(int j = 0; j < numverts; ++j)
161 : {
162 0 : int e1 = j,
163 0 : e2 = j+1 < numverts ? j+1 : 0;
164 0 : ivec d = pos[e2];
165 0 : d.sub(pos[e1]);
166 0 : if(!d)
167 : {
168 0 : continue;
169 : }
170 : //axistemp1/2 used in `axis` only
171 : // x = 0, y = 1, z = 2, `int axis` is the largest component of `d` using
172 : // axistemp1/2 to determine if x,y > z and then if x > y
173 0 : int axistemp1 = std::abs(d.x) > std::abs(d.z) ? 0 : 2,
174 0 : axistemp2 = std::abs(d.y) > std::abs(d.z) ? 1 : 2,
175 0 : axis = std::abs(d.x) > std::abs(d.y) ? axistemp1 : axistemp2;
176 0 : if(d[axis] < 0)
177 : {
178 0 : d.neg();
179 0 : std::swap(e1, e2);
180 : }
181 0 : reduceslope(d);
182 0 : int t1 = pos[e1][axis]/d[axis],
183 0 : t2 = pos[e2][axis]/d[axis];
184 0 : edgegroup g;
185 0 : g.origin = ivec(pos[e1]).sub(ivec(d).mul(t1));
186 0 : g.slope = d;
187 0 : g.axis = axis;
188 : cubeedge ce;
189 0 : ce.c = &c;
190 0 : ce.offset = t1;
191 0 : ce.size = t2 - t1;
192 0 : ce.index = i*(Face_MaxVerts+1)+j;
193 0 : ce.flags = CubeEdge_Start | CubeEdge_End | (e1!=j ? CubeEdge_Flip : 0);
194 0 : ce.next = -1;
195 0 : bool insert = true;
196 0 : auto exists = edgegroups.find(g);
197 0 : if(exists != edgegroups.end())
198 : {
199 0 : int prev = -1,
200 0 : cur = (*exists).second;
201 0 : while(cur >= 0)
202 : {
203 0 : cubeedge &p = cubeedges[cur];
204 0 : if(p.flags&CubeEdge_Dup ?
205 0 : ce.offset>=p.offset && ce.offset+ce.size<=p.offset+p.size :
206 0 : ce.offset==p.offset && ce.size==p.size)
207 : {
208 0 : p.flags |= CubeEdge_Dup;
209 0 : insert = false;
210 0 : break;
211 : }
212 0 : else if(ce.offset >= p.offset)
213 : {
214 0 : if(ce.offset == p.offset+p.size)
215 : {
216 0 : ce.flags &= ~CubeEdge_Start;
217 : }
218 0 : prev = cur;
219 0 : cur = p.next;
220 : }
221 : else
222 : {
223 0 : break;
224 : }
225 : }
226 0 : if(insert)
227 : {
228 0 : ce.next = cur;
229 0 : while(cur >= 0)
230 : {
231 0 : const cubeedge &p = cubeedges[cur];
232 0 : if(ce.offset+ce.size==p.offset)
233 : {
234 0 : ce.flags &= ~CubeEdge_End;
235 0 : break;
236 : }
237 0 : cur = p.next;
238 : }
239 0 : if(prev>=0)
240 : {
241 0 : cubeedges[prev].next = cubeedges.size();
242 : }
243 : else
244 : {
245 0 : (*exists).second = cubeedges.size();
246 : }
247 : }
248 : }
249 : else
250 : {
251 0 : edgegroups[g] = cubeedges.size();
252 : }
253 0 : if(insert)
254 : {
255 0 : cubeedges.push_back(ce);
256 : }
257 : }
258 : }
259 : }
260 0 : }
261 :
262 0 : void gencubeedges(std::array<cube, 8> &c, const ivec &co = ivec(0, 0, 0), int size = rootworld.mapsize()>>1)
263 : {
264 0 : neighborstack[++neighbordepth] = &c[0];
265 0 : for(int i = 0; i < 8; ++i)
266 : {
267 0 : ivec o(i, co, size);
268 0 : if(c[i].ext)
269 : {
270 0 : c[i].ext->tjoints = -1;
271 : }
272 0 : if(c[i].children)
273 : {
274 0 : gencubeedges(*(c[i].children), o, size>>1);
275 : }
276 0 : else if(!(c[i].isempty()))
277 : {
278 0 : gencubeedges(c[i], o, size);
279 : }
280 : }
281 0 : --neighbordepth;
282 0 : }
283 :
284 0 : void addtjoint(const edgegroup &g, const cubeedge &e, int offset)
285 : {
286 0 : const int vcoord = (g.slope[g.axis]*offset + g.origin[g.axis]) & 0x7FFF;
287 0 : tjoint tj = tjoint();
288 0 : tj.offset = vcoord / g.slope[g.axis];
289 0 : tj.edge = e.index;
290 0 : int prev = -1,
291 0 : cur = ext(*e.c).tjoints;
292 0 : while(cur >= 0)
293 : {
294 0 : const tjoint &o = tjoints[cur];
295 0 : if(tj.edge < o.edge || (tj.edge==o.edge && (e.flags&CubeEdge_Flip ? tj.offset > o.offset : tj.offset < o.offset)))
296 : {
297 : break;
298 : }
299 0 : prev = cur;
300 0 : cur = o.next;
301 : }
302 0 : tj.next = cur;
303 0 : tjoints.push_back(tj);
304 0 : if(prev < 0)
305 : {
306 0 : e.c->ext->tjoints = tjoints.size()-1;
307 : }
308 : else
309 : {
310 0 : tjoints[prev].next = tjoints.size()-1;
311 : }
312 0 : }
313 :
314 :
315 : /**
316 : * @brief Calls lookupvslot for textures associated with the VA list
317 : *
318 : * Searches the VA list for all associated texture indices and calls lookupvslot
319 : * for each one found. Only calls once even if multiple VAs share the same
320 : * texture.
321 : */
322 0 : void precachetextures()
323 : {
324 0 : std::vector<int> texs;
325 0 : for(size_t i = 0; i < valist.size(); i++)
326 : {
327 0 : const vtxarray *va = valist[i];
328 0 : for(int j = 0; j < va->texs; ++j)
329 : {
330 0 : int tex = va->texelems[j].texture;
331 0 : if(std::find(texs.begin(), texs.end(), tex) == texs.end())
332 : {
333 0 : texs.push_back(tex);
334 : }
335 : }
336 : }
337 0 : for(size_t i = 0; i < texs.size(); i++)
338 : {
339 0 : lookupvslot(texs[i]);
340 : }
341 0 : }
342 : }
343 :
344 : /* externally relevant functionality */
345 : ///////////////////////////////////////
346 :
347 0 : void findtjoints(int cur, const edgegroup &g)
348 : {
349 0 : int active = -1;
350 0 : while(cur >= 0)
351 : {
352 0 : cubeedge &e = cubeedges[cur];
353 0 : int prevactive = -1,
354 0 : curactive = active;
355 0 : while(curactive >= 0)
356 : {
357 0 : const cubeedge &a = cubeedges[curactive];
358 0 : if(a.offset+a.size <= e.offset)
359 : {
360 0 : if(prevactive >= 0)
361 : {
362 0 : cubeedges[prevactive].next = a.next;
363 : }
364 : else
365 : {
366 0 : active = a.next;
367 : }
368 : }
369 : else
370 : {
371 0 : prevactive = curactive;
372 0 : if(!(a.flags&CubeEdge_Dup))
373 : {
374 0 : if(e.flags&CubeEdge_Start && e.offset > a.offset && e.offset < a.offset+a.size)
375 : {
376 0 : addtjoint(g, a, e.offset);
377 : }
378 0 : if(e.flags&CubeEdge_End && e.offset+e.size > a.offset && e.offset+e.size < a.offset+a.size)
379 : {
380 0 : addtjoint(g, a, e.offset+e.size);
381 : }
382 : }
383 0 : if(!(e.flags&CubeEdge_Dup))
384 : {
385 0 : if(a.flags&CubeEdge_Start && a.offset > e.offset && a.offset < e.offset+e.size)
386 : {
387 0 : addtjoint(g, e, a.offset);
388 : }
389 0 : if(a.flags&CubeEdge_End && a.offset+a.size > e.offset && a.offset+a.size < e.offset+e.size)
390 : {
391 0 : addtjoint(g, e, a.offset+a.size);
392 : }
393 : }
394 : }
395 0 : curactive = a.next;
396 : }
397 0 : int next = e.next;
398 0 : e.next = active;
399 0 : active = cur;
400 0 : cur = next;
401 : }
402 0 : }
403 :
404 0 : void reduceslope(ivec &n)
405 : {
406 0 : int mindim = -1,
407 0 : minval = 64;
408 0 : for(int i = 0; i < 3; ++i)
409 : {
410 0 : if(n[i])
411 : {
412 0 : int val = std::abs(n[i]);
413 0 : if(mindim < 0 || val < minval)
414 : {
415 0 : mindim = i;
416 0 : minval = val;
417 : }
418 : }
419 : }
420 0 : if(!(n[R[mindim]]%minval) && !(n[C[mindim]]%minval))
421 : {
422 0 : n.div(minval);
423 : }
424 0 : while(!((n.x|n.y|n.z)&1))
425 : {
426 0 : n.shr(1); //shift right 1 to reduce slope
427 : }
428 0 : }
429 :
430 0 : void guessnormals(const vec *pos, int numverts, vec *normals)
431 : {
432 0 : vec n1, n2;
433 0 : n1.cross(pos[0], pos[1], pos[2]);
434 0 : if(numverts != 4)
435 : {
436 0 : n1.normalize();
437 0 : for(int k = 0; k < numverts; ++k)
438 : {
439 0 : normals[k] = n1;
440 : }
441 0 : return;
442 : }
443 0 : n2.cross(pos[0], pos[2], pos[3]);
444 0 : if(n1.iszero())
445 : {
446 0 : n2.normalize();
447 0 : for(int k = 0; k < 4; ++k)
448 : {
449 0 : normals[k] = n2;
450 : }
451 0 : return;
452 : }
453 : else
454 : {
455 0 : n1.normalize();
456 : }
457 0 : if(n2.iszero())
458 : {
459 0 : for(int k = 0; k < 4; ++k)
460 : {
461 0 : normals[k] = n1;
462 : }
463 0 : return;
464 : }
465 : else
466 : {
467 0 : n2.normalize();
468 : }
469 0 : vec avg = vec(n1).add(n2).normalize();
470 0 : normals[0] = avg;
471 0 : normals[1] = n1;
472 0 : normals[2] = avg;
473 0 : normals[3] = n2;
474 : }
475 :
476 : //va external fxns
477 :
478 0 : void destroyva(vtxarray *va, bool reparent)
479 : {
480 0 : wverts -= va->verts;
481 0 : wtris -= va->tris + va->alphabacktris + va->alphafronttris + va->refracttris + va->decaltris;
482 0 : allocva--;
483 0 : std::vector<vtxarray *>::iterator itr = std::find(valist.begin(), valist.end(), va);
484 0 : if(itr != valist.end())
485 : {
486 0 : valist.erase(itr);
487 : }
488 0 : if(!va->parent)
489 : {
490 0 : std::vector<vtxarray *>::iterator itr2 = std::find(valist.begin(), valist.end(), va);
491 0 : if(itr2 != valist.end())
492 : {
493 0 : valist.erase(itr2);
494 : }
495 : }
496 0 : if(reparent)
497 : {
498 0 : if(va->parent)
499 : {
500 0 : std::vector<vtxarray *>::iterator itr = std::find(va->parent->children.begin(), va->parent->children.end(), va);
501 0 : if(itr != va->parent->children.end())
502 : {
503 0 : va->parent->children.erase(itr);
504 : }
505 : }
506 0 : for(size_t i = 0; i < va->children.size(); i++)
507 : {
508 0 : vtxarray *child = va->children[i];
509 0 : child->parent = va->parent;
510 0 : if(child->parent)
511 : {
512 0 : child->parent->children.push_back(child);
513 : }
514 : }
515 : }
516 0 : if(va->vbuf)
517 : {
518 0 : destroyvbo(va->vbuf);
519 : }
520 0 : if(va->ebuf)
521 : {
522 0 : destroyvbo(va->ebuf);
523 : }
524 0 : if(va->skybuf)
525 : {
526 0 : destroyvbo(va->skybuf);
527 : }
528 0 : if(va->decalbuf)
529 : {
530 0 : destroyvbo(va->decalbuf);
531 : }
532 0 : if(va->texelems)
533 : {
534 0 : delete[] va->texelems;
535 : }
536 0 : if(va->decalelems)
537 : {
538 0 : delete[] va->decalelems;
539 : }
540 0 : delete va;
541 0 : }
542 :
543 0 : void clearvas(std::array<cube, 8> &c)
544 : {
545 0 : for(int i = 0; i < 8; ++i)
546 : {
547 0 : if(c[i].ext)
548 : {
549 0 : if(c[i].ext->va)
550 : {
551 0 : destroyva(c[i].ext->va, false);
552 : }
553 0 : c[i].ext->va = nullptr;
554 0 : c[i].ext->tjoints = -1;
555 : }
556 0 : if(c[i].children)
557 : {
558 0 : clearvas(*c[i].children);
559 : }
560 : }
561 0 : }
562 :
563 0 : void vtxarray::updatevabb(bool force)
564 : {
565 0 : if(!force && bbmin.x >= 0)
566 : {
567 0 : return;
568 : }
569 0 : bbmin = geommin;
570 0 : bbmax = geommax;
571 0 : bbmin.min(watermin);
572 0 : bbmax.max(watermax);
573 0 : bbmin.min(glassmin);
574 0 : bbmax.max(glassmax);
575 0 : for(vtxarray *child : children)
576 : {
577 0 : child->updatevabb(force);
578 0 : bbmin.min(child->bbmin);
579 0 : bbmax.max(child->bbmax);
580 : }
581 0 : for(const octaentities *oe : mapmodels)
582 : {
583 0 : bbmin.min(oe->bbmin);
584 0 : bbmax.max(oe->bbmax);
585 : }
586 0 : for(const octaentities *oe : decals)
587 : {
588 0 : bbmin.min(oe->bbmin);
589 0 : bbmax.max(oe->bbmax);
590 : }
591 0 : bbmin.max(o);
592 0 : bbmax.min(ivec(o).add(size));
593 0 : worldmin.min(bbmin);
594 0 : worldmax.max(bbmax);
595 : }
596 :
597 : //update vertex array bounding boxes recursively from the root va object down to all children
598 0 : void updatevabbs(bool force)
599 : {
600 0 : if(force)
601 : {
602 0 : worldmin = ivec(rootworld.mapsize(), rootworld.mapsize(), rootworld.mapsize());
603 0 : worldmax = ivec(0, 0, 0);
604 0 : for(size_t i = 0; i < varoot.size(); i++)
605 : {
606 0 : varoot[i]->updatevabb(true);
607 : }
608 0 : if(worldmin.x >= worldmax.x)
609 : {
610 0 : worldmin = ivec(0, 0, 0);
611 0 : worldmax = ivec(rootworld.mapsize(), rootworld.mapsize(), rootworld.mapsize());
612 : }
613 : }
614 : else
615 : {
616 0 : for(size_t i = 0; i < varoot.size(); i++)
617 : {
618 0 : varoot[i]->updatevabb();
619 : }
620 : }
621 0 : }
622 :
623 0 : void cubeworld::findtjoints()
624 : {
625 0 : gencubeedges(*worldroot);
626 0 : tjoints.clear();
627 0 : for(auto &[k, t] : edgegroups)
628 : {
629 0 : ::findtjoints(t, k);
630 : }
631 0 : cubeedges.clear();
632 0 : edgegroups.clear();
633 0 : }
634 :
635 1 : void cubeworld::allchanged(bool load)
636 : {
637 1 : if(!worldroot)
638 : {
639 1 : return;
640 : }
641 0 : if(mainmenu)
642 : {
643 0 : load = false;
644 : }
645 0 : if(load)
646 : {
647 0 : initlights();
648 : }
649 0 : clearvas(*worldroot);
650 0 : occlusionengine.resetqueries();
651 0 : resetclipplanes();
652 0 : entitiesinoctanodes();
653 0 : tjoints.clear();
654 0 : if(filltjoints)
655 : {
656 0 : findtjoints();
657 : }
658 0 : octarender();
659 0 : if(load)
660 : {
661 0 : precachetextures();
662 : }
663 0 : setupmaterials();
664 0 : clearshadowcache();
665 0 : updatevabbs(true);
666 0 : if(load)
667 : {
668 0 : genshadowmeshes();
669 0 : seedparticles();
670 : }
671 : }
672 :
673 0 : void initoctarendercmds()
674 : {
675 0 : addcommand("recalc", reinterpret_cast<identfun>(+[](){rootworld.allchanged(true);}), "", Id_Command);
676 0 : }
|