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