4 import iv
.glbinds
.utils
;
11 static import iv
.timer
;
15 version = voxdata_debug
;
16 //version = vox_check_invariants;
19 // ////////////////////////////////////////////////////////////////////////// //
20 static align(1) struct VoxXYZ16
{
22 ushort x
= void, y
= void, z
= void;
26 // ////////////////////////////////////////////////////////////////////////// //
28 this is a struct that keeps info about an individual voxel.
29 it keeps voxel color, and face visibility info.
31 align(1) struct VoxPix
{
35 uint nextz
; // voxel with the next z; 0 means "no more"
36 ushort z
; // z of the current voxel
38 uint rgb () const pure nothrow @safe @nogc {
40 return 0xff000000U|b|
(cast(uint)g
<<8)|
(cast(uint)r
<<16);
43 uint rgbcull () const pure nothrow @safe @nogc {
45 return b|
(cast(uint)g
<<8)|
(cast(uint)r
<<16)|
(cast(uint)cull
<<24);
50 // ////////////////////////////////////////////////////////////////////////// //
52 this keeps voxel "voxmap" (it's like a pixmap, but with voxels instead
53 of pixels). the representation is slightly optimised, tho: it keeps only
54 actually used voxels, in vertical "slabs". as most voxel models have only
55 contour voxels stored, this greatly reduces memory consumption. quering
56 the individial voxel data is slightly slower, tho, but for our case it is
59 there are some methods to "optimise" the voxmap. for example, to fix voxel
60 face visibility info, and to remove "internal" voxels (it is called "hollow fill").
62 also note that some optimisation methods may leave empty voxels in the voxmap.
63 they have `cull` field equal to zero.
65 to build a voxmap you simply call `addVoxel()` method, optionally providing
66 the face visibility info. but you can use `0x3f` as a visibility, and then ask
67 `VoxelData` object to calculate the proprer "cull" values for you. you can also
68 add "internal" voxels, and then ask the object to fix the vixmap for you, removing
69 all the unnecessary data.
73 uint xsize
= 0, ysize
= 0, zsize
= 0;
74 float cx
= 0.0f, cy
= 0.0f, cz
= 0.0f;
76 VoxPix
[] data
; // [0] is never used
77 // xsize*ysize array, offsets in `data`; 0 means "no data here"
78 // slabs are sorted from bottom to top, and never intersects
84 void save (VFile fl
) {
85 fl
.rawWriteExact("K8VOXDT0");
86 fl
.writeNum
!ushort(cast(ushort)xsize
);
87 fl
.writeNum
!ushort(cast(ushort)ysize
);
88 fl
.writeNum
!ushort(cast(ushort)zsize
);
94 foreach (int y
; 0..ysize
) {
95 foreach (int x
; 0..xsize
) {
96 for (uint dofs
= getDOfs(x
, y
); dofs
; dofs
= data
[dofs
].nextz
) {
97 if (data
[dofs
].cull
) ++voxelCount
;
101 fl
.writeNum(voxelCount
);
103 foreach (int y
; 0..ysize
) {
104 foreach (int x
; 0..xsize
) {
106 for (uint dofs
= getDOfs(x
, y
); dofs
; dofs
= data
[dofs
].nextz
) {
107 if (data
[dofs
].cull
) ++count
;
109 fl
.writeNum
!ushort(cast(ushort)count
);
110 for (uint dofs
= getDOfs(x
, y
); dofs
; dofs
= data
[dofs
].nextz
) {
111 if (data
[dofs
].cull
) {
112 fl
.writeNum(data
[dofs
].b
);
113 fl
.writeNum(data
[dofs
].g
);
114 fl
.writeNum(data
[dofs
].r
);
115 fl
.writeNum(data
[dofs
].cull
);
116 fl
.writeNum(data
[dofs
].z
);
123 void load (VFile fl
) {
125 fl
.rawReadExact(sign
[]);
126 if (sign
[] != "K8VOXDT0") throw new Exception("invalid voxel data");
128 xsize
= fl
.readNum
!ushort;
129 ysize
= fl
.readNum
!ushort;
130 zsize
= fl
.readNum
!ushort;
131 cx
= fl
.readNum
!float;
132 cy
= fl
.readNum
!float;
133 cz
= fl
.readNum
!float;
135 const uint voxelCount
= fl
.readNum
!uint;
137 xyofs
= new uint[xsize
*ysize
];
139 data
= new VoxPix
[voxelCount
+1]; // [0] is unused
142 voxpixtotal
= voxelCount
;
145 foreach (int y
; 0..ysize
) {
146 foreach (int x
; 0..xsize
) {
147 uint count
= fl
.readNum
!ushort;
149 xyofs
[cast(uint)y
*xsize
+cast(uint)x
] = 0;
152 xyofs
[cast(uint)y
*xsize
+cast(uint)x
] = cpos
;
155 VoxPix
*vx
= &data
[cpos
];
156 vx
.b
= fl
.readNum
!ubyte;
157 vx
.g
= fl
.readNum
!ubyte;
158 vx
.r
= fl
.readNum
!ubyte;
159 vx
.cull
= fl
.readNum
!ubyte;
161 if (!vx
.cull
) throw new Exception("invalid voxel data (cull)");
162 vx
.z
= fl
.readNum
!ushort;
164 if (data
[prevz
].z
>= vx
.z
) throw new Exception("invalid voxel data (z)");
165 data
[prevz
].nextz
= cast(int)cpos
;
167 prevz
= cast(int)cpos
;
172 assert(cpos
== data
.length
);
176 uint allocVox () nothrow @safe {
180 if (data
.length
>= 0x3fffffffU
) assert(0, "too many voxels");
181 const uint lastel
= cast(uint)data
.length
;
183 freelist
= cast(uint)data
.length
-1;
184 while (freelist
>= lastel
) {
185 data
[freelist
].nextz
= freelist
+1;
191 const uint res
= freelist
;
192 freelist
= data
[res
].nextz
;
202 xsize
= ysize
= zsize
= 0;
208 void setSize (uint xs
, uint ys
, uint zs
) {
210 if (!xs ||
!ys ||
!zs
) return;
214 xyofs
= new uint[xsize
*ysize
];
216 data
.length
= 1; // data[0] is never used
219 uint getDOfs (int x
, int y
) const nothrow @trusted @nogc {
220 pragma(inline
, true);
221 if (x
< 0 || y
< 0) return 0;
222 if (cast(uint)x
>= xsize ||
cast(uint)y
>= ysize
) return 0;
223 return xyofs
.ptr
[cast(uint)y
*xsize
+cast(uint)x
];
226 // high byte is cull info
227 // returns 0 if there is no such voxel
228 uint voxofs (int x
, int y
, int z
) const nothrow @safe @nogc {
229 uint dofs
= getDOfs(x
, y
);
231 if (data
[dofs
].z
== cast(ushort)z
) return dofs
;
232 if (data
[dofs
].z
> cast(ushort)z
) return 0;
233 dofs
= data
[dofs
].nextz
;
238 // high byte is cull info
239 // returns 0 if there is no such voxel
240 uint query (int x
, int y
, int z
) const nothrow @safe @nogc {
241 immutable uint dofs
= voxofs(x
, y
, z
);
243 if (!data
[dofs
].cull
) return 0;
244 return data
[dofs
].rgbcull();
247 VoxPix
*queryVP (int x
, int y
, int z
) nothrow @trusted @nogc {
248 pragma(inline
, true);
249 immutable uint dofs
= voxofs(x
, y
, z
);
250 return (dofs ?
&data
[dofs
] : null);
253 // high byte is cull info
254 // returns 0 if there is no such voxel
255 ubyte queryCull (int x
, int y
, int z
) const nothrow @safe @nogc {
256 immutable uint dofs
= voxofs(x
, y
, z
);
257 return (dofs ? data
[dofs
].cull
: 0);
260 void removeVoxel (int x
, int y
, int z
) nothrow @safe @nogc {
261 uint dofs
= getDOfs(x
, y
);
264 if (data
[dofs
].z
== cast(ushort)z
) {
267 data
[prevdofs
].nextz
= data
[dofs
].nextz
;
269 xyofs
[cast(uint)y
*xsize
+cast(uint)x
] = data
[dofs
].nextz
;
271 data
[dofs
].nextz
= freelist
;
276 if (data
[dofs
].z
> cast(ushort)z
) return;
278 dofs
= data
[dofs
].nextz
;
282 void addVoxel (int x
, int y
, int z
, uint rgb
, ubyte cull
) nothrow @safe {
284 if (!cull
) { removeVoxel(x
, y
, z
); return; }
285 if (x
< 0 || y
< 0 || z
< 0) return;
286 if (cast(uint)x
>= xsize ||
cast(uint)y
>= ysize ||
cast(uint)z
>= zsize
) return;
287 uint dofs
= getDOfs(x
, y
);
290 if (data
[dofs
].z
== cast(ushort)z
) {
291 // replace this voxel
292 data
[dofs
].b
= rgb
&0xff;
293 data
[dofs
].g
= (rgb
>>8)&0xff;
294 data
[dofs
].r
= (rgb
>>16)&0xff;
295 data
[dofs
].cull
= cull
;
298 if (data
[dofs
].z
> cast(ushort)z
) break;
300 dofs
= data
[dofs
].nextz
;
302 // insert before dofs
303 immutable uint vidx
= allocVox();
304 data
[vidx
].b
= rgb
&0xff;
305 data
[vidx
].g
= (rgb
>>8)&0xff;
306 data
[vidx
].r
= (rgb
>>16)&0xff;
307 data
[vidx
].cull
= cull
;
308 data
[vidx
].z
= cast(ushort)z
;
309 data
[vidx
].nextz
= dofs
;
311 assert(data
[prevdofs
].nextz
== dofs
);
312 data
[prevdofs
].nextz
= vidx
;
314 xyofs
[cast(uint)y
*xsize
+cast(uint)x
] = vidx
;
318 // only for existing voxels; won't remove empty voxels
319 void setVoxelCull (int x
, int y
, int z
, ubyte cull
) nothrow @safe @nogc {
320 VoxPix
*vp
= queryVP(x
, y
, z
);
321 if (vp
) vp
.cull
= cast(ubyte)(cull
&0x3f);
324 version(vox_check_invariants
)
325 void checkInvariants () const nothrow /*@safe*/ @trusted /*@nogc*/ {
326 version(voxdata_debug
) conwriteln("checking invariants...");
328 foreach (uint y
; 0..ysize
) {
329 foreach (uint x
; 0..xsize
) {
330 uint dofs
= getDOfs(x
, y
);
333 ushort prevz
= data
[dofs
].z
;
334 dofs
= data
[dofs
].nextz
;
337 assert(prevz
< data
[dofs
].z
, "broken voxel data Z invariant");
338 prevz
= data
[dofs
].z
;
339 dofs
= data
[dofs
].nextz
;
343 assert(voxcount
== voxpixtotal
, "invalid number of voxels");
346 void removeEmptyVoxels () nothrow /*@safe*/ @trusted /*@nogc*/ {
347 version(voxdata_debug
) conwriteln("removing empty voxels...");
349 foreach (uint y
; 0..ysize
) {
350 foreach (uint x
; 0..xsize
) {
351 uint dofs
= getDOfs(x
, y
);
355 if (!data
[dofs
].cull
) {
357 const uint ndofs
= data
[dofs
].nextz
;
359 data
[prevdofs
].nextz
= ndofs
;
361 xyofs
[cast(uint)y
*xsize
+cast(uint)x
] = ndofs
;
363 data
[dofs
].nextz
= freelist
;
370 dofs
= data
[dofs
].nextz
;
375 if (count
) conwriteln("removed ", count
, " empty voxel", (count
!= 1 ?
"s" : ""));
378 static immutable int[3][6] cullofs
= [
384 [ 0, 0,-1], // bottom
387 static ubyte cullmask (uint cidx
) pure nothrow @safe @nogc {
388 pragma(inline
, true);
389 return cast(ubyte)(1U<<cidx
);
393 static ubyte cullopmask (uint cidx
) pure nothrow @safe @nogc {
394 pragma(inline
, true);
395 return cast(ubyte)(1U<<(cidx^
1));
398 // remove inside voxels, leaving only contour
399 void removeInsideFaces () nothrow /*@safe*/ @trusted {
400 version(voxdata_debug
) conwriteln("removing inside voxels...");
401 foreach (int y
; 0..ysize
) {
402 foreach (int x
; 0..xsize
) {
403 for (uint dofs
= getDOfs(x
, y
); dofs
; dofs
= data
[dofs
].nextz
) {
404 if (!data
[dofs
].cull
) continue;
406 const int z
= cast(int)data
[dofs
].z
;
407 foreach (uint cidx
; 0..6) {
408 // go in this dir, removing the corresponding voxel side
409 immutable ubyte cmask
= cullmask(cidx
);
410 immutable ubyte opmask
= cullopmask(cidx
);
411 immutable ubyte checkmask
= cmask|opmask
;
412 immutable int dx
= cullofs
[cidx
][0];
413 immutable int dy
= cullofs
[cidx
][1];
414 immutable int dz
= cullofs
[cidx
][2];
415 int vx
= x
, vy
= y
, vz
= z
;
417 while (myofs
&& (data
[myofs
].cull
&cmask
)) {
418 immutable int sx
= vx
+dx
;
419 immutable int sy
= vy
+dy
;
420 immutable int sz
= vz
+dz
;
421 immutable uint sofs
= voxofs(sx
, sy
, sz
);
423 if (!(data
[sofs
].cull
&checkmask
)) break;
425 data
[myofs
].cull ^
= cmask
;
426 data
[sofs
].cull
&= cast(ubyte)(~cast(uint)opmask
);
438 // if we have ANY voxel at the corresponding side, don't render that face
439 // return number of fixed voxels
440 uint fixFaceVisibility () nothrow @safe @nogc {
442 foreach (int y
; 0..ysize
) {
443 foreach (int x
; 0..xsize
) {
444 for (uint dofs
= getDOfs(x
, y
); dofs
; dofs
= data
[dofs
].nextz
) {
445 const ubyte ocull
= data
[dofs
].cull
;
446 if (!ocull
) continue;
447 const int z
= cast(int)data
[dofs
].z
;
448 // if we have ANY voxel at the corresponding side, don't render that face
449 foreach (uint cidx
; 0..6) {
450 const ubyte cmask
= cullmask(cidx
);
451 if (data
[dofs
].cull
&cmask
) {
452 if (queryCull(x
+cullofs
[cidx
][0], y
+cullofs
[cidx
][1], z
+cullofs
[cidx
][2])) {
453 data
[dofs
].cull ^
= cmask
; // reset bit
457 count
+= (data
[dofs
].cull
!= ocull
);
464 void create3DBitmap (ref Vox3DBitmap bmp
) {
465 bmp
.setSize(xsize
, ysize
, zsize
);
466 foreach (int y
; 0..ysize
) {
467 foreach (int x
; 0..xsize
) {
468 for (uint dofs
= getDOfs(x
, y
); dofs
; dofs
= data
[dofs
].nextz
) {
469 if (data
[dofs
].cull
) bmp
.setPixel(x
, y
, cast(int)data
[dofs
].z
);
475 // this fills everything outside of the voxel, and
476 // then resets culling bits for all invisible faces
477 // i don't care about memory yet
478 uint hollowFill () @trusted {
480 scope(exit
) { bmp
.clear(); }
481 bmp
.setSize(xsize
+2, ysize
+2, zsize
+2);
484 scope(exit
) delete stack
;
487 stack
.length
= 32768;
488 stack
.assumeSafeAppend
;
490 assert(xsize
<= cast(uint)stack
.length
);
492 // this is definitely empty
494 xyz
.x
= xyz
.y
= xyz
.z
= 0;
495 bmp
.setPixel(cast(int)xyz
.x
, cast(int)xyz
.y
, cast(int)xyz
.z
);
496 stack
[stackpos
++] = xyz
;
498 immutable int[3][6] deltas
= [
507 auto tm
= iv
.timer
.Timer(true);
509 xyz
= stack
[--stackpos
];
510 for (uint dd = 0; dd < 6; ++dd) {
511 const int nx
= cast(int)xyz
.x
+deltas
[dd][0];
512 const int ny
= cast(int)xyz
.y
+deltas
[dd][1];
513 const int nz
= cast(int)xyz
.z
+deltas
[dd][2];
514 if (bmp
.setPixel(nx
, ny
, nz
)) continue;
515 if (queryCull(nx
-1, ny
-1, nz
-1)) continue;
516 if (stackpos
== cast(uint)stack
.length
) {
517 stack
.length
+= 32768;
518 stack
.assumeSafeAppend
;
520 stack
[stackpos
++] = VoxXYZ16(cast(ushort)nx
, cast(ushort)ny
, cast(ushort)nz
);
524 version(voxdata_debug
) conwriteln("*** flooded in ", tm
.toString(), " ", stack
.length
, " stack items used.");
526 // unmark contour voxels
527 // this is required for proper face removing
528 foreach (int y
; 0..ysize
) {
529 foreach (int x
; 0..xsize
) {
530 for (uint dofs
= getDOfs(x
, y
); dofs
; dofs
= data
[dofs
].nextz
) {
531 if (!data
[dofs
].cull
) continue;
532 const int z
= cast(int)data
[dofs
].z
;
533 bmp
.resetPixel(x
+1, y
+1, z
+1);
540 foreach (int y
; 0..ysize
) {
541 foreach (int x
; 0..xsize
) {
542 for (uint dofs
= getDOfs(x
, y
); dofs
; dofs
= data
[dofs
].nextz
) {
543 immutable ubyte omask
= data
[dofs
].cull
;
544 if (!omask
) continue;
545 data
[dofs
].cull
= 0x3f;
547 const int z
= cast(int)data
[dofs
].z
;
548 foreach (uint cidx
; 0..6) {
549 immutable ubyte cmask
= cullmask(cidx
);
550 if (!(data
[dofs
].cull
&cmask
)) continue;
551 const int nx
= x
+cullofs
[cidx
][0];
552 const int ny
= y
+cullofs
[cidx
][1];
553 const int nz
= z
+cullofs
[cidx
][2];
554 if (bmp
.getPixel(nx
+1, ny
+1, nz
+1)) continue;
555 // reset this cull bit
556 data
[dofs
].cull ^
= cmask
;
558 changed
+= (omask
!= data
[dofs
].cull
);
565 void optimise (bool doHollowFill
) @trusted {
566 version(vox_check_invariants
) checkInvariants();
567 version(voxdata_debug
) conwriteln("optimising mesh with ", voxpixtotal
, " individual voxels...");
569 version(voxdata_debug
) conwriteln("optimising voxel culling...");
570 uint count
= hollowFill();
571 if (count
) conwriteln("fixed ", count
, " voxel", (count
!= 1 ?
"s" : ""));
572 //count = fixFaceVisibility();
573 //if (count) conwriteln("fixed ", count, " voxel", (count != 1 ? "s" : ""));
576 version(voxdata_debug
) conwriteln("optimising voxel culling...");
577 uint count
= fixFaceVisibility();
578 if (count
) conwriteln("fixed ", count
, " voxel", (count
!= 1 ?
"s" : ""));
581 version(vox_check_invariants
) checkInvariants();
582 version(voxdata_debug
) conwriteln("final optimised mesh contains ", voxpixtotal
, " individual voxels...");
587 // ////////////////////////////////////////////////////////////////////////// //
589 voxel data optimised for queries
591 each slab is kept in this format:
594 then run index follows:
597 ushort ofs (from this z)
599 index always ends with zhi+1, zhi+1
600 then (b,g,r,cull) array
602 struct VoxelDataSmall
{
604 uint xsize
= 0, ysize
= 0, zsize
= 0;
605 float cx
= 0.0f, cy
= 0.0f, cz
= 0.0f;
608 // xsize*ysize array, offsets in `data`; 0 means "no data here"
609 // slabs are sorted from bottom to top, and never intersects
613 void save (VFile fl
) {
614 fl
.rawWriteExact("K8VOXDC0");
615 fl
.writeNum
!ushort(cast(ushort)xsize
);
616 fl
.writeNum
!ushort(cast(ushort)ysize
);
617 fl
.writeNum
!ushort(cast(ushort)zsize
);
621 fl
.rawWriteExact(xyofs
[]);
622 fl
.writeNum
!uint(cast(uint)data
.length
);
623 fl
.rawWriteExact(data
[]);
627 void load (VFile fl
) {
629 fl
.rawReadExact(sign
[]);
630 if (sign
[] != "K8VOXDC0") throw new Exception("invalid compressed voxel data");
631 xsize
= fl
.readNum
!ushort;
632 ysize
= fl
.readNum
!ushort;
633 zsize
= fl
.readNum
!ushort;
634 cx
= fl
.readNum
!float;
635 cy
= fl
.readNum
!float;
636 cz
= fl
.readNum
!float;
638 xyofs
= new uint[xsize
*ysize
];
639 fl
.rawReadExact(xyofs
[]);
640 const uint dsz
= fl
.readNum
!uint;
642 data
= new ubyte[dsz
];
643 fl
.rawReadExact(data
[]);
647 void appendByte (ubyte v
) @trusted {
648 pragma(inline
, true);
649 data
.assumeSafeAppend
;
653 void appendShort (ushort v
) @trusted {
654 pragma(inline
, true);
655 data
.assumeSafeAppend
;
656 data
~= cast(ubyte)v
;
657 data
.assumeSafeAppend
;
658 data
~= cast(ubyte)(v
>>8);
662 private uint createSlab (ref VoxelData vox
, uint dofs0
) {
663 while (dofs0
&& !vox
.data
[dofs0
].cull
) dofs0
= vox
.data
[dofs0
].nextz
;
664 if (!dofs0
) return 0;
665 // calculate zlo and zhi, and count runs
667 ushort z0
= vox
.data
[dofs0
].z
;
669 ushort nxz
= cast(ushort)(z0
-1);
670 for (uint dofs
= dofs0
; dofs
; dofs
= vox
.data
[dofs
].nextz
) {
671 if (!vox
.data
[dofs
].cull
) continue;
672 z1
= vox
.data
[dofs
].z
;
673 if (z1
!= nxz
) ++runcount
;
674 nxz
= cast(ushort)(z1
+1);
677 if (data
.length
== 0) appendByte(0); // unused
678 const uint startofs
= cast(uint)data
.length
;
684 appendShort(runcount
);
685 // run index (will be filled later)
686 uint idxofs
= cast(uint)data
.length
;
687 for (uint f
= 0; f
< runcount
; ++f
) {
688 appendShort(0); // z0
689 appendShort(0); // z1
690 appendShort(0); // offset
691 appendShort(0); // reserved
694 appendShort(cast(ushort)(z1
+1));
695 appendShort(cast(ushort)(z1
+1));
696 appendShort(0); // offset
697 appendShort(0); // reserved
698 nxz
= cast(ushort)(z0
-1);
699 ushort lastz
= ushort.max
;
701 for (uint dofs
= dofs0
; dofs
; dofs
= vox
.data
[dofs
].nextz
) {
702 if (!vox
.data
[dofs
].cull
) continue;
703 z1
= vox
.data
[dofs
].z
;
707 if (lastz
!= ushort.max
) {
708 data
[idxofs
-6] = cast(ubyte)lastz
;
709 data
[idxofs
-5] = cast(ubyte)(lastz
>>8);
713 const uint rofs
= cast(uint)data
.length
-idxofs
;
714 assert(rofs
<= 0xffffU
);
716 data
[idxofs
++] = cast(ubyte)z1
;
717 data
[idxofs
++] = cast(ubyte)(z1
>>8);
721 data
[idxofs
++] = cast(ubyte)rofs
;
722 data
[idxofs
++] = cast(ubyte)(rofs
>>8);
726 lastz
= nxz
= cast(ushort)(z1
+1);
728 appendByte(vox
.data
[dofs
].b
);
729 appendByte(vox
.data
[dofs
].g
);
730 appendByte(vox
.data
[dofs
].r
);
731 appendByte(vox
.data
[dofs
].cull
);
734 assert(lastz
!= ushort.max
);
735 data
[idxofs
-6] = cast(ubyte)lastz
;
736 data
[idxofs
-5] = cast(ubyte)(lastz
>>8);
741 void checkValidity (ref VoxelData vox
) {
743 foreach (uint y
; 0..ysize
) {
744 foreach (uint x
; 0..xsize
) {
745 for (uint dofs
= vox
.getDOfs(x
, y
); dofs
; dofs
= vox
.data
[dofs
].nextz
) {
746 if (!vox
.data
[dofs
].cull
) continue;
747 const uint vd
= queryVox(x
, y
, vox
.data
[dofs
].z
);
748 if (vd
!= vox
.data
[dofs
].rgbcull()) assert(0);
753 foreach (uint y
; 0..ysize
) {
754 foreach (uint x
; 0..xsize
) {
755 foreach (uint z
; 0..zsize
) {
756 const uint vd
= vox
.query(x
, y
, z
);
757 if (vd
!= queryVox(x
, y
, z
)) assert(0);
765 void checkAccessTimes (ref VoxelData vox
) {
767 auto tm
= iv
.timer
.Timer(true);
768 foreach (uint count
; 0..3) {
769 foreach (uint y
; 0..ysize
) {
770 foreach (uint x
; 0..xsize
) {
771 foreach (uint z
; 0..zsize
) {
772 vvn
+= vox
.query(x
, y
, z
);
778 conwriteln("linked list data query time: ", tm
.toString(), " (", vvn
, ")");
782 foreach (uint count
; 0..3) {
783 foreach (uint y
; 0..ysize
) {
784 foreach (uint x
; 0..xsize
) {
785 foreach (uint z
; 0..zsize
) {
786 vvn
+= queryVox(x
, y
, z
);
792 conwriteln("sorted array data query time: ", tm
.toString(), " (", vvn
, ")");
797 delete data
; data
= null;
798 delete xyofs
; xyofs
= null;
799 xsize
= ysize
= zsize
= 0;
804 void createFrom (ref VoxelData vox
) {
806 //vox.removeEmptyVoxels(); // just in case
810 xyofs
.length
= xsize
*ysize
;
814 foreach (uint y
; 0..ysize
) {
815 foreach (uint x
; 0..xsize
) {
816 const uint dofs
= createSlab(vox
, vox
.getDOfs(x
, y
));
817 xyofs
[cast(uint)y
*xsize
+cast(uint)x
] = dofs
;
820 conwriteln("*** created compressed voxel data; ", data
.length
, " bytes for ",
821 xsize
, "x", ysize
, "x", zsize
);
826 uint queryVox (int x
, int y
, int z
) nothrow @trusted @nogc {
827 //pragma(inline, true);
828 if (x
< 0 || y
< 0 || z
< 0) return 0;
829 if (cast(uint)x
>= xsize ||
cast(uint)y
>= ysize
) return 0;
830 uint dofs
= xyofs
.ptr
[cast(uint)y
*xsize
+cast(uint)x
];
832 const(ushort) *dptr
= cast(const(ushort)*)(data
.ptr
+dofs
);
833 //conwriteln("z=", z, "; zlo=", dptr[0], "; zhi=", dptr[1], "; runcount=", dptr[2]);
834 if (cast(ushort)z
< *dptr
++) return 0;
835 if (cast(ushort)z
> *dptr
++) return 0;
836 uint runcount
= *dptr
++;
838 // there is no reason to perform binary search here
839 while (cast(ushort)z
> *dptr
) dptr
+= 4;
840 //conwriteln(" rz0=", dptr[0], "; rz1=", dptr[1], "; rofs=", dptr[2]);
842 const(uint) *dv
= cast(const(uint)*)((cast(const(ubyte)*)dptr
)+dptr
[2]);
846 const ushort cz
= *dptr
;
847 //conwriteln(" cz=", cz, "; cz1=", dptr[1], "; rofs=", dptr[2]);
849 if (cast(ushort)z
>= dptr
[1]) return 0; // no such voxel
850 const(uint) *dv
= cast(const(uint)*)((cast(const(ubyte)*)dptr
)+dptr
[2]);
854 //{ import core.stdc.stdio : printf; printf("runcount=%u\n", cast(uint)runcount); }
856 // perform binary search
857 uint lo
= 0, hi
= runcount
-1;
859 uint mid
= (lo
+hi
)>>1;
860 const(ushort) *dp
= dptr
+(mid
<<2);
861 if (cast(ushort)z
>= dp
[0] && cast(ushort)z
< dp
[1]) {
862 const(uint) *dv
= cast(const(uint)*)((cast(const(ubyte)*)dp
)+dp
[2]);
865 if (cast(ushort)z
< dp
[0]) {
866 if (mid
== lo
) break;
869 if (mid
== hi
) { lo
= hi
; break; }
873 const(ushort) *dp
= dptr
+(lo
<<2);
874 //{ import core.stdc.stdio : printf; printf("000: z=%u; lo=%u; cz0=%u; cz1=%u\n", cast(uint)z, cast(uint)lo, cast(uint)dp[0], cast(uint)dp[1]); }
875 while (cast(ushort)z
>= dp
[1]) dp
+= 4;
876 //{ import core.stdc.stdio : printf; printf("001: lo=%u; cz0=%u; cz1=%u\n", cast(uint)z, cast(uint)lo, cast(uint)dp[0], cast(uint)dp[1]); }
877 if (cast(ushort)z
< dp
[0]) return 0;
878 const(uint) *dv
= cast(const(uint)*)((cast(const(ubyte)*)dp
)+dp
[2]);