4 import iv
.glbinds
.utils
;
11 static import iv
.timer
;
20 // ////////////////////////////////////////////////////////////////////////// //
21 public enum kvx_max_optim_level
= 4;
22 public __gshared
int vox_optimisation
= kvx_max_optim_level
-1;
23 public __gshared
bool vox_allow_normals
= true;
24 // this helps a little, so why not
25 public __gshared
bool vox_optimiser_use_3dbmp
= true;
26 public __gshared
bool vox_optimiser_use_cvox
= true;
29 // ////////////////////////////////////////////////////////////////////////// //
30 static align(1) struct VoxQuadVertex
{
34 ubyte qtype
; // Xn_Yn_Zn
37 // quad is always one texel strip
38 static align(1) struct VoxQuad
{
40 uint cidx
; // in colorpack's `citems`
41 VoxWH16 wh
; // width and height
43 int type
= -1/*Invalid*/;
44 ubyte cull
; // for which face this quad was created?
47 if (vox_allow_normals
) {
48 immutable vec3 v1
= vec3(vx
[0].x
, vx
[0].y
, vx
[0].z
);
49 immutable vec3 v2
= vec3(vx
[1].x
, vx
[1].y
, vx
[1].z
);
50 immutable vec3 v3
= vec3(vx
[2].x
, vx
[2].y
, vx
[2].z
);
51 immutable vec3 d1
= v2
-v3
;
52 immutable vec3 d2
= v1
-v3
;
53 vec3 PlaneNormal
= d1
.cross(d2
);
54 if (PlaneNormal
.lengthSquared() == 0.0f) {
55 PlaneNormal
= vec3(0.0f, 0.0f, 1.0f);
57 PlaneNormal
= PlaneNormal
.normalized();
59 normal
.x
= normal
.dx
= PlaneNormal
.x
;
60 normal
.y
= normal
.dy
= PlaneNormal
.y
;
61 normal
.z
= normal
.dz
= PlaneNormal
.z
;
63 normal
.x
= normal
.dx
= 0.0f;
64 normal
.y
= normal
.dy
= 0.0f;
65 normal
.z
= normal
.dz
= 0.0f;
72 this thing is used to create a quad mesh from a voxel data.
73 it contains several conversion methods, from the most simple one
74 "one quad for each visible voxel face", to the most complex one,
75 that goes through each voxel plane, and joins individual voxel
76 faces into a bigger quads (i.e. quads covering several faces at once).
78 this doesn't directly calculates texture coords, tho: it is using an
79 atlas, and records rect coords for each quad. real texture coords are
80 calculated in GL mesh builder instead.
94 Cull_Right
= 0x01, // x axis
95 Cull_Left
= 0x02, // x axis
96 Cull_Near
= 0x04, // y axis
97 Cull_Far
= 0x08, // y axis
98 Cull_Top
= 0x10, // z axis
99 Cull_Bottom
= 0x20, // z axis
101 Cull_XAxisMask
= (Cull_Right|Cull_Left
),
102 Cull_YAxisMask
= (Cull_Near|Cull_Far
),
103 Cull_ZAxisMask
= (Cull_Top|Cull_Bottom
),
112 // bitmasks, `DMV_n` can be used to check for `0` or `1`
125 // voxel center point
130 static immutable ubyte[4][6] quadFaces
= [
131 // right (&0x01) (right)
138 // left (&0x02) (left)
145 // top (&0x04) (near)
152 // bottom (&0x08) (far)
159 // back (&0x10) (top)
166 // front (&0x20) (bottom)
175 private void setColors (ref VoxQuad vq
, const(uint)[] clrs
, uint wdt
, uint hgt
) {
176 if (catlas
.findRect(clrs
, wdt
, hgt
, &vq
.cidx
, &vq
.wh
)) {
178 conwriteln(" ...reused rect: (", catlas.getTexX(vq.cidx, vq.rc), ",",
179 catlas.getTexY(vq.cidx, vq.rc), ")-(", wdt, "x", hgt, ")");
183 vq
.cidx
= catlas
.addNewRect(clrs
, wdt
, hgt
);
184 vq
.wh
= VoxWH16(wdt
, hgt
);
187 private static VoxQuadVertex
genVertex (ubyte type
, const float x
, const float y
, const float z
,
188 const float xlen
, const float ylen
, const float zlen
)
190 VoxQuadVertex vx
= void;
192 vx
.dx
= vx
.dy
= vx
.dz
= 0.0f;
196 if (type
&DMV_X
) { vx
.x
+= xlen
; vx
.dx
= 1.0f; }
197 if (type
&DMV_Y
) { vx
.y
+= ylen
; vx
.dy
= 1.0f; }
198 if (type
&DMV_Z
) { vx
.z
+= zlen
; vx
.dz
= 1.0f; }
202 // dmv: bit 2 means XLong, bit 1 means YLong, bit 0 means ZLong
203 void addSlabFace (ubyte cull
, ubyte dmv
,
204 float x
, float y
, float z
,
205 int len
, const(uint)[] colors
)
208 assert(dmv
== DMV_X || dmv
== DMV_Y || dmv
== DMV_Z
);
209 assert(cull
== 0x01 || cull
== 0x02 || cull
== 0x04 || cull
== 0x08 || cull
== 0x10 || cull
== 0x20);
210 assert(colors
.length
>= len
);
211 colors
= colors
[0..len
];
214 foreach (auto cidx
; 1..colors
.length
) {
215 if (colors
[cidx
] != colors
[0]) {
220 if (allsame
) colors
= colors
[0..1];
223 colors
.length
== 1 ? Point
:
224 (dmv
&DMV_X
) ? XLong
:
225 (dmv
&DMV_Y
) ? YLong
:
227 const float dx
= (dmv
&DMV_X ?
cast(float)len
: 1.0f);
228 const float dy
= (dmv
&DMV_Y ?
cast(float)len
: 1.0f);
229 const float dz
= (dmv
&DMV_Z ?
cast(float)len
: 1.0f);
232 case 0x01: qidx
= 0; break;
233 case 0x02: qidx
= 1; break;
234 case 0x04: qidx
= 2; break;
235 case 0x08: qidx
= 3; break;
236 case 0x10: qidx
= 4; break;
237 case 0x20: qidx
= 5; break;
241 foreach (uint vidx
; 0..4) {
242 vq
.vx
[vidx
] = genVertex(quadFaces
[qidx
][vidx
], x
, y
, z
, dx
, dy
, dz
);
244 setColors(ref vq
, colors
[], cast(uint)colors
.length
, 1);
250 void addCube (ubyte cull
, float x
, float y
, float z
, uint rgb
) {
251 immutable uint[1] carr
= [rgb
];
253 foreach (uint qidx
; 0..6) {
254 const ubyte cmask
= VoxelData
.cullmask(qidx
);
256 addSlabFace(cmask
, DMV_X
/*doesn't matter*/, x
, y
, z
, 1, carr
[]);
261 void addQuad (ubyte cull
,
262 float x
, float y
, float z
,
263 int wdt
, int hgt
, // quad size
264 const(uint)[] colors
)
266 assert(wdt
> 0 && hgt
> 0);
267 assert(cull
== 0x01 || cull
== 0x02 || cull
== 0x04 || cull
== 0x08 || cull
== 0x10 || cull
== 0x20);
268 assert(colors
.length
>= wdt
*hgt
);
269 colors
= colors
[0..wdt
*hgt
];
272 foreach (auto cidx
; 1..colors
.length
) {
273 if (colors
[cidx
] != colors
[0]) {
278 if (allsame
) colors
= colors
[0..1];
280 const int qtype
= Quad
;
283 case 0x01: qidx
= 0; break;
284 case 0x02: qidx
= 1; break;
285 case 0x04: qidx
= 2; break;
286 case 0x08: qidx
= 3; break;
287 case 0x10: qidx
= 4; break;
288 case 0x20: qidx
= 5; break;
293 for (uint vidx
= 0; vidx
< 4; ++vidx
) {
294 const ubyte vtype
= quadFaces
[qidx
][vidx
];
295 VoxQuadVertex vx
= void;
297 vx
.dx
= vx
.dy
= vx
.dz
= 0.0f;
301 if (cull
&Cull_ZAxisMask
) {
302 if (vtype
&DMV_X
) vx
.dx
= cast(float)wdt
;
303 if (vtype
&DMV_Y
) vx
.dy
= cast(float)hgt
;
304 if (vtype
&DMV_Z
) vx
.dz
= 1.0f;
305 } else if (cull
&Cull_XAxisMask
) {
306 if (vtype
&DMV_X
) vx
.dx
= 1.0f;
307 if (vtype
&DMV_Y
) vx
.dy
= cast(float)wdt
;
308 if (vtype
&DMV_Z
) vx
.dz
= cast(float)hgt
;
309 } else if (cull
&Cull_YAxisMask
) {
310 if (vtype
&DMV_X
) vx
.dx
= cast(float)wdt
;
311 if (vtype
&DMV_Y
) vx
.dy
= 1.0f;
312 if (vtype
&DMV_Z
) vx
.dz
= cast(float)hgt
;
322 if (colors
.length
== 1) {
323 setColors(ref vq
, colors
[], 1, 1);
325 setColors(ref vq
, colors
[], wdt
, hgt
);
334 void buildOpt0 (ref VoxelData vox
) {
335 const float px
= vox
.cx
;
336 const float py
= vox
.cy
;
337 const float pz
= vox
.cz
;
338 foreach (int y
; 0..vox
.ysize
) {
339 foreach (int x
; 0..vox
.xsize
) {
340 uint dofs
= vox
.getDOfs(x
, y
);
342 addCube(vox
.data
[dofs
].cull
, x
-px
, y
-py
, vox
.data
[dofs
].z
-pz
, vox
.data
[dofs
].rgb());
343 dofs
= vox
.data
[dofs
].nextz
;
349 void buildOpt1 (ref VoxelData vox
) {
350 const float px
= vox
.cx
;
351 const float py
= vox
.cy
;
352 const float pz
= vox
.cz
;
354 uint[1024] slab
= void;
356 foreach (int y
; 0..vox
.ysize
) {
357 foreach (int x
; 0..vox
.xsize
) {
358 // try slabs in all 6 directions?
359 uint dofs
= vox
.getDOfs(x
, y
);
362 // long top and bottom quads
364 foreach (uint cidx
; 4..6) {
365 const ubyte cmask
= VoxelData
.cullmask(cidx
);
366 if ((vox
.data
[dofs
].cull
&cmask
) == 0) continue;
367 const int z
= cast(int)vox
.data
[dofs
].z
;
368 slab
[0] = vox
.data
[dofs
].rgb();
369 addSlabFace(cmask
, DMV_X
, x
-px
, y
-py
, z
-pz
, 1, slab
[0..1]);
371 dofs
= vox
.data
[dofs
].nextz
;
374 // build long quads for each side
375 foreach (uint cidx
; 0..4) {
376 const ubyte cmask
= VoxelData
.cullmask(cidx
);
377 dofs
= vox
.getDOfs(x
, y
);
379 while (dofs
&& (vox
.data
[dofs
].cull
&cmask
) == 0) dofs
= vox
.data
[dofs
].nextz
;
381 const int z
= cast(int)vox
.data
[dofs
].z
;
384 while (eofs
&& (vox
.data
[eofs
].cull
&cmask
)) {
385 if (cast(int)vox
.data
[eofs
].z
!= z
+count
) break;
386 vox
.data
[eofs
].cull ^
= cmask
;
387 slab
[count
] = vox
.data
[eofs
].rgb();
388 eofs
= vox
.data
[eofs
].nextz
;
390 if (count
== cast(int)slab
.length
) break;
394 addSlabFace(cmask
, DMV_Z
, x
-px
, y
-py
, z
-pz
, count
, slab
[0..count
]);
401 void buildOpt2 (ref VoxelData vox
) {
402 const float px
= vox
.cx
;
403 const float py
= vox
.cy
;
404 const float pz
= vox
.cz
;
406 uint[1024] slab
= void;
408 foreach (int y
; 0..vox
.ysize
) {
409 foreach (int x
; 0..vox
.xsize
) {
410 // try slabs in all 6 directions?
411 uint dofs
= vox
.getDOfs(x
, y
);
414 // long top and bottom quads
416 foreach (uint cidx
; 4..6) {
417 const ubyte cmask
= VoxelData
.cullmask(cidx
);
418 if ((vox
.data
[dofs
].cull
&cmask
) == 0) continue;
419 const int z
= cast(int)vox
.data
[dofs
].z
;
420 assert(vox
.queryCull(x
, y
, z
) == vox
.data
[dofs
].cull
);
423 while (x
+xcount
< cast(int)vox
.xsize
) {
424 const ubyte vcull
= vox
.queryCull(x
+xcount
, y
, z
);
425 if ((vcull
&cmask
) == 0) break;
430 while (y
+ycount
< cast(int)vox
.ysize
) {
431 const ubyte vcull
= vox
.queryCull(x
, y
+ycount
, z
);
432 if ((vcull
&cmask
) == 0) break;
435 assert(xcount
&& ycount
);
436 // now use the longest one
437 if (xcount
>= ycount
) {
439 while (x
+xcount
< cast(int)vox
.xsize
) {
440 const uint vrgb
= vox
.query(x
+xcount
, y
, z
);
441 if (((vrgb
>>24)&cmask
) == 0) break;
442 slab
[xcount
] = vrgb|
0xff000000U
;
443 vox
.setVoxelCull(x
+xcount
, y
, z
, (vrgb
>>24)^cmask
);
447 addSlabFace(cmask
, DMV_X
, x
-px
, y
-py
, z
-pz
, xcount
, slab
[0..xcount
]);
450 while (y
+ycount
< cast(int)vox
.ysize
) {
451 const uint vrgb
= vox
.query(x
, y
+ycount
, z
);
452 if (((vrgb
>>24)&cmask
) == 0) break;
453 slab
[ycount
] = vrgb|
0xff000000U
;
454 vox
.setVoxelCull(x
, y
+ycount
, z
, (vrgb
>>24)^cmask
);
458 addSlabFace(cmask
, DMV_Y
, x
-px
, y
-py
, z
-pz
, ycount
, slab
[0..ycount
]);
461 dofs
= vox
.data
[dofs
].nextz
;
464 // build long quads for each side
465 foreach (uint cidx
; 0..4) {
466 const ubyte cmask
= VoxelData
.cullmask(cidx
);
467 dofs
= vox
.getDOfs(x
, y
);
469 while (dofs
&& (vox
.data
[dofs
].cull
&cmask
) == 0) dofs
= vox
.data
[dofs
].nextz
;
471 const int z
= cast(int)vox
.data
[dofs
].z
;
474 while (eofs
&& (vox
.data
[eofs
].cull
&cmask
)) {
475 if (cast(int)vox
.data
[eofs
].z
!= z
+count
) break;
476 vox
.data
[eofs
].cull ^
= cmask
;
477 slab
[count
] = vox
.data
[eofs
].rgb();
478 eofs
= vox
.data
[eofs
].nextz
;
480 if (count
== cast(int)slab
.length
) break;
484 addSlabFace(cmask
, DMV_Z
, x
-px
, y
-py
, z
-pz
, count
, slab
[0..count
]);
491 void buildOpt3 (ref VoxelData vox
) {
492 const float px
= vox
.cx
;
493 const float py
= vox
.cy
;
494 const float pz
= vox
.cz
;
496 // try slabs in all 6 directions?
497 uint[1024] slab
= void;
499 immutable ubyte[2][3] dmove
= [
500 [DMV_Y
, DMV_Z
], // left, right
501 [DMV_X
, DMV_Z
], // near, far
502 [DMV_X
, DMV_Y
], // top, bottom
505 static int getDX (in ubyte dmv
) pure nothrow @safe @nogc { pragma(inline
, true); return !!(dmv
&DMV_X
); }
506 static int getDY (in ubyte dmv
) pure nothrow @safe @nogc { pragma(inline
, true); return !!(dmv
&DMV_Y
); }
507 static int getDZ (in ubyte dmv
) pure nothrow @safe @nogc { pragma(inline
, true); return !!(dmv
&DMV_Z
); }
509 static void incXYZ (in ubyte dmv
, ref int sx
, ref int sy
, ref int sz
) nothrow @safe @nogc {
510 pragma(inline
, true);
516 foreach (int y
; 0..vox
.ysize
) {
517 foreach (int x
; 0..vox
.xsize
) {
518 for (uint dofs
= vox
.getDOfs(x
, y
); dofs
; dofs
= vox
.data
[dofs
].nextz
) {
519 while (vox
.data
[dofs
].cull
) {
523 const int z
= cast(int)vox
.data
[dofs
].z
;
525 foreach (uint cidx
; 0..6) {
526 const ubyte cmask
= VoxelData
.cullmask(cidx
);
527 if ((vox
.data
[dofs
].cull
&cmask
) == 0) continue;
529 foreach (uint ndir
; 0..2) {
530 const ubyte dmv
= dmove
[cidx
>>1][ndir
];
532 int sx
= x
, sy
= y
, sz
= z
;
533 incXYZ(dmv
, ref sx
, ref sy
, ref sz
);
535 const ubyte vxc
= vox
.queryCull(sx
, sy
, sz
);
536 if ((vxc
&cmask
) == 0) break;
538 incXYZ(dmv
, ref sx
, ref sy
, ref sz
);
549 assert(clrdmv
== DMV_X || clrdmv
== DMV_Y || clrdmv
== DMV_Z
);
550 int sx
= x
, sy
= y
, sz
= z
;
551 for (uint f
= 0; f
< count
; ++f
) {
552 VoxPix
*vp
= vox
.queryVP(sx
, sy
, sz
);
554 assert(vp
.cull
&clrmask
);
556 incXYZ(clrdmv
, ref sx
, ref sy
, ref sz
);
558 addSlabFace(clrmask
, clrdmv
, x
-px
, y
-py
, z
-pz
, count
, slab
[0..count
]);
567 // 128 chars should be enough for everyone
568 static char[] milliToBuffer (uint msecs
, char[] dest
) nothrow @trusted @nogc {
569 import core
.stdc
.stdio
: snprintf
;
570 char[128] buf
= void;
571 //immutable uint micro = 0; msecs /= 1000;
572 immutable uint milli
= cast(uint)(msecs
%1000);
574 immutable uint seconds
= cast(uint)(msecs
%60);
576 immutable uint minutes
= cast(uint)(msecs
%60);
578 immutable uint hours
= cast(uint)msecs
;
580 if (hours
) len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%u:%02u:%02u.%03u", hours
, minutes
, seconds
, milli
);
581 else if (minutes
) len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%u:%02u.%03u", minutes
, seconds
, milli
);
582 else if (seconds
) len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%u.%03u", seconds
, milli
);
583 else if (milli
>= 10) len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%ums", milli
);
584 //else if (micro != 0) len = cast(uint)snprintf(buf.ptr, buf.length, "%ums:%umcs", milli, micro);
585 else len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%ums", milli
);
586 if (len
> dest
.length
) len
= cast(uint)dest
.length
;
587 dest
.ptr
[0..len
] = buf
.ptr
[0..len
];
588 return dest
.ptr
[0..len
];
591 // this tries to create big quads
592 void buildOpt4 (ref VoxelData vox
) {
593 const float px
= vox
.cx
;
594 const float py
= vox
.cy
;
595 const float pz
= vox
.cz
;
597 // try slabs in all 6 directions?
599 scope(exit
) { delete slab
; slab
= null; }
603 scope(exit
) bmp3d
.clear();
604 if (vox_optimiser_use_3dbmp
) vox
.create3DBitmap(ref bmp3d
);
606 VoxelDataSmall vxopt
;
607 scope(exit
) vxopt
.clear();
608 if (vox_optimiser_use_cvox
) vxopt
.createFrom(ref vox
);
610 const ulong tstart
= clockMilli();
611 ulong tlastreport
= 0;
612 auto tm
= iv
.timer
.Timer(true);
614 char[129] tbuf
= void;
615 char[129] tbuf2
= void;
616 bool wasPrint
= false;
619 for (uint cidx
= 0; cidx
< 6; ++cidx
) {
620 const ubyte cmask
= VoxelData
.cullmask(cidx
);
622 uint vwdt
, vhgt
, vlen
;
623 if (cmask
&Cull_ZAxisMask
) {
627 } else if (cmask
&Cull_XAxisMask
) {
636 bmp2d
.setSize(vwdt
, vhgt
);
638 for (uint vcrd
= 0; vcrd
< vlen
; ++vcrd
) {
639 //bmp2d.clearBmp(); // no need to, it is guaranteed
640 assert(bmp2d
.dotCount
== 0);
641 for (uint vdy
= 0; vdy
< vhgt
; ++vdy
) {
642 for (uint vdx
= 0; vdx
< vwdt
; ++vdx
) {
644 if (cmask
&Cull_ZAxisMask
) { vx
= vdx
; vy
= vdy
; vz
= vcrd
; }
645 else if (cmask
&Cull_XAxisMask
) { vx
= vcrd
; vy
= vdx
; vz
= vdy
; }
646 else { vx
= vdx
; vy
= vcrd
; vz
= vdy
; }
647 //conwriteln("*vcrd=", vcrd, "; vdx=", vdx, "; vdy=", vdy);
648 if (vox_optimiser_use_3dbmp
&& !bmp3d
.getPixel(vx
, vy
, vz
)) continue;
649 if (vox_optimiser_use_cvox
) {
650 const uint vd
= vxopt
.queryVox(vx
, vy
, vz
);
651 if (((vd
>>24)&cmask
) == 0) continue;
652 bmp2d
.setPixel(vdx
, vdy
, vd|
0xff000000U
);
654 auto vp
= vox
.queryVP(vx
, vy
, vz
);
655 if (!vp ||
((*vp
).cull
&cmask
) == 0) continue;
656 bmp2d
.setPixel(vdx
, vdy
, (*vp
).rgb());
657 if (((*vp
).cull ^
= cmask
) == 0) {
658 vox
.removeVoxel(vx
, vy
, vz
);
659 if (vox_optimiser_use_3dbmp
) bmp3d
.resetPixel(vx
, vy
, vz
);
664 //conwriteln(":: cidx=", cidx, "; vcrd=", vcrd, "; dotCount=", bmp2d.dotCount);
665 if (bmp2d
.dotCount
== 0) continue;
666 // ok, we have some dots, go create quads
668 while (bmp2d
.doOne(&x0
, &y0
, &x1
, &y1
)) {
669 const uint cwdt
= (x1
-x0
)+1;
670 const uint chgt
= (y1
-y0
)+1;
671 if (slab
.length
< cwdt
*chgt
) slab
.length
= ((cwdt
*chgt
)|
0xff)+1;
674 for (int dy
= y0
; dy
<= y1
; ++dy
) {
675 for (int dx
= x0
; dx
<= x1
; ++dx
) {
676 *dp
++ = bmp2d
.resetPixel(dx
, dy
);
680 if (cmask
&Cull_ZAxisMask
) { fx
= x0
; fy
= y0
; fz
= vcrd
; }
681 else if (cmask
&Cull_XAxisMask
) { fx
= vcrd
; fy
= x0
; fz
= y0
; }
682 else { fx
= x0
; fy
= vcrd
; fz
= y0
; }
683 addQuad(cmask
, fx
-px
, fy
-py
, fz
-pz
, cwdt
, chgt
, slab
[0..cwdt
*chgt
]);
686 const ulong ctt
= clockMilli();
687 if (ctt
-tlastreport
>= 1000) {
689 const uint curr
= vlen
*cidx
+vcrd
+1U;
690 const uint total
= vlen
*6U;
691 const uint stt
= cast(uint)(ctt
-tstart
);
692 const uint ett
= cast(uint)(cast(ulong)stt
*total
/curr
);
695 char[] bf
= milliToBuffer(stt
, tbuf
[0..$-1]);
696 char[] bf2
= milliToBuffer(ett
, tbuf2
[0..$-1]);
697 import core
.stdc
.stdio
: fprintf
, stdout
, fflush
;
698 fprintf(stdout
, " %3u%% %s (ETA: %s)\x1b[K\r", 100U*curr
/total
, bf
.ptr
, bf2
.ptr
);
707 import core
.stdc
.stdio
: fprintf
, stdout
, fflush
;
708 fprintf(stdout
, "\r\x1b[K"); fflush(stdout
);
710 conwriteln("*** ultra conversion done in ", tm
.toString());
714 void createFrom (ref VoxelData vox
) {
715 assert(vox
.xsize
&& vox
.ysize
&& vox
.zsize
);
717 conwriteln("building slabs...");
718 auto tm
= iv
.timer
.Timer(true);
719 switch (vox_optimisation
) {
720 case 0: buildOpt0(vox
); break;
721 case 1: buildOpt1(vox
); break;
722 case 2: buildOpt2(vox
); break;
723 case 3: buildOpt3(vox
); break;
724 case 4: default: buildOpt4(vox
); break;
727 conwriteln("basic conversion: ", quads
.length
, " quads (", quads
.length
*2, " tris).");
728 conwriteln("converted in ", tm
.toString(), "; optlevel is ", vox_optimisation
, "/", kvx_max_optim_level
);
735 delete quads
; quads
= null;
740 bool isEmpty () const pure { pragma(inline
, true); return (quads
.length
== 0); }