3 #include "SDL_thread.h"
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;
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) \
36 res.coord = ((hi.coord&~0x11)>>1) + 0x44; \
41 else if(hi.coord==0xFF) \
43 res.coord = (lo.coord&0xEE)>>1; \
47 else if(lo.row!=hi.row || lo.col!=hi.col || (lo.coord&0xF0)!=0x80 || (hi.coord&0xF)!=0) return false; \
50 res.coord = ((lo.coord&~0xF1)>>1) | (((hi.coord&~0x1F)>>1) + 0x40); \
55 bvec
&res
= bbs
[x
+ 2*y
];
56 MERGEBBS(res
, z
, x
, y
);
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
);
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();
76 ivec
o(i
, co
.x
, co
.y
, co
.z
, size
);
77 pvsnode
&n
= origpvsnodes
.add();
80 if(c
[i
].children
|| isempty(c
[i
])) memset(n
.edges
.v
, 0xFF, 3);
83 uint face
= c
[i
].faces
[k
];
84 if(face
==F_SOLID
) n
.edges
[k
] = 0x80;
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));
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);
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
;
113 uchar rnear
, cnear
, rfar
, cfar
;
120 struct { ushort x
, y
, z
; };
124 ushort
&operator[](int i
) { return v
[i
]; }
125 ushort
operator[](int i
) const { return v
[i
]; }
133 struct { usvec min
, max
; };
137 shaftbb(const ivec
&o
, int 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
;
187 shaftplane planes
[8];
190 shaft(const shaftbb
&from
, const shaftbb
&to
)
195 void calcshaft(const shaftbb
&from
, const shaftbb
&to
)
197 uchar match
= 0, color
= 0;
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
]; }
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)
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;
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;
257 pvsdata(int offset
, int len
) : offset(offset
), len(len
) {}
260 static vector
<uchar
> pvsbuf
;
262 static inline uint
hthash(const pvsdata
&k
)
265 loopi(k
.len
) h
= ((h
<<5)+h
)^pvsbuf
[k
.offset
+i
];
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
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
298 vector
<materialsurface
*> matsurfs
;
299 } waterplanes
[MAXWATERPVS
];
300 static vector
<materialsurface
*> waterfalls
;
301 uint numwaterplanes
= 0;
305 pvsworker() : thread(NULL
), pvsnodes(new pvsnode
[origpvsnodes
.length()])
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;
340 pvsnode
*cur
= levels
[curlevel
];
341 while(cur
->children
&& !(cur
->flags
&PVS_HIDE_BB
))
343 cur
= &pvsnodes
[cur
->children
];
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))
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;
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;
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
)
390 pvsnode
*children
= &pvsnodes
[p
.children
];
391 loopi(8) hidepvs(children
[i
]);
392 p
.flags
|= PVS_HIDE_BB
;
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; }
407 pvsnode
*children
= &pvsnodes
[p
.children
];
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
;
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
];
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
);
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
])
449 rmin
[dim
] = geom
[dim
+ 3*dc
] + (dc
? -1 : 0);
450 rmin
[r
] = geom
.min
[r
];
453 rmax
[r
] = geom
.max
[r
] - 1;
454 int rcenter
= (rmin
[r
] + rmax
[r
])/2;
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);
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
;)
477 cmin
[c
] += cminstep
; cminstep
= INT_MIN
;
480 for(int rstep
= 1; rstep
&& cmin
[r
] <= rmax
[r
];)
482 rstep
= hasvoxel(cmin
, r
, 1, c
, 0, &cminstep
);
485 if(cmin
[r
] <= rmax
[r
]) cminstep
= 0;
489 cmax
[c
] += cmaxstep
; cmaxstep
= INT_MAX
;
492 for(int rstep
= 1; rstep
&& cmax
[r
] <= rmax
[r
];)
494 rstep
= hasvoxel(cmax
, r
, 1, c
, 1, &cmaxstep
);
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
;)
513 emin
[r
] += -1; rminstep
= INT_MIN
;
516 for(int cstep
= 1; cstep
&& emin
[c
] <= cmax
[c
];)
518 cstep
= hasvoxel(emin
, c
, 1, r
, 0, &rminstep
);
521 if(emin
[c
] <= cmax
[c
]) rminstep
= 0;
525 emax
[r
] += 1; rmaxstep
= INT_MAX
;
528 for(int cstep
= 1; cstep
&& emax
[c
] <= cmax
[c
];)
530 cstep
= hasvoxel(emax
, c
, 1, r
, 1, &rmaxstep
);
533 if(emax
[c
] <= cmax
[c
]) rmaxstep
= 0;
536 if(!rminstep
) emin
[r
]++;
537 if(!rmaxstep
) emax
[r
]--;
539 bb
.min
[dim
] = rmin
[dim
];
540 bb
.max
[dim
] = rmin
[dim
]+1;
542 bb
.max
[r
] = emax
[r
]+1;
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
,
554 int odim
= j
< 2 ? c
: r
;
557 if(bb
.max
[odim
] >= viewcellbb
.max
[odim
]) continue;
558 dmax
[odim
] = bb
.max
[odim
]-1;
562 if(bb
.min
[odim
] <= viewcellbb
.min
[odim
]) continue;
563 dmax
[odim
] = bb
.min
[odim
];
566 dmax
[dim
] = bb
.min
[dim
];
567 int stepdim
= j
< 2 ? r
: c
, stepstart
= bb
.min
[stepdim
], stepend
= bb
.max
[stepdim
];
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
;
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
]);
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);
596 if(prevblockers
[j
].contains(bb
)) { dup
= true; break; }
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;
618 if(!compresspvs(children
[i
], size
/2, threshold
)) canreduce
= false;
622 int hide
= children
[7].flags
&PVS_HIDE_BB
;
623 loopi(7) if((children
[i
].flags
&PVS_HIDE_BB
)!=hide
) canreduce
= false;
626 p
.flags
= (p
.flags
& ~PVS_HIDE_BB
) | hide
;
631 if(size
<= threshold
)
639 vector
<uchar
> outbuf
;
641 bool serializepvs(pvsnode
&p
, int storage
= -1)
646 loopi(8) outbuf
.add(p
.flags
&PVS_HIDE_BB
? 0xFF : 0);
649 int index
= outbuf
.length();
650 pvsnode
*children
= &pvsnodes
[p
.children
];
652 uchar leafvalues
= 0;
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
);
668 loopj(8) outbuf
.add(leafvalues
&(1<<j
) ? 0xFF : 0);
669 uchar leafmask
= (1<<i
)-1;
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
;
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;
692 bool materialoccluded(vector
<materialsurface
*> &matsurfs
)
694 if(pvsnodes
[0].flags
& PVS_HIDE_BB
) return true;
695 if(!pvsnodes
[0].children
) return false;
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
;
705 if(!materialoccluded(pvsnodes
[0], vec(0, 0, 0), hdr
.worldsize
/2, bborigin
, bbsize
)) return false;
710 int wateroccluded
, waterbytes
;
712 void calcpvs(const ivec
&co
, int size
)
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]);
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
;
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
)
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();
751 int genviewcell(const ivec
&co
, int size
)
755 if(pvsmutex
) SDL_LockMutex(pvsmutex
);
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
);
764 val
= &pvscompress
[key
];
768 if(pvsmutex
) SDL_UnlockMutex(pvsmutex
);
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
);
798 viewcellnode() : leafmask(0xFF)
800 loopi(8) children
[i
].pvs
= -1;
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;
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
;
844 vtxarray
*va
= valist
[i
];
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
)
859 if(h
.children
? !isallclip(h
.children
) : (!isentirelysolid(h
) && (!h
.ext
|| (h
.ext
->material
&MATF_CLIP
)!=MAT_CLIP
)))
865 static int countviewcells(cube
*c
, const ivec
&co
, int size
, int threshold
)
870 ivec
o(i
, co
.x
, co
.y
, co
.z
, size
);
871 if(pvsbounds
.outside(o
, size
)) continue;
877 count
+= countviewcells(h
.children
, o
, size
>>1, threshold
);
880 if(isallclip(h
.children
)) continue;
882 else if(isentirelysolid(h
) || (h
.ext
&& (h
.ext
->material
&MATF_CLIP
)==MAT_CLIP
)) continue;
888 static void genviewcells(viewcellnode
&p
, cube
*c
, const ivec
&co
, int size
, int threshold
)
890 if(genpvs_canceled
) return;
893 ivec
o(i
, co
.x
, co
.y
, co
.z
, size
);
894 if(pvsbounds
.outside(o
, size
)) continue;
900 p
.leafmask
&= ~(1<<i
);
901 p
.children
[i
].node
= new viewcellnode
;
902 genviewcells(*p
.children
[i
].node
, h
.children
, o
, size
>>1, threshold
);
905 if(isallclip(h
.children
)) continue;
907 else if(isentirelysolid(h
) || (h
.ext
&& (h
.ext
->material
&MATF_CLIP
)==MAT_CLIP
)) continue;
910 if(genpvs_canceled
) return;
911 p
.children
[i
].pvs
= pvsworkers
[0]->genviewcell(o
, size
);
912 if(check_genpvs_progress
) show_genpvs_progress();
916 viewcellrequest
&req
= viewcellrequests
.add();
917 req
.result
= &p
.children
[i
].pvs
;
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
;
946 static void lockpvs_(bool lock
)
948 if(lockedpvs
) DELETEA(lockedpvs
);
950 pvsdata
*d
= lookupviewcell(camera1
->o
);
952 int wbytes
= d
->len
%9, len
= d
->len
- wbytes
;
953 lockedpvs
= new uchar
[len
];
954 memcpy(lockedpvs
, &pvsbuf
[d
->offset
+ wbytes
], len
);
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
;
972 curwaterpvs
= lockedwaterpvs
;
976 pvsdata
*d
= lookupviewcell(p
);
977 curpvs
= d
? &pvsbuf
[d
->offset
] : NULL
;
981 loopi(d
->len
%9) curwaterpvs
|= *curpvs
++ << (i
*8);
984 if(!usepvs
|| !usewaterpvs
) curwaterpvs
= 0;
990 pvs
.setsizenodelete(0);
991 pvsbuf
.setsizenodelete(0);
998 COMMAND(clearpvs
, "");
1000 static void findwaterplanes()
1002 extern vector
<vtxarray
*> valist
;
1005 waterplanes
[i
].height
= -1;
1006 waterplanes
[i
].matsurfs
.setsizenodelete(0);
1008 waterfalls
.setsizenodelete(0);
1012 vtxarray
*va
= valist
[i
];
1015 materialsurface
&m
= va
->matbuf
[j
];
1016 if(m
.material
!=MAT_WATER
|| m
.orient
==O_BOTTOM
) continue;
1022 loopk(numwaterplanes
) if(waterplanes
[k
].height
== m
.o
.z
)
1024 waterplanes
[k
].matsurfs
.add(&m
);
1027 if(numwaterplanes
< MAXWATERPVS
)
1029 waterplanes
[numwaterplanes
].height
= m
.o
.z
;
1030 waterplanes
[numwaterplanes
].matsurfs
.add(&m
);
1036 if(waterfalls
.length() > 0 && numwaterplanes
< MAXWATERPVS
) numwaterplanes
++;
1039 void testpvs(int *vcsize
)
1043 uint oldnumwaterplanes
= numwaterplanes
;
1044 int oldwaterplanes
[MAXWATERPVS
];
1045 loopi(numwaterplanes
) oldwaterplanes
[i
] = waterplanes
[i
].height
;
1049 pvsnode
&root
= origpvsnodes
.add();
1050 memset(root
.edges
.v
, 0xFF, 3);
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
;
1065 lockedpvs
= w
.testviewcell(o
, size
, &lockedwaterpvs
, &len
);
1066 loopi(MAXWATERPVS
) lockedwaterplanes
[i
] = waterplanes
[i
].height
;
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");
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");
1095 pvsnode
&root
= origpvsnodes
.add();
1096 memset(root
.edges
.v
, 0xFF, 3);
1099 genpvsnodes(worldroot
);
1101 totalviewcells
= countviewcells(worldroot
, ivec(0, 0, 0), hdr
.worldsize
>>1, *viewcellsize
>0 ? *viewcellsize
: 32);
1103 genpvs_canceled
= false;
1104 check_genpvs_progress
= false;
1105 SDL_TimerID timer
= NULL
;
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);
1115 SDL_RemoveTimer(timer
);
1119 show_out_of_renderloop_progress(0, "creating threads");
1120 if(!pvsmutex
) pvsmutex
= SDL_CreateMutex();
1121 if(!viewcellmutex
) viewcellmutex
= SDL_CreateMutex();
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
)
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();
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");
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
);
1175 uchar leafvalues
= buf
[1+i
];
1176 if(!leafvalues
|| (leafvalues
!=0xFF && octantrectangleoverlap(o
, size
>>1, bborigin
, bbsize
)&~leafvalues
))
1179 else if(!pvsoccluded(buf
+9*buf
[1+i
], o
, size
>>1, bborigin
, bbsize
)) return false;
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
);
1193 uchar leafmask
= buf
[0];
1196 uchar leafvalues
= buf
[1+i
];
1197 return leafvalues
&& (leafvalues
==0xFF || !(octantrectangleoverlap(ivec(bborigin
).mask(~((2<<scale
)-1)), 1<<scale
, bborigin
, bbsize
)&~leafvalues
));
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;
1214 loopi(MAXWATERPVS
) if(lockedwaterplanes
[i
]==height
) return (curwaterpvs
&(1<<i
))!=0;
1218 loopi(numwaterplanes
) if(waterplanes
[i
].height
==height
) return (curwaterpvs
&(1<<i
))!=0;
1223 void saveviewcells(gzFile f
, viewcellnode
&p
)
1225 gzputc(f
, p
.leafmask
);
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;
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
);
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
);
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);
1302 gzread(f
, &len
, sizeof(ushort
));
1303 endianswap(&len
, sizeof(ushort
), 1);
1304 pvs
.add(pvsdata(offset
, len
));
1307 gzread(f
, pvsbuf
.reserve(totallen
).buf
, totallen
);
1308 pvsbuf
.advance(totallen
);
1309 viewcells
= loadviewcells(f
);
1312 int getnumviewcells() { return pvs
.length(); }