Initial Comit: First commit.
[SauerEngine.git] / src / engine / pvs.cpp
blob1ed80f6f411c06c36233a0eec6ecca4c25975067
1 #include "pch.h"
2 #include "engine.h"
3 #include "SDL_thread.h"
5 enum
7 PVS_HIDE_GEOM = 1<<0,
8 PVS_HIDE_BB = 1<<1
9 };
11 struct pvsnode
13 bvec edges;
14 uchar flags;
15 uint children;
18 static vector<pvsnode> origpvsnodes;
20 static bool mergepvsnodes(pvsnode &p, pvsnode *children)
22 loopi(7) if(children[i].flags!=children[7].flags) return false;
23 bvec bbs[4];
24 loop(x, 2) loop(y, 2)
26 const bvec &lo = children[octaindex(2, x, y, 0)].edges,
27 &hi = children[octaindex(2, x, y, 1)].edges;
28 if(lo.x!=0xFF && (lo.x&0x11 || lo.y&0x11 || lo.z&0x11)) return false;
29 if(hi.x!=0xFF && (hi.x&0x11 || hi.y&0x11 || hi.z&0x11)) return false;
31 #define MERGEBBS(res, coord, row, col) \
32 if(lo.coord==0xFF) \
33 { \
34 if(hi.coord!=0xFF) \
35 { \
36 res.coord = ((hi.coord&~0x11)>>1) + 0x44; \
37 res.row = hi.row; \
38 res.col = hi.col; \
39 } \
40 } \
41 else if(hi.coord==0xFF) \
42 { \
43 res.coord = (lo.coord&0xEE)>>1; \
44 res.row = lo.row; \
45 res.col = lo.col; \
46 } \
47 else if(lo.row!=hi.row || lo.col!=hi.col || (lo.coord&0xF0)!=0x80 || (hi.coord&0xF)!=0) return false; \
48 else \
49 { \
50 res.coord = ((lo.coord&~0xF1)>>1) | (((hi.coord&~0x1F)>>1) + 0x40); \
51 res.row = lo.row; \
52 res.col = lo.col; \
55 bvec &res = bbs[x + 2*y];
56 MERGEBBS(res, z, x, y);
57 res.x = lo.x;
58 res.y = lo.y;
60 loop(x, 2)
62 bvec &lo = bbs[x], &hi = bbs[x+2];
63 MERGEBBS(lo, y, x, z);
65 bvec &lo = bbs[0], &hi = bbs[1];
66 MERGEBBS(p.edges, x, y, z);
68 return true;
71 static void genpvsnodes(cube *c, int parent = 0, const ivec &co = ivec(0, 0, 0), int size = hdr.worldsize/2)
73 int index = origpvsnodes.length();
74 loopi(8)
76 ivec o(i, co.x, co.y, co.z, size);
77 pvsnode &n = origpvsnodes.add();
78 n.flags = 0;
79 n.children = 0;
80 if(c[i].children || isempty(c[i])) memset(n.edges.v, 0xFF, 3);
81 else loopk(3)
83 uint face = c[i].faces[k];
84 if(face==F_SOLID) n.edges[k] = 0x80;
85 else
87 uchar low = max(max(face&0xF, (face>>8)&0xF), max((face>>16)&0xF, (face>>24)&0xF)),
88 high = min(min((face>>4)&0xF, (face>>12)&0xF), min((face>>20)&0xF, (face>>28)&0xF));
89 if(size<8)
91 if(low&((8/size)-1)) { low += 8/size - (low&((8/size)-1)); }
92 if(high&((8/size)-1)) high &= ~(8/size-1);
94 if(low >= high) { memset(n.edges.v, 0xFF, 3); break; }
95 n.edges[k] = low | (high<<4);
99 int branches = 0;
100 loopi(8) if(c[i].children)
102 ivec o(i, co.x, co.y, co.z, size);
103 genpvsnodes(c[i].children, index+i, o, size>>1);
104 if(origpvsnodes[index+i].children) branches++;
106 if(!branches && mergepvsnodes(origpvsnodes[parent], &origpvsnodes[index])) origpvsnodes.setsizenodelete(index);
107 else origpvsnodes[parent].children = index;
110 struct shaftplane
112 float r, c, offset;
113 uchar rnear, cnear, rfar, cfar;
116 struct usvec
118 union
120 struct { ushort x, y, z; };
121 ushort v[3];
124 ushort &operator[](int i) { return v[i]; }
125 ushort operator[](int i) const { return v[i]; }
128 struct shaftbb
130 union
132 ushort v[6];
133 struct { usvec min, max; };
136 shaftbb() {}
137 shaftbb(const ivec &o, int size)
139 min.x = o.x;
140 min.y = o.y;
141 min.z = o.z;
142 max.x = o.x + size;
143 max.y = o.y + size;
144 max.z = o.z + size;
146 shaftbb(const ivec &o, int size, const bvec &edges)
148 min.x = o.x + (size*(edges.x&0xF))/8;
149 min.y = o.y + (size*(edges.y&0xF))/8;
150 min.z = o.z + (size*(edges.z&0xF))/8;
151 max.x = o.x + (size*(edges.x>>4))/8;
152 max.y = o.y + (size*(edges.y>>4))/8;
153 max.z = o.z + (size*(edges.z>>4))/8;
156 ushort &operator[](int i) { return v[i]; }
157 ushort operator[](int i) const { return v[i]; }
159 bool contains(const shaftbb &o) const
161 return min.x<=o.min.x && min.y<=o.min.y && min.z<=o.min.z &&
162 max.x>=o.max.x && max.y>=o.max.y && max.z>=o.max.z;
165 bool outside(const ivec &o, int size) const
167 return o.x>=max.x || o.y>=max.y || o.z>=max.z ||
168 o.x+size<=min.x || o.y+size<=min.y || o.z+size<=min.z;
171 bool outside(const shaftbb &o) const
173 return o.min.x>max.x || o.min.y>max.y || o.min.z>max.z ||
174 o.max.x<min.x || o.max.y<min.y || o.max.z<min.z;
177 bool notinside(const shaftbb &o) const
179 return o.min.x<min.x || o.min.y<min.y || o.min.z<min.z ||
180 o.max.x>max.x || o.max.y>max.y || o.max.z>max.z;
184 struct shaft
186 shaftbb bounds;
187 shaftplane planes[8];
188 int numplanes;
190 shaft(const shaftbb &from, const shaftbb &to)
192 calcshaft(from, to);
195 void calcshaft(const shaftbb &from, const shaftbb &to)
197 uchar match = 0, color = 0;
198 loopi(3)
200 if(to.min[i] < from.min[i]) { color |= 1<<i; bounds.min[i] = 0; }
201 else if(to.min[i] > from.min[i]) bounds.min[i] = to.min[i]+1;
202 else { match |= 1<<i; bounds.min[i] = to.min[i]; }
204 if(to.max[i] > from.max[i]) { color |= 8<<i; bounds.max[i] = USHRT_MAX; }
205 else if(to.max[i] < from.max[i]) bounds.max[i] = to.max[i]-1;
206 else { match |= 8<<i; bounds.max[i] = to.max[i]; }
208 numplanes = 0;
209 loopi(5) if(!(match&(1<<i))) for(int j = i+1; j<6; j++) if(!(match&(1<<j)) && i+3!=j && ((color>>i)^(color>>j))&1)
211 int r = i%3, c = j%3, d = (r+1)%3;
212 if(d==c) d = (c+1)%3;
213 shaftplane &p = planes[numplanes++];
214 p.r = from[j] - to[j];
215 if(i<3 ? p.r >= 0 : p.r < 0)
217 p.r = -p.r;
218 p.c = from[i] - to[i];
220 else p.c = to[i] - from[i];
221 p.offset = -(from[i]*p.r + from[j]*p.c);
222 p.rnear = p.r >= 0 ? r : 3+r;
223 p.cnear = p.c >= 0 ? c : 3+c;
224 p.rfar = p.r < 0 ? r : 3+r;
225 p.cfar = p.c < 0 ? c : 3+c;
229 bool outside(const shaftbb &o) const
231 if(bounds.outside(o)) return true;
233 for(const shaftplane *p = planes; p < &planes[numplanes]; p++)
235 if(o[p->rnear]*p->r + o[p->cnear]*p->c + p->offset > 0) return true;
237 return false;
240 bool inside(const shaftbb &o) const
242 if(bounds.notinside(o)) return false;
244 for(const shaftplane *p = planes; p < &planes[numplanes]; p++)
246 if(o[p->rfar]*p->r + o[p->cfar]*p->c + p->offset > 0) return false;
248 return true;
252 struct pvsdata
254 int offset, len;
256 pvsdata() {}
257 pvsdata(int offset, int len) : offset(offset), len(len) {}
260 static vector<uchar> pvsbuf;
262 static inline uint hthash(const pvsdata &k)
264 uint h = 5381;
265 loopi(k.len) h = ((h<<5)+h)^pvsbuf[k.offset+i];
266 return h;
269 static inline bool htcmp(const pvsdata &x, const pvsdata &y)
271 return x.len==y.len && !memcmp(&pvsbuf[x.offset], &pvsbuf[y.offset], x.len);
274 static SDL_mutex *pvsmutex = NULL;
275 static hashtable<pvsdata, int> pvscompress;
276 static vector<pvsdata> pvs;
278 static SDL_mutex *viewcellmutex = NULL;
279 struct viewcellrequest
281 int *result;
282 ivec o;
283 int size;
285 static vector<viewcellrequest> viewcellrequests;
287 static bool genpvs_canceled = false;
288 static int numviewcells = 0;
290 VAR(maxpvsblocker, 1, 512, 1<<16);
291 VAR(pvsleafsize, 1, 64, 1024);
293 #define MAXWATERPVS 32
295 static struct
297 int height;
298 vector<materialsurface *> matsurfs;
299 } waterplanes[MAXWATERPVS];
300 static vector<materialsurface *> waterfalls;
301 uint numwaterplanes = 0;
303 struct pvsworker
305 pvsworker() : thread(NULL), pvsnodes(new pvsnode[origpvsnodes.length()])
308 ~pvsworker()
310 delete[] pvsnodes;
313 SDL_Thread *thread;
314 pvsnode *pvsnodes;
316 shaftbb viewcellbb;
318 pvsnode *levels[32];
319 int curlevel;
320 ivec origin;
322 void resetlevels()
324 curlevel = worldscale;
325 levels[curlevel] = &pvsnodes[0];
326 origin = ivec(0, 0, 0);
329 int hasvoxel(const ivec &p, int coord, int dir, int ocoord = 0, int odir = 0, int *omin = NULL)
331 uint diff = (origin.x^p.x)|(origin.y^p.y)|(origin.z^p.z);
332 if(diff >= uint(hdr.worldsize)) return 0;
333 diff >>= curlevel;
334 while(diff)
336 curlevel++;
337 diff >>= 1;
340 pvsnode *cur = levels[curlevel];
341 while(cur->children && !(cur->flags&PVS_HIDE_BB))
343 cur = &pvsnodes[cur->children];
344 curlevel--;
345 cur += ((p.z>>(curlevel-2))&4) | ((p.y>>(curlevel-1))&2) | ((p.x>>curlevel)&1);
346 levels[curlevel] = cur;
349 origin = ivec(p.x&(~0<<curlevel), p.y&(~0<<curlevel), p.z&(~0<<curlevel));
351 if(cur->flags&PVS_HIDE_BB || cur->edges==bvec(0x80, 0x80, 0x80))
353 if(omin)
355 int step = origin[ocoord] + (odir<<curlevel) - p[ocoord] + odir - 1;
356 if(odir ? step < *omin : step > *omin) *omin = step;
358 return origin[coord] + (dir<<curlevel) - p[coord] + dir - 1;
361 if(cur->edges.x==0xFF) return 0;
362 ivec bbp(p);
363 bbp.sub(origin);
364 ivec bbmin, bbmax;
365 bbmin.x = ((cur->edges.x&0xF)<<curlevel)/8;
366 if(bbp.x < bbmin.x) return 0;
367 bbmax.x = ((cur->edges.x>>4)<<curlevel)/8;
368 if(bbp.x >= bbmax.x) return 0;
369 bbmin.y = ((cur->edges.y&0xF)<<curlevel)/8;
370 if(bbp.y < bbmin.y) return 0;
371 bbmax.y = ((cur->edges.y>>4)<<curlevel)/8;
372 if(bbp.y >= bbmax.y) return 0;
373 bbmin.z = ((cur->edges.z&0xF)<<curlevel)/8;
374 if(bbp.z < bbmin.z) return 0;
375 bbmax.z = ((cur->edges.z>>4)<<curlevel)/8;
376 if(bbp.z >= bbmax.z) return 0;
378 if(omin)
380 int step = (odir ? bbmax[ocoord] : bbmin[ocoord]) - bbp[ocoord] + (odir - 1);
381 if(odir ? step < *omin : step > *omin) *omin = step;
383 return (dir ? bbmax[coord] : bbmin[coord]) - bbp[coord] + (dir - 1);
386 void hidepvs(pvsnode &p)
388 if(p.children)
390 pvsnode *children = &pvsnodes[p.children];
391 loopi(8) hidepvs(children[i]);
392 p.flags |= PVS_HIDE_BB;
393 return;
395 p.flags |= PVS_HIDE_BB;
396 if(p.edges.x!=0xFF) p.flags |= PVS_HIDE_GEOM;
399 void shaftcullpvs(shaft &s, pvsnode &p, const ivec &co = ivec(0, 0, 0), int size = hdr.worldsize)
401 if(p.flags&PVS_HIDE_BB) return;
402 shaftbb bb(co, size);
403 if(s.outside(bb)) return;
404 if(s.inside(bb)) { hidepvs(p); return; }
405 if(p.children)
407 pvsnode *children = &pvsnodes[p.children];
408 uchar flags = 0xFF;
409 loopi(8)
411 ivec o(i, co.x, co.y, co.z, size>>1);
412 shaftcullpvs(s, children[i], o, size>>1);
413 flags &= children[i].flags;
415 if(flags & PVS_HIDE_BB) p.flags |= PVS_HIDE_BB;
416 return;
418 if(p.edges.x==0xFF) return;
419 shaftbb geom(co, size, p.edges);
420 if(s.inside(geom)) p.flags |= PVS_HIDE_GEOM;
423 ringbuf<shaftbb, 32> prevblockers;
425 void cullpvs(pvsnode &p, const ivec &co = ivec(0, 0, 0), int size = hdr.worldsize)
427 if(p.flags&(PVS_HIDE_BB | PVS_HIDE_GEOM) || genpvs_canceled) return;
428 if(p.children && !(p.flags&PVS_HIDE_BB))
430 pvsnode *children = &pvsnodes[p.children];
431 loopi(8)
433 ivec o(i, co.x, co.y, co.z, size>>1);
434 cullpvs(children[i], o, size>>1);
436 if(!(p.flags & PVS_HIDE_BB)) return;
438 bvec edges = p.children ? bvec(0x80, 0x80, 0x80) : p.edges;
439 if(edges.x==0xFF) return;
440 shaftbb geom(co, size, edges);
441 loopi(6)
443 int dim = dimension(i), dc = dimcoord(i), r = R[dim], c = C[dim];
444 int ccenter = geom.min[c];
445 if(geom.min[r]==geom.max[r] || geom.min[c]==geom.max[c]) continue;
446 while(ccenter < geom.max[c])
448 ivec rmin;
449 rmin[dim] = geom[dim + 3*dc] + (dc ? -1 : 0);
450 rmin[r] = geom.min[r];
451 rmin[c] = ccenter;
452 ivec rmax = rmin;
453 rmax[r] = geom.max[r] - 1;
454 int rcenter = (rmin[r] + rmax[r])/2;
455 resetlevels();
456 for(int minstep = -1, maxstep = 1; (minstep || maxstep) && rmax[r] - rmin[r] < maxpvsblocker;)
458 if(minstep) minstep = hasvoxel(rmin, r, 0);
459 if(maxstep) maxstep = hasvoxel(rmax, r, 1);
460 rmin[r] += minstep;
461 rmax[r] += maxstep;
463 rmin[r] = rcenter + (rmin[r] - rcenter)/2;
464 rmax[r] = rcenter + (rmax[r] - rcenter)/2;
465 if(rmin[r]>=geom.min[r] && rmax[r]<geom.max[r]) { rmin[r] = geom.min[r]; rmax[r] = geom.max[r] - 1; }
466 ivec cmin = rmin, cmax = rmin;
467 if(rmin[r]>=geom.min[r] && rmax[r]<geom.max[r])
469 cmin[c] = geom.min[c];
470 cmax[c] = geom.max[c]-1;
472 int cminstep = -1, cmaxstep = 1;
473 for(; (cminstep || cmaxstep) && cmax[c] - cmin[c] < maxpvsblocker;)
475 if(cminstep)
477 cmin[c] += cminstep; cminstep = INT_MIN;
478 cmin[r] = rmin[r];
479 resetlevels();
480 for(int rstep = 1; rstep && cmin[r] <= rmax[r];)
482 rstep = hasvoxel(cmin, r, 1, c, 0, &cminstep);
483 cmin[r] += rstep;
485 if(cmin[r] <= rmax[r]) cminstep = 0;
487 if(cmaxstep)
489 cmax[c] += cmaxstep; cmaxstep = INT_MAX;
490 cmax[r] = rmin[r];
491 resetlevels();
492 for(int rstep = 1; rstep && cmax[r] <= rmax[r];)
494 rstep = hasvoxel(cmax, r, 1, c, 1, &cmaxstep);
495 cmax[r] += rstep;
497 if(cmax[r] <= rmax[r]) cmaxstep = 0;
500 if(!cminstep) cmin[c]++;
501 if(!cmaxstep) cmax[c]--;
502 ivec emin = rmin, emax = rmax;
503 if(cmin[c]>=geom.min[c] && cmax[c]<geom.max[c])
505 if(emin[r]>geom.min[r]) emin[r] = geom.min[r];
506 if(emax[r]<geom.max[r]-1) emax[r] = geom.max[r]-1;
508 int rminstep = -1, rmaxstep = 1;
509 for(; (rminstep || rmaxstep) && emax[r] - emin[r] < maxpvsblocker;)
511 if(rminstep)
513 emin[r] += -1; rminstep = INT_MIN;
514 emin[c] = cmin[c];
515 resetlevels();
516 for(int cstep = 1; cstep && emin[c] <= cmax[c];)
518 cstep = hasvoxel(emin, c, 1, r, 0, &rminstep);
519 emin[c] += cstep;
521 if(emin[c] <= cmax[c]) rminstep = 0;
523 if(rmaxstep)
525 emax[r] += 1; rmaxstep = INT_MAX;
526 emax[c] = cmin[c];
527 resetlevels();
528 for(int cstep = 1; cstep && emax[c] <= cmax[c];)
530 cstep = hasvoxel(emax, c, 1, r, 1, &rmaxstep);
531 emax[c] += cstep;
533 if(emax[c] <= cmax[c]) rmaxstep = 0;
536 if(!rminstep) emin[r]++;
537 if(!rmaxstep) emax[r]--;
538 shaftbb bb;
539 bb.min[dim] = rmin[dim];
540 bb.max[dim] = rmin[dim]+1;
541 bb.min[r] = emin[r];
542 bb.max[r] = emax[r]+1;
543 bb.min[c] = cmin[c];
544 bb.max[c] = cmax[c]+1;
545 if(bb.min[dim] >= viewcellbb.max[dim] || bb.max[dim] <= viewcellbb.min[dim])
547 int ddir = bb.min[dim] >= viewcellbb.max[dim] ? 1 : -1,
548 dval = ddir>0 ? USHRT_MAX-1 : 0,
549 dlimit = maxpvsblocker,
550 numsides = 0;
551 loopj(4)
553 ivec dmax;
554 int odim = j < 2 ? c : r;
555 if(j&1)
557 if(bb.max[odim] >= viewcellbb.max[odim]) continue;
558 dmax[odim] = bb.max[odim]-1;
560 else
562 if(bb.min[odim] <= viewcellbb.min[odim]) continue;
563 dmax[odim] = bb.min[odim];
565 numsides++;
566 dmax[dim] = bb.min[dim];
567 int stepdim = j < 2 ? r : c, stepstart = bb.min[stepdim], stepend = bb.max[stepdim];
568 int dstep = ddir;
569 for(; dstep && ddir*(dmax[dim] - (int)bb.min[dim]) < dlimit;)
571 dmax[dim] += dstep; dstep = ddir > 0 ? INT_MAX : INT_MIN;
572 dmax[stepdim] = stepstart;
573 resetlevels();
574 for(int step = 1; step && dmax[stepdim] < stepend;)
576 step = hasvoxel(dmax, stepdim, 1, dim, (ddir+1)/2, &dstep);
577 dmax[stepdim] += step;
579 if(dmax[stepdim] < stepend) dstep = 0;
581 dlimit = min(dlimit, ddir*(dmax[dim] - (int)bb.min[dim]));
582 if(!dstep) dmax[dim] -= ddir;
583 if(ddir>0) dval = min(dval, dmax[dim]);
584 else dval = max(dval, dmax[dim]);
586 if(numsides>0)
588 if(ddir>0) bb.max[dim] = dval+1;
589 else bb.min[dim] = dval;
591 //printf("(%d,%d,%d) x %d,%d,%d, side %d, ccenter = %d, origin = (%d,%d,%d), size = %d\n", bb.min.x, bb.min.y, bb.min.z, bb.max.x-bb.min.x, bb.max.y-bb.min.y, bb.max.z-bb.min.z, i, ccenter, co.x, co.y, co.z, size);
593 bool dup = false;
594 loopvj(prevblockers)
596 if(prevblockers[j].contains(bb)) { dup = true; break; }
598 if(!dup)
600 shaft s(viewcellbb, bb);
601 shaftcullpvs(s, pvsnodes[0]);
602 prevblockers.add(bb);
604 if(bb.contains(geom)) return;
605 ccenter = cmax[c] + 1;
610 bool compresspvs(pvsnode &p, int size, int threshold)
612 if(!p.children) return true;
613 if(p.flags&PVS_HIDE_BB) { p.children = 0; return true; }
614 pvsnode *children = &pvsnodes[p.children];
615 bool canreduce = true;
616 loopi(8)
618 if(!compresspvs(children[i], size/2, threshold)) canreduce = false;
620 if(canreduce)
622 int hide = children[7].flags&PVS_HIDE_BB;
623 loopi(7) if((children[i].flags&PVS_HIDE_BB)!=hide) canreduce = false;
624 if(canreduce)
626 p.flags = (p.flags & ~PVS_HIDE_BB) | hide;
627 p.children = 0;
628 return true;
631 if(size <= threshold)
633 p.children = 0;
634 return true;
636 return false;
639 vector<uchar> outbuf;
641 bool serializepvs(pvsnode &p, int storage = -1)
643 if(!p.children)
645 outbuf.add(0xFF);
646 loopi(8) outbuf.add(p.flags&PVS_HIDE_BB ? 0xFF : 0);
647 return true;
649 int index = outbuf.length();
650 pvsnode *children = &pvsnodes[p.children];
651 int i = 0;
652 uchar leafvalues = 0;
653 if(storage>=0)
655 for(; i < 8; i++)
657 pvsnode &child = children[i];
658 if(child.flags&PVS_HIDE_BB) leafvalues |= 1<<i;
659 else if(child.children) break;
661 if(i==8) { outbuf[storage] = leafvalues; return false; }
662 // if offset won't fit, just mark the space as a visible to avoid problems
663 int offset = (index - storage + 8)/9;
664 if(offset>255) { outbuf[storage] = 0; return false; }
665 outbuf[storage] = uchar(offset);
667 outbuf.add(0);
668 loopj(8) outbuf.add(leafvalues&(1<<j) ? 0xFF : 0);
669 uchar leafmask = (1<<i)-1;
670 for(; i < 8; i++)
672 pvsnode &child = children[i];
673 if(child.children) { if(!serializepvs(child, index+1+i)) leafmask |= 1<<i; }
674 else { leafmask |= 1<<i; outbuf[index+1+i] = child.flags&PVS_HIDE_BB ? 0xFF : 0; }
676 outbuf[index] = leafmask;
677 return true;
680 bool materialoccluded(pvsnode &p, const ivec &co, int size, const ivec &bborigin, const ivec &bbsize)
682 pvsnode *children = &pvsnodes[p.children];
683 loopoctabox(co, size, bborigin, bbsize)
685 ivec o(i, co.x, co.y, co.z, size);
686 if(children[i].flags & PVS_HIDE_BB) continue;
687 if(!children[i].children || !materialoccluded(children[i], o, size/2, bborigin, bbsize)) return false;
689 return true;
692 bool materialoccluded(vector<materialsurface *> &matsurfs)
694 if(pvsnodes[0].flags & PVS_HIDE_BB) return true;
695 if(!pvsnodes[0].children) return false;
696 loopv(matsurfs)
698 materialsurface &m = *matsurfs[i];
699 ivec bborigin(m.o), bbsize(0, 0, 0);
700 int dim = dimension(m.orient);
701 bbsize[C[dim]] = m.csize;
702 bbsize[R[dim]] = m.rsize;
703 bborigin[dim] -= 2;
704 bbsize[dim] = 2;
705 if(!materialoccluded(pvsnodes[0], vec(0, 0, 0), hdr.worldsize/2, bborigin, bbsize)) return false;
707 return true;
710 int wateroccluded, waterbytes;
712 void calcpvs(const ivec &co, int size)
714 loopk(3)
716 viewcellbb.min[k] = co[k];
717 viewcellbb.max[k] = co[k]+size;
719 memcpy(pvsnodes, origpvsnodes.getbuf(), origpvsnodes.length()*sizeof(pvsnode));
720 prevblockers.clear();
721 cullpvs(pvsnodes[0]);
723 wateroccluded = 0;
724 loopi(numwaterplanes)
726 if(waterplanes[i].height < 0)
728 if(waterfalls.length() && materialoccluded(waterfalls)) wateroccluded |= 1<<i;
730 else if(waterplanes[i].matsurfs.length() && materialoccluded(waterplanes[i].matsurfs)) wateroccluded |= 1<<i;
732 waterbytes = 0;
733 loopi(4) if(wateroccluded&(0xFF<<(i*8))) waterbytes = i+1;
735 compresspvs(pvsnodes[0], hdr.worldsize, pvsleafsize);
736 outbuf.setsizenodelete(0);
737 serializepvs(pvsnodes[0]);
740 uchar *testviewcell(const ivec &co, int size, int *waterpvs = NULL, int *len = NULL)
742 calcpvs(co, size);
744 uchar *buf = new uchar[outbuf.length()];
745 memcpy(buf, outbuf.getbuf(), outbuf.length());
746 if(waterpvs) *waterpvs = wateroccluded;
747 if(len) *len = outbuf.length();
748 return buf;
751 int genviewcell(const ivec &co, int size)
753 calcpvs(co, size);
755 if(pvsmutex) SDL_LockMutex(pvsmutex);
756 numviewcells++;
757 pvsdata key(pvsbuf.length(), waterbytes + outbuf.length());
758 loopi(waterbytes) pvsbuf.add((wateroccluded>>(i*8))&0xFF);
759 pvsbuf.put(outbuf.getbuf(), outbuf.length());
760 int *val = pvscompress.access(key);
761 if(val) pvsbuf.setsizenodelete(key.offset);
762 else
764 val = &pvscompress[key];
765 *val = pvs.length();
766 pvs.add(key);
768 if(pvsmutex) SDL_UnlockMutex(pvsmutex);
769 return *val;
772 static int run(void *data)
774 pvsworker *w = (pvsworker *)data;
775 SDL_LockMutex(viewcellmutex);
776 while(viewcellrequests.length())
778 viewcellrequest req = viewcellrequests.pop();
779 SDL_UnlockMutex(viewcellmutex);
780 int result = w->genviewcell(req.o, req.size);
781 SDL_LockMutex(viewcellmutex);
782 *req.result = result;
784 SDL_UnlockMutex(viewcellmutex);
785 return 0;
789 struct viewcellnode
791 uchar leafmask;
792 union viewcellchild
794 int pvs;
795 viewcellnode *node;
796 } children[8];
798 viewcellnode() : leafmask(0xFF)
800 loopi(8) children[i].pvs = -1;
802 ~viewcellnode()
804 loopi(8) if(!(leafmask&(1<<i))) delete children[i].node;
808 VARP(pvsthreads, 1, 1, 16);
809 static vector<pvsworker *> pvsworkers;
811 static volatile bool check_genpvs_progress = false;
813 static Uint32 genpvs_timer(Uint32 interval, void *param)
815 check_genpvs_progress = true;
816 return interval;
819 static int totalviewcells = 0;
821 static void show_genpvs_progress(int unique = pvs.length(), int processed = numviewcells)
823 float bar1 = float(processed) / float(totalviewcells>0 ? totalviewcells : 1),
824 bar2 = float(unique) / float(totalviewcells>0 ? totalviewcells : 1);
826 s_sprintfd(text1)("%d%% - %d of %d view cells", int(bar1 * 100), processed, totalviewcells);
827 s_sprintfd(text2)("%d unique view cells", unique);
829 show_out_of_renderloop_progress(bar1, text1, bar2, text2);
831 if(interceptkey(SDLK_ESCAPE)) genpvs_canceled = true;
832 check_genpvs_progress = false;
835 static shaftbb pvsbounds;
837 static void calcpvsbounds()
839 loopk(3) pvsbounds.min[k] = USHRT_MAX;
840 loopk(3) pvsbounds.max[k] = 0;
841 extern vector<vtxarray *> valist;
842 loopv(valist)
844 vtxarray *va = valist[i];
845 loopk(3)
847 if(va->geommin[k]>va->geommax[k]) continue;
848 pvsbounds.min[k] = min(pvsbounds.min[k], (ushort)va->geommin[k]);
849 pvsbounds.max[k] = max(pvsbounds.max[k], (ushort)va->geommax[k]);
854 static inline bool isallclip(cube *c)
856 loopi(8)
858 cube &h = c[i];
859 if(h.children ? !isallclip(h.children) : (!isentirelysolid(h) && (!h.ext || (h.ext->material&MATF_CLIP)!=MAT_CLIP)))
860 return false;
862 return true;
865 static int countviewcells(cube *c, const ivec &co, int size, int threshold)
867 int count = 0;
868 loopi(8)
870 ivec o(i, co.x, co.y, co.z, size);
871 if(pvsbounds.outside(o, size)) continue;
872 cube &h = c[i];
873 if(h.children)
875 if(size>threshold)
877 count += countviewcells(h.children, o, size>>1, threshold);
878 continue;
880 if(isallclip(h.children)) continue;
882 else if(isentirelysolid(h) || (h.ext && (h.ext->material&MATF_CLIP)==MAT_CLIP)) continue;
883 count++;
885 return count;
888 static void genviewcells(viewcellnode &p, cube *c, const ivec &co, int size, int threshold)
890 if(genpvs_canceled) return;
891 loopi(8)
893 ivec o(i, co.x, co.y, co.z, size);
894 if(pvsbounds.outside(o, size)) continue;
895 cube &h = c[i];
896 if(h.children)
898 if(size>threshold)
900 p.leafmask &= ~(1<<i);
901 p.children[i].node = new viewcellnode;
902 genviewcells(*p.children[i].node, h.children, o, size>>1, threshold);
903 continue;
905 if(isallclip(h.children)) continue;
907 else if(isentirelysolid(h) || (h.ext && (h.ext->material&MATF_CLIP)==MAT_CLIP)) continue;
908 if(pvsthreads<=1)
910 if(genpvs_canceled) return;
911 p.children[i].pvs = pvsworkers[0]->genviewcell(o, size);
912 if(check_genpvs_progress) show_genpvs_progress();
914 else
916 viewcellrequest &req = viewcellrequests.add();
917 req.result = &p.children[i].pvs;
918 req.o = o;
919 req.size = size;
924 static viewcellnode *viewcells = NULL;
925 static int lockedwaterplanes[MAXWATERPVS];
926 static uchar *curpvs = NULL, *lockedpvs = NULL;
927 static int curwaterpvs = 0, lockedwaterpvs = 0;
929 static inline pvsdata *lookupviewcell(const vec &p)
931 uint x = uint(floor(p.x)), y = uint(floor(p.y)), z = uint(floor(p.z));
932 if(!viewcells || (x|y|z)>=uint(hdr.worldsize)) return NULL;
933 viewcellnode *vc = viewcells;
934 for(int scale = worldscale-1; scale>=0; scale--)
936 int i = octastep(x, y, z, scale);
937 if(vc->leafmask&(1<<i))
939 return vc->children[i].pvs>=0 ? &pvs[vc->children[i].pvs] : NULL;
941 vc = vc->children[i].node;
943 return NULL;
946 static void lockpvs_(bool lock)
948 if(lockedpvs) DELETEA(lockedpvs);
949 if(!lock) return;
950 pvsdata *d = lookupviewcell(camera1->o);
951 if(!d) return;
952 int wbytes = d->len%9, len = d->len - wbytes;
953 lockedpvs = new uchar[len];
954 memcpy(lockedpvs, &pvsbuf[d->offset + wbytes], len);
955 lockedwaterpvs = 0;
956 loopi(wbytes) lockedwaterpvs |= pvsbuf[d->offset + i] << (i*8);
957 loopi(MAXWATERPVS) lockedwaterplanes[i] = waterplanes[i].height;
958 conoutf("locked view cell at %.1f, %.1f, %.1f", camera1->o.x, camera1->o.y, camera1->o.z);
961 VARF(lockpvs, 0, 0, 1, lockpvs_(lockpvs!=0));
963 VARN(pvs, usepvs, 0, 1, 1);
964 VARN(waterpvs, usewaterpvs, 0, 1, 1);
966 void setviewcell(const vec &p)
968 if(!usepvs) curpvs = NULL;
969 else if(lockedpvs)
971 curpvs = lockedpvs;
972 curwaterpvs = lockedwaterpvs;
974 else
976 pvsdata *d = lookupviewcell(p);
977 curpvs = d ? &pvsbuf[d->offset] : NULL;
978 curwaterpvs = 0;
979 if(d)
981 loopi(d->len%9) curwaterpvs |= *curpvs++ << (i*8);
984 if(!usepvs || !usewaterpvs) curwaterpvs = 0;
987 void clearpvs()
989 DELETEP(viewcells);
990 pvs.setsizenodelete(0);
991 pvsbuf.setsizenodelete(0);
992 curpvs = NULL;
993 numwaterplanes = 0;
994 lockpvs = 0;
995 lockpvs_(false);
998 COMMAND(clearpvs, "");
1000 static void findwaterplanes()
1002 extern vector<vtxarray *> valist;
1003 loopi(MAXWATERPVS)
1005 waterplanes[i].height = -1;
1006 waterplanes[i].matsurfs.setsizenodelete(0);
1008 waterfalls.setsizenodelete(0);
1009 numwaterplanes = 0;
1010 loopv(valist)
1012 vtxarray *va = valist[i];
1013 loopj(va->matsurfs)
1015 materialsurface &m = va->matbuf[j];
1016 if(m.material!=MAT_WATER || m.orient==O_BOTTOM) continue;
1017 if(m.orient!=O_TOP)
1019 waterfalls.add(&m);
1020 continue;
1022 loopk(numwaterplanes) if(waterplanes[k].height == m.o.z)
1024 waterplanes[k].matsurfs.add(&m);
1025 goto nextmatsurf;
1027 if(numwaterplanes < MAXWATERPVS)
1029 waterplanes[numwaterplanes].height = m.o.z;
1030 waterplanes[numwaterplanes].matsurfs.add(&m);
1031 numwaterplanes++;
1033 nextmatsurf:;
1036 if(waterfalls.length() > 0 && numwaterplanes < MAXWATERPVS) numwaterplanes++;
1039 void testpvs(int *vcsize)
1041 lockpvs_(false);
1043 uint oldnumwaterplanes = numwaterplanes;
1044 int oldwaterplanes[MAXWATERPVS];
1045 loopi(numwaterplanes) oldwaterplanes[i] = waterplanes[i].height;
1047 findwaterplanes();
1049 pvsnode &root = origpvsnodes.add();
1050 memset(root.edges.v, 0xFF, 3);
1051 root.flags = 0;
1052 root.children = 0;
1053 genpvsnodes(worldroot);
1055 genpvs_canceled = false;
1056 check_genpvs_progress = false;
1058 int size = *vcsize>0 ? *vcsize : 32;
1059 for(int mask = 1; mask < size; mask <<= 1) size &= ~mask;
1061 ivec o = camera1->o;
1062 o.mask(~(size-1));
1063 pvsworker w;
1064 int len;
1065 lockedpvs = w.testviewcell(o, size, &lockedwaterpvs, &len);
1066 loopi(MAXWATERPVS) lockedwaterplanes[i] = waterplanes[i].height;
1067 lockpvs = 1;
1068 conoutf("generated test view cell of size %d at %.1f, %.1f, %.1f (%d B)", size, camera1->o.x, camera1->o.y, camera1->o.z, len);
1070 origpvsnodes.setsizenodelete(0);
1071 numwaterplanes = oldnumwaterplanes;
1072 loopi(numwaterplanes) waterplanes[i].height = oldwaterplanes[i];
1075 COMMAND(testpvs, "i");
1077 void genpvs(int *viewcellsize)
1079 if(hdr.worldsize > 1<<15)
1081 conoutf(CON_ERROR, "map is too large for PVS");
1082 return;
1085 computescreen("generating PVS (esc to abort)");
1086 genpvs_canceled = false;
1087 Uint32 start = SDL_GetTicks();
1089 show_out_of_renderloop_progress(0, "finding view cells");
1091 clearpvs();
1092 calcpvsbounds();
1093 findwaterplanes();
1095 pvsnode &root = origpvsnodes.add();
1096 memset(root.edges.v, 0xFF, 3);
1097 root.flags = 0;
1098 root.children = 0;
1099 genpvsnodes(worldroot);
1101 totalviewcells = countviewcells(worldroot, ivec(0, 0, 0), hdr.worldsize>>1, *viewcellsize>0 ? *viewcellsize : 32);
1102 numviewcells = 0;
1103 genpvs_canceled = false;
1104 check_genpvs_progress = false;
1105 SDL_TimerID timer = NULL;
1106 if(pvsthreads<=1)
1108 pvsworkers.add(new pvsworker);
1109 timer = SDL_AddTimer(500, genpvs_timer, NULL);
1111 viewcells = new viewcellnode;
1112 genviewcells(*viewcells, worldroot, ivec(0, 0, 0), hdr.worldsize>>1, *viewcellsize>0 ? *viewcellsize : 32);
1113 if(pvsthreads<=1)
1115 SDL_RemoveTimer(timer);
1117 else
1119 show_out_of_renderloop_progress(0, "creating threads");
1120 if(!pvsmutex) pvsmutex = SDL_CreateMutex();
1121 if(!viewcellmutex) viewcellmutex = SDL_CreateMutex();
1122 loopi(pvsthreads)
1124 pvsworker *w = pvsworkers.add(new pvsworker);
1125 w->thread = SDL_CreateThread(pvsworker::run, w);
1127 show_genpvs_progress(0, 0);
1128 while(!genpvs_canceled)
1130 SDL_Delay(500);
1131 SDL_LockMutex(viewcellmutex);
1132 int unique = pvs.length(), processed = numviewcells, remaining = viewcellrequests.length();
1133 SDL_UnlockMutex(viewcellmutex);
1134 show_genpvs_progress(unique, processed);
1135 if(!remaining) break;
1137 SDL_LockMutex(viewcellmutex);
1138 viewcellrequests.setsizenodelete(0);
1139 SDL_UnlockMutex(viewcellmutex);
1140 loopv(pvsworkers) SDL_WaitThread(pvsworkers[i]->thread, NULL);
1142 pvsworkers.deletecontentsp();
1144 origpvsnodes.setsizenodelete(0);
1145 pvscompress.clear();
1147 Uint32 end = SDL_GetTicks();
1148 if(genpvs_canceled)
1150 clearpvs();
1151 conoutf("genpvs aborted");
1153 else conoutf("generated %d unique view cells totaling %.1f kB and averaging %d B (%.1f seconds)",
1154 pvs.length(), pvsbuf.length()/1024.0f, pvsbuf.length()/max(pvs.length(), 1), (end - start) / 1000.0f);
1157 COMMAND(genpvs, "i");
1159 void pvsstats()
1161 conoutf("%d unique view cells totaling %.1f kB and averaging %d B",
1162 pvs.length(), pvsbuf.length()/1024.0f, pvsbuf.length()/max(pvs.length(), 1));
1165 COMMAND(pvsstats, "");
1167 static inline bool pvsoccluded(uchar *buf, const ivec &co, int size, const ivec &bborigin, const ivec &bbsize)
1169 uchar leafmask = buf[0];
1170 loopoctabox(co, size, bborigin, bbsize)
1172 ivec o(i, co.x, co.y, co.z, size);
1173 if(leafmask&(1<<i))
1175 uchar leafvalues = buf[1+i];
1176 if(!leafvalues || (leafvalues!=0xFF && octantrectangleoverlap(o, size>>1, bborigin, bbsize)&~leafvalues))
1177 return false;
1179 else if(!pvsoccluded(buf+9*buf[1+i], o, size>>1, bborigin, bbsize)) return false;
1181 return true;
1184 static inline bool pvsoccluded(uchar *buf, const ivec &bborigin, const ivec &bbsize)
1186 int diff = (bborigin.x^(bborigin.x+bbsize.x)) | (bborigin.y^(bborigin.y+bbsize.y)) | (bborigin.z^(bborigin.z+bbsize.z));
1187 if(diff&~((1<<worldscale)-1)) return false;
1188 int scale = worldscale-1;
1189 while(!(diff&(1<<scale)))
1191 int i = octastep(bborigin.x, bborigin.y, bborigin.z, scale);
1192 scale--;
1193 uchar leafmask = buf[0];
1194 if(leafmask&(1<<i))
1196 uchar leafvalues = buf[1+i];
1197 return leafvalues && (leafvalues==0xFF || !(octantrectangleoverlap(ivec(bborigin).mask(~((2<<scale)-1)), 1<<scale, bborigin, bbsize)&~leafvalues));
1199 buf += 9*buf[1+i];
1201 return pvsoccluded(buf, ivec(bborigin).mask(~((2<<scale)-1)), 1<<scale, bborigin, bbsize);
1204 bool pvsoccluded(const ivec &bborigin, const ivec &bbsize)
1206 return curpvs!=NULL && pvsoccluded(curpvs, bborigin, bbsize);
1209 bool waterpvsoccluded(int height)
1211 if(!curwaterpvs) return false;
1212 if(lockedpvs)
1214 loopi(MAXWATERPVS) if(lockedwaterplanes[i]==height) return (curwaterpvs&(1<<i))!=0;
1216 else
1218 loopi(numwaterplanes) if(waterplanes[i].height==height) return (curwaterpvs&(1<<i))!=0;
1220 return false;
1223 void saveviewcells(gzFile f, viewcellnode &p)
1225 gzputc(f, p.leafmask);
1226 loopi(8)
1228 if(p.leafmask&(1<<i))
1230 int pvsindex = p.children[i].pvs;
1231 endianswap(&pvsindex, sizeof(int), 1);
1232 gzwrite(f, &pvsindex, sizeof(int));
1234 else saveviewcells(f, *p.children[i].node);
1238 void savepvs(gzFile f)
1240 uint totallen = pvsbuf.length() | (numwaterplanes>0 ? 0x80000000U : 0);
1241 endianswap(&totallen, sizeof(uint), 1);
1242 gzwrite(f, &totallen, sizeof(uint));
1243 if(numwaterplanes>0)
1245 uint numwp = numwaterplanes;
1246 endianswap(&numwp, sizeof(uint), 1);
1247 gzwrite(f, &numwp, sizeof(uint));
1248 loopi(numwaterplanes)
1250 int height = waterplanes[i].height;
1251 endianswap(&height, sizeof(int), 1);
1252 gzwrite(f, &height, sizeof(int));
1253 if(waterplanes[i].height < 0) break;
1256 loopv(pvs)
1258 ushort len = pvs[i].len;
1259 endianswap(&len, sizeof(ushort), 1);
1260 gzwrite(f, &len, sizeof(ushort));
1262 gzwrite(f, pvsbuf.getbuf(), pvsbuf.length());
1263 saveviewcells(f, *viewcells);
1266 viewcellnode *loadviewcells(gzFile f)
1268 viewcellnode *p = new viewcellnode;
1269 p->leafmask = gzgetc(f);
1270 loopi(8)
1272 if(p->leafmask&(1<<i))
1274 gzread(f, &p->children[i].pvs, sizeof(int));
1275 endianswap(&p->children[i].pvs, sizeof(int), 1);
1277 else p->children[i].node = loadviewcells(f);
1279 return p;
1282 void loadpvs(gzFile f)
1284 uint totallen = pvsbuf.length();
1285 gzread(f, &totallen, sizeof(uint));
1286 endianswap(&totallen, sizeof(uint), 1);
1287 if(totallen & 0x80000000U)
1289 totallen &= ~0x80000000U;
1290 gzread(f, &numwaterplanes, sizeof(uint));
1291 endianswap(&numwaterplanes, sizeof(uint), 1);
1292 loopi(numwaterplanes)
1294 gzread(f, &waterplanes[i].height, sizeof(int));
1295 endianswap(&waterplanes[i].height, sizeof(int), 1);
1298 int offset = 0;
1299 loopi(hdr.numpvs)
1301 ushort len;
1302 gzread(f, &len, sizeof(ushort));
1303 endianswap(&len, sizeof(ushort), 1);
1304 pvs.add(pvsdata(offset, len));
1305 offset += len;
1307 gzread(f, pvsbuf.reserve(totallen).buf, totallen);
1308 pvsbuf.advance(totallen);
1309 viewcells = loadviewcells(f);
1312 int getnumviewcells() { return pvs.length(); }