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 auto tm
= iv
.timer
.Timer(true);
500 xyz
= stack
[--stackpos
];
501 for (uint dd = 0; dd < 6; ++dd) {
502 const int nx
= cast(int)xyz
.x
+cullofs
[dd][0];
503 const int ny
= cast(int)xyz
.y
+cullofs
[dd][1];
504 const int nz
= cast(int)xyz
.z
+cullofs
[dd][2];
505 if (bmp
.setPixel(nx
, ny
, nz
)) continue;
506 if (queryCull(nx
-1, ny
-1, nz
-1)) continue;
507 if (stackpos
== cast(uint)stack
.length
) {
508 stack
.length
+= 32768;
509 stack
.assumeSafeAppend
;
511 stack
[stackpos
++] = VoxXYZ16(cast(ushort)nx
, cast(ushort)ny
, cast(ushort)nz
);
515 version(voxdata_debug
) conwriteln("*** flooded in ", tm
.toString(), " ", stack
.length
, " stack items used.");
517 // unmark contour voxels
518 // this is required for proper face removing
519 foreach (int y
; 0..ysize
) {
520 foreach (int x
; 0..xsize
) {
521 for (uint dofs
= getDOfs(x
, y
); dofs
; dofs
= data
[dofs
].nextz
) {
522 if (!data
[dofs
].cull
) continue;
523 const int z
= cast(int)data
[dofs
].z
;
524 bmp
.resetPixel(x
+1, y
+1, z
+1);
531 foreach (int y
; 0..ysize
) {
532 foreach (int x
; 0..xsize
) {
533 for (uint dofs
= getDOfs(x
, y
); dofs
; dofs
= data
[dofs
].nextz
) {
534 immutable ubyte omask
= data
[dofs
].cull
;
535 if (!omask
) continue;
536 data
[dofs
].cull
= 0x3f;
538 const int z
= cast(int)data
[dofs
].z
;
539 foreach (uint cidx
; 0..6) {
540 immutable ubyte cmask
= cullmask(cidx
);
541 if (!(data
[dofs
].cull
&cmask
)) continue;
542 const int nx
= x
+cullofs
[cidx
][0];
543 const int ny
= y
+cullofs
[cidx
][1];
544 const int nz
= z
+cullofs
[cidx
][2];
545 if (bmp
.getPixel(nx
+1, ny
+1, nz
+1)) continue;
546 // reset this cull bit
547 data
[dofs
].cull ^
= cmask
;
549 changed
+= (omask
!= data
[dofs
].cull
);
556 void optimise (bool doHollowFill
) @trusted {
557 version(vox_check_invariants
) checkInvariants();
558 version(voxdata_debug
) conwriteln("optimising mesh with ", voxpixtotal
, " individual voxels...");
560 version(voxdata_debug
) conwriteln("optimising voxel culling...");
561 uint count
= hollowFill();
562 if (count
) conwriteln("fixed ", count
, " voxel", (count
!= 1 ?
"s" : ""));
563 //count = fixFaceVisibility();
564 //if (count) conwriteln("fixed ", count, " voxel", (count != 1 ? "s" : ""));
567 version(voxdata_debug
) conwriteln("optimising voxel culling...");
568 uint count
= fixFaceVisibility();
569 if (count
) conwriteln("fixed ", count
, " voxel", (count
!= 1 ?
"s" : ""));
572 version(vox_check_invariants
) checkInvariants();
573 version(voxdata_debug
) conwriteln("final optimised mesh contains ", voxpixtotal
, " individual voxels...");
578 // ////////////////////////////////////////////////////////////////////////// //
580 voxel data optimised for queries
582 each slab is kept in this format:
585 then run index follows:
588 ushort ofs (from this z)
590 index always ends with zhi+1, zhi+1
591 then (b,g,r,cull) array
593 struct VoxelDataSmall
{
595 uint xsize
= 0, ysize
= 0, zsize
= 0;
596 float cx
= 0.0f, cy
= 0.0f, cz
= 0.0f;
599 // xsize*ysize array, offsets in `data`; 0 means "no data here"
600 // slabs are sorted from bottom to top, and never intersects
604 void save (VFile fl
) {
605 fl
.rawWriteExact("K8VOXDC0");
606 fl
.writeNum
!ushort(cast(ushort)xsize
);
607 fl
.writeNum
!ushort(cast(ushort)ysize
);
608 fl
.writeNum
!ushort(cast(ushort)zsize
);
612 fl
.rawWriteExact(xyofs
[]);
613 fl
.writeNum
!uint(cast(uint)data
.length
);
614 fl
.rawWriteExact(data
[]);
618 void load (VFile fl
) {
620 fl
.rawReadExact(sign
[]);
621 if (sign
[] != "K8VOXDC0") throw new Exception("invalid compressed voxel data");
622 xsize
= fl
.readNum
!ushort;
623 ysize
= fl
.readNum
!ushort;
624 zsize
= fl
.readNum
!ushort;
625 cx
= fl
.readNum
!float;
626 cy
= fl
.readNum
!float;
627 cz
= fl
.readNum
!float;
629 xyofs
= new uint[xsize
*ysize
];
630 fl
.rawReadExact(xyofs
[]);
631 const uint dsz
= fl
.readNum
!uint;
633 data
= new ubyte[dsz
];
634 fl
.rawReadExact(data
[]);
638 void appendByte (ubyte v
) @trusted {
639 pragma(inline
, true);
640 data
.assumeSafeAppend
;
644 void appendShort (ushort v
) @trusted {
645 pragma(inline
, true);
646 data
.assumeSafeAppend
;
647 data
~= cast(ubyte)v
;
648 data
.assumeSafeAppend
;
649 data
~= cast(ubyte)(v
>>8);
653 private uint createSlab (ref VoxelData vox
, uint dofs0
) {
654 while (dofs0
&& !vox
.data
[dofs0
].cull
) dofs0
= vox
.data
[dofs0
].nextz
;
655 if (!dofs0
) return 0;
656 // calculate zlo and zhi, and count runs
658 ushort z0
= vox
.data
[dofs0
].z
;
660 ushort nxz
= cast(ushort)(z0
-1);
661 for (uint dofs
= dofs0
; dofs
; dofs
= vox
.data
[dofs
].nextz
) {
662 if (!vox
.data
[dofs
].cull
) continue;
663 z1
= vox
.data
[dofs
].z
;
664 if (z1
!= nxz
) ++runcount
;
665 nxz
= cast(ushort)(z1
+1);
668 if (data
.length
== 0) appendByte(0); // unused
669 const uint startofs
= cast(uint)data
.length
;
675 appendShort(runcount
);
676 // run index (will be filled later)
677 uint idxofs
= cast(uint)data
.length
;
678 for (uint f
= 0; f
< runcount
; ++f
) {
679 appendShort(0); // z0
680 appendShort(0); // z1
681 appendShort(0); // offset
682 appendShort(0); // reserved
685 appendShort(cast(ushort)(z1
+1));
686 appendShort(cast(ushort)(z1
+1));
687 appendShort(0); // offset
688 appendShort(0); // reserved
689 nxz
= cast(ushort)(z0
-1);
690 ushort lastz
= ushort.max
;
692 for (uint dofs
= dofs0
; dofs
; dofs
= vox
.data
[dofs
].nextz
) {
693 if (!vox
.data
[dofs
].cull
) continue;
694 z1
= vox
.data
[dofs
].z
;
698 if (lastz
!= ushort.max
) {
699 data
[idxofs
-6] = cast(ubyte)lastz
;
700 data
[idxofs
-5] = cast(ubyte)(lastz
>>8);
704 const uint rofs
= cast(uint)data
.length
-idxofs
;
705 assert(rofs
<= 0xffffU
);
707 data
[idxofs
++] = cast(ubyte)z1
;
708 data
[idxofs
++] = cast(ubyte)(z1
>>8);
712 data
[idxofs
++] = cast(ubyte)rofs
;
713 data
[idxofs
++] = cast(ubyte)(rofs
>>8);
717 lastz
= nxz
= cast(ushort)(z1
+1);
719 appendByte(vox
.data
[dofs
].b
);
720 appendByte(vox
.data
[dofs
].g
);
721 appendByte(vox
.data
[dofs
].r
);
722 appendByte(vox
.data
[dofs
].cull
);
725 assert(lastz
!= ushort.max
);
726 data
[idxofs
-6] = cast(ubyte)lastz
;
727 data
[idxofs
-5] = cast(ubyte)(lastz
>>8);
732 void checkValidity (ref VoxelData vox
) {
734 foreach (uint y
; 0..ysize
) {
735 foreach (uint x
; 0..xsize
) {
736 for (uint dofs
= vox
.getDOfs(x
, y
); dofs
; dofs
= vox
.data
[dofs
].nextz
) {
737 if (!vox
.data
[dofs
].cull
) continue;
738 const uint vd
= queryVox(x
, y
, vox
.data
[dofs
].z
);
739 if (vd
!= vox
.data
[dofs
].rgbcull()) assert(0);
744 foreach (uint y
; 0..ysize
) {
745 foreach (uint x
; 0..xsize
) {
746 foreach (uint z
; 0..zsize
) {
747 const uint vd
= vox
.query(x
, y
, z
);
748 if (vd
!= queryVox(x
, y
, z
)) assert(0);
756 void checkAccessTimes (ref VoxelData vox
) {
758 auto tm
= iv
.timer
.Timer(true);
759 foreach (uint count
; 0..3) {
760 foreach (uint y
; 0..ysize
) {
761 foreach (uint x
; 0..xsize
) {
762 foreach (uint z
; 0..zsize
) {
763 vvn
+= vox
.query(x
, y
, z
);
769 conwriteln("linked list data query time: ", tm
.toString(), " (", vvn
, ")");
773 foreach (uint count
; 0..3) {
774 foreach (uint y
; 0..ysize
) {
775 foreach (uint x
; 0..xsize
) {
776 foreach (uint z
; 0..zsize
) {
777 vvn
+= queryVox(x
, y
, z
);
783 conwriteln("sorted array data query time: ", tm
.toString(), " (", vvn
, ")");
788 delete data
; data
= null;
789 delete xyofs
; xyofs
= null;
790 xsize
= ysize
= zsize
= 0;
795 void createFrom (ref VoxelData vox
) {
797 //vox.removeEmptyVoxels(); // just in case
801 xyofs
.length
= xsize
*ysize
;
805 foreach (uint y
; 0..ysize
) {
806 foreach (uint x
; 0..xsize
) {
807 const uint dofs
= createSlab(vox
, vox
.getDOfs(x
, y
));
808 xyofs
[cast(uint)y
*xsize
+cast(uint)x
] = dofs
;
811 conwriteln("*** created compressed voxel data; ", data
.length
, " bytes for ",
812 xsize
, "x", ysize
, "x", zsize
);
817 uint queryVox (int x
, int y
, int z
) nothrow @trusted @nogc {
818 //pragma(inline, true);
819 if (x
< 0 || y
< 0 || z
< 0) return 0;
820 if (cast(uint)x
>= xsize ||
cast(uint)y
>= ysize
) return 0;
821 uint dofs
= xyofs
.ptr
[cast(uint)y
*xsize
+cast(uint)x
];
823 const(ushort) *dptr
= cast(const(ushort)*)(data
.ptr
+dofs
);
824 //conwriteln("z=", z, "; zlo=", dptr[0], "; zhi=", dptr[1], "; runcount=", dptr[2]);
825 if (cast(ushort)z
< *dptr
++) return 0;
826 if (cast(ushort)z
> *dptr
++) return 0;
827 uint runcount
= *dptr
++;
829 // there is no reason to perform binary search here
830 while (cast(ushort)z
> *dptr
) dptr
+= 4;
831 //conwriteln(" rz0=", dptr[0], "; rz1=", dptr[1], "; rofs=", dptr[2]);
833 const(uint) *dv
= cast(const(uint)*)((cast(const(ubyte)*)dptr
)+dptr
[2]);
837 const ushort cz
= *dptr
;
838 //conwriteln(" cz=", cz, "; cz1=", dptr[1], "; rofs=", dptr[2]);
840 if (cast(ushort)z
>= dptr
[1]) return 0; // no such voxel
841 const(uint) *dv
= cast(const(uint)*)((cast(const(ubyte)*)dptr
)+dptr
[2]);
845 //{ import core.stdc.stdio : printf; printf("runcount=%u\n", cast(uint)runcount); }
847 // perform binary search
848 uint lo
= 0, hi
= runcount
-1;
850 uint mid
= (lo
+hi
)>>1;
851 const(ushort) *dp
= dptr
+(mid
<<2);
852 if (cast(ushort)z
>= dp
[0] && cast(ushort)z
< dp
[1]) {
853 const(uint) *dv
= cast(const(uint)*)((cast(const(ubyte)*)dp
)+dp
[2]);
856 if (cast(ushort)z
< dp
[0]) {
857 if (mid
== lo
) break;
860 if (mid
== hi
) { lo
= hi
; break; }
864 const(ushort) *dp
= dptr
+(lo
<<2);
865 //{ 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]); }
866 while (cast(ushort)z
>= dp
[1]) dp
+= 4;
867 //{ 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]); }
868 if (cast(ushort)z
< dp
[0]) return 0;
869 const(uint) *dv
= cast(const(uint)*)((cast(const(ubyte)*)dp
)+dp
[2]);