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?
49 this thing is used to create a quad mesh from a voxel data.
50 it contains several conversion methods, from the most simple one
51 "one quad for each visible voxel face", to the most complex one,
52 that goes through each voxel plane, and joins individual voxel
53 faces into a bigger quads (i.e. quads covering several faces at once).
55 this doesn't directly calculates texture coords, tho: it is using an
56 atlas, and records rect coords for each quad. real texture coords are
57 calculated in GL mesh builder instead.
71 Cull_Right
= 0x01, // x axis
72 Cull_Left
= 0x02, // x axis
73 Cull_Near
= 0x04, // y axis
74 Cull_Far
= 0x08, // y axis
75 Cull_Top
= 0x10, // z axis
76 Cull_Bottom
= 0x20, // z axis
78 Cull_XAxisMask
= (Cull_Right|Cull_Left
),
79 Cull_YAxisMask
= (Cull_Near|Cull_Far
),
80 Cull_ZAxisMask
= (Cull_Top|Cull_Bottom
),
89 // bitmasks, `DMV_n` can be used to check for `0` or `1`
102 // voxel center point
107 static immutable ubyte[4][6] quadFaces
= [
108 // right (&0x01) (right)
115 // left (&0x02) (left)
122 // top (&0x04) (near)
129 // bottom (&0x08) (far)
136 // back (&0x10) (top)
143 // front (&0x20) (bottom)
152 static immutable float[3][6] quadNormals
= [
153 // right (&0x01) (right)
155 // left (&0x02) (left)
157 // top (&0x04) (near)
159 // bottom (&0x08) (far)
161 // back (&0x10) (top)
163 // front (&0x20) (bottom)
167 private void quadCalcNormal (ref VoxQuad vq
) {
168 if (vox_allow_normals
) {
171 case 0x01: qidx
= 0; break;
172 case 0x02: qidx
= 1; break;
173 case 0x04: qidx
= 2; break;
174 case 0x08: qidx
= 3; break;
175 case 0x10: qidx
= 4; break;
176 case 0x20: qidx
= 5; break;
179 vq
.normal
.x
= vq
.normal
.dx
= quadNormals
[qidx
][0];
180 vq
.normal
.y
= vq
.normal
.dy
= quadNormals
[qidx
][1];
181 vq
.normal
.z
= vq
.normal
.dz
= quadNormals
[qidx
][2];
183 vq
.normal
.x
= vq
.normal
.dx
= 0.0f;
184 vq
.normal
.y
= vq
.normal
.dy
= 0.0f;
185 vq
.normal
.z
= vq
.normal
.dz
= 1.0f;
189 private void setColors (ref VoxQuad vq
, const(uint)[] clrs
, uint wdt
, uint hgt
) {
190 if (catlas
.findRect(clrs
, wdt
, hgt
, &vq
.cidx
, &vq
.wh
)) {
192 conwriteln(" ...reused rect: (", catlas.getTexX(vq.cidx, vq.rc), ",",
193 catlas.getTexY(vq.cidx, vq.rc), ")-(", wdt, "x", hgt, ")");
197 vq
.cidx
= catlas
.addNewRect(clrs
, wdt
, hgt
);
198 vq
.wh
= VoxWH16(wdt
, hgt
);
201 private static VoxQuadVertex
genVertex (ubyte type
, const float x
, const float y
, const float z
,
202 const float xlen
, const float ylen
, const float zlen
)
204 VoxQuadVertex vx
= void;
206 vx
.dx
= vx
.dy
= vx
.dz
= 0.0f;
210 if (type
&DMV_X
) { vx
.x
+= xlen
; vx
.dx
= 1.0f; }
211 if (type
&DMV_Y
) { vx
.y
+= ylen
; vx
.dy
= 1.0f; }
212 if (type
&DMV_Z
) { vx
.z
+= zlen
; vx
.dz
= 1.0f; }
216 // dmv: bit 2 means XLong, bit 1 means YLong, bit 0 means ZLong
217 void addSlabFace (ubyte cull
, ubyte dmv
,
218 float x
, float y
, float z
,
219 int len
, const(uint)[] colors
)
222 assert(dmv
== DMV_X || dmv
== DMV_Y || dmv
== DMV_Z
);
223 assert(cull
== 0x01 || cull
== 0x02 || cull
== 0x04 || cull
== 0x08 || cull
== 0x10 || cull
== 0x20);
224 assert(colors
.length
>= len
);
225 colors
= colors
[0..len
];
228 foreach (auto cidx
; 1..colors
.length
) {
229 if (colors
[cidx
] != colors
[0]) {
234 if (allsame
) colors
= colors
[0..1];
237 colors
.length
== 1 ? Point
:
238 (dmv
&DMV_X
) ? XLong
:
239 (dmv
&DMV_Y
) ? YLong
:
241 const float dx
= (dmv
&DMV_X ?
cast(float)len
: 1.0f);
242 const float dy
= (dmv
&DMV_Y ?
cast(float)len
: 1.0f);
243 const float dz
= (dmv
&DMV_Z ?
cast(float)len
: 1.0f);
246 case 0x01: qidx
= 0; break;
247 case 0x02: qidx
= 1; break;
248 case 0x04: qidx
= 2; break;
249 case 0x08: qidx
= 3; break;
250 case 0x10: qidx
= 4; break;
251 case 0x20: qidx
= 5; break;
255 foreach (uint vidx
; 0..4) {
256 vq
.vx
[vidx
] = genVertex(quadFaces
[qidx
][vidx
], x
, y
, z
, dx
, dy
, dz
);
258 setColors(ref vq
, colors
[], cast(uint)colors
.length
, 1);
265 void addCube (ubyte cull
, float x
, float y
, float z
, uint rgb
) {
266 immutable uint[1] carr
= [rgb
];
268 foreach (uint qidx
; 0..6) {
269 const ubyte cmask
= VoxelData
.cullmask(qidx
);
271 addSlabFace(cmask
, DMV_X
/*doesn't matter*/, x
, y
, z
, 1, carr
[]);
276 void addQuad (ubyte cull
,
277 float x
, float y
, float z
,
278 int wdt
, int hgt
, // quad size
279 const(uint)[] colors
)
281 assert(wdt
> 0 && hgt
> 0);
282 assert(cull
== 0x01 || cull
== 0x02 || cull
== 0x04 || cull
== 0x08 || cull
== 0x10 || cull
== 0x20);
283 assert(colors
.length
>= wdt
*hgt
);
284 colors
= colors
[0..wdt
*hgt
];
287 foreach (auto cidx
; 1..colors
.length
) {
288 if (colors
[cidx
] != colors
[0]) {
293 if (allsame
) colors
= colors
[0..1];
295 const int qtype
= Quad
;
298 case 0x01: qidx
= 0; break;
299 case 0x02: qidx
= 1; break;
300 case 0x04: qidx
= 2; break;
301 case 0x08: qidx
= 3; break;
302 case 0x10: qidx
= 4; break;
303 case 0x20: qidx
= 5; break;
308 for (uint vidx
= 0; vidx
< 4; ++vidx
) {
309 const ubyte vtype
= quadFaces
[qidx
][vidx
];
310 VoxQuadVertex vx
= void;
312 vx
.dx
= vx
.dy
= vx
.dz
= 0.0f;
316 if (cull
&Cull_ZAxisMask
) {
317 if (vtype
&DMV_X
) vx
.dx
= cast(float)wdt
;
318 if (vtype
&DMV_Y
) vx
.dy
= cast(float)hgt
;
319 if (vtype
&DMV_Z
) vx
.dz
= 1.0f;
320 } else if (cull
&Cull_XAxisMask
) {
321 if (vtype
&DMV_X
) vx
.dx
= 1.0f;
322 if (vtype
&DMV_Y
) vx
.dy
= cast(float)wdt
;
323 if (vtype
&DMV_Z
) vx
.dz
= cast(float)hgt
;
324 } else if (cull
&Cull_YAxisMask
) {
325 if (vtype
&DMV_X
) vx
.dx
= cast(float)wdt
;
326 if (vtype
&DMV_Y
) vx
.dy
= 1.0f;
327 if (vtype
&DMV_Z
) vx
.dz
= cast(float)hgt
;
337 if (colors
.length
== 1) {
338 setColors(ref vq
, colors
[], 1, 1);
340 setColors(ref vq
, colors
[], wdt
, hgt
);
350 void buildOpt0 (ref VoxelData vox
) {
351 const float px
= vox
.cx
;
352 const float py
= vox
.cy
;
353 const float pz
= vox
.cz
;
354 foreach (int y
; 0..vox
.ysize
) {
355 foreach (int x
; 0..vox
.xsize
) {
356 uint dofs
= vox
.getDOfs(x
, y
);
358 addCube(vox
.data
[dofs
].cull
, x
-px
, y
-py
, vox
.data
[dofs
].z
-pz
, vox
.data
[dofs
].rgb());
359 dofs
= vox
.data
[dofs
].nextz
;
365 void buildOpt1 (ref VoxelData vox
) {
366 const float px
= vox
.cx
;
367 const float py
= vox
.cy
;
368 const float pz
= vox
.cz
;
370 uint[1024] slab
= void;
372 foreach (int y
; 0..vox
.ysize
) {
373 foreach (int x
; 0..vox
.xsize
) {
374 // try slabs in all 6 directions?
375 uint dofs
= vox
.getDOfs(x
, y
);
378 // long top and bottom quads
380 foreach (uint cidx
; 4..6) {
381 const ubyte cmask
= VoxelData
.cullmask(cidx
);
382 if ((vox
.data
[dofs
].cull
&cmask
) == 0) continue;
383 const int z
= cast(int)vox
.data
[dofs
].z
;
384 slab
[0] = vox
.data
[dofs
].rgb();
385 addSlabFace(cmask
, DMV_X
, x
-px
, y
-py
, z
-pz
, 1, slab
[0..1]);
387 dofs
= vox
.data
[dofs
].nextz
;
390 // build long quads for each side
391 foreach (uint cidx
; 0..4) {
392 const ubyte cmask
= VoxelData
.cullmask(cidx
);
393 dofs
= vox
.getDOfs(x
, y
);
395 while (dofs
&& (vox
.data
[dofs
].cull
&cmask
) == 0) dofs
= vox
.data
[dofs
].nextz
;
397 const int z
= cast(int)vox
.data
[dofs
].z
;
400 while (eofs
&& (vox
.data
[eofs
].cull
&cmask
)) {
401 if (cast(int)vox
.data
[eofs
].z
!= z
+count
) break;
402 vox
.data
[eofs
].cull ^
= cmask
;
403 slab
[count
] = vox
.data
[eofs
].rgb();
404 eofs
= vox
.data
[eofs
].nextz
;
406 if (count
== cast(int)slab
.length
) break;
410 addSlabFace(cmask
, DMV_Z
, x
-px
, y
-py
, z
-pz
, count
, slab
[0..count
]);
417 void buildOpt2 (ref VoxelData vox
) {
418 const float px
= vox
.cx
;
419 const float py
= vox
.cy
;
420 const float pz
= vox
.cz
;
422 uint[1024] slab
= void;
424 foreach (int y
; 0..vox
.ysize
) {
425 foreach (int x
; 0..vox
.xsize
) {
426 // try slabs in all 6 directions?
427 uint dofs
= vox
.getDOfs(x
, y
);
430 // long top and bottom quads
432 foreach (uint cidx
; 4..6) {
433 const ubyte cmask
= VoxelData
.cullmask(cidx
);
434 if ((vox
.data
[dofs
].cull
&cmask
) == 0) continue;
435 const int z
= cast(int)vox
.data
[dofs
].z
;
436 assert(vox
.queryCull(x
, y
, z
) == vox
.data
[dofs
].cull
);
439 while (x
+xcount
< cast(int)vox
.xsize
) {
440 const ubyte vcull
= vox
.queryCull(x
+xcount
, y
, z
);
441 if ((vcull
&cmask
) == 0) break;
446 while (y
+ycount
< cast(int)vox
.ysize
) {
447 const ubyte vcull
= vox
.queryCull(x
, y
+ycount
, z
);
448 if ((vcull
&cmask
) == 0) break;
451 assert(xcount
&& ycount
);
452 // now use the longest one
453 if (xcount
>= ycount
) {
455 while (x
+xcount
< cast(int)vox
.xsize
) {
456 const uint vrgb
= vox
.query(x
+xcount
, y
, z
);
457 if (((vrgb
>>24)&cmask
) == 0) break;
458 slab
[xcount
] = vrgb|
0xff000000U
;
459 vox
.setVoxelCull(x
+xcount
, y
, z
, (vrgb
>>24)^cmask
);
463 addSlabFace(cmask
, DMV_X
, x
-px
, y
-py
, z
-pz
, xcount
, slab
[0..xcount
]);
466 while (y
+ycount
< cast(int)vox
.ysize
) {
467 const uint vrgb
= vox
.query(x
, y
+ycount
, z
);
468 if (((vrgb
>>24)&cmask
) == 0) break;
469 slab
[ycount
] = vrgb|
0xff000000U
;
470 vox
.setVoxelCull(x
, y
+ycount
, z
, (vrgb
>>24)^cmask
);
474 addSlabFace(cmask
, DMV_Y
, x
-px
, y
-py
, z
-pz
, ycount
, slab
[0..ycount
]);
477 dofs
= vox
.data
[dofs
].nextz
;
480 // build long quads for each side
481 foreach (uint cidx
; 0..4) {
482 const ubyte cmask
= VoxelData
.cullmask(cidx
);
483 dofs
= vox
.getDOfs(x
, y
);
485 while (dofs
&& (vox
.data
[dofs
].cull
&cmask
) == 0) dofs
= vox
.data
[dofs
].nextz
;
487 const int z
= cast(int)vox
.data
[dofs
].z
;
490 while (eofs
&& (vox
.data
[eofs
].cull
&cmask
)) {
491 if (cast(int)vox
.data
[eofs
].z
!= z
+count
) break;
492 vox
.data
[eofs
].cull ^
= cmask
;
493 slab
[count
] = vox
.data
[eofs
].rgb();
494 eofs
= vox
.data
[eofs
].nextz
;
496 if (count
== cast(int)slab
.length
) break;
500 addSlabFace(cmask
, DMV_Z
, x
-px
, y
-py
, z
-pz
, count
, slab
[0..count
]);
507 void buildOpt3 (ref VoxelData vox
) {
508 const float px
= vox
.cx
;
509 const float py
= vox
.cy
;
510 const float pz
= vox
.cz
;
512 // try slabs in all 6 directions?
513 uint[1024] slab
= void;
515 immutable ubyte[2][3] dmove
= [
516 [DMV_Y
, DMV_Z
], // left, right
517 [DMV_X
, DMV_Z
], // near, far
518 [DMV_X
, DMV_Y
], // top, bottom
521 static int getDX (in ubyte dmv
) pure nothrow @safe @nogc { pragma(inline
, true); return !!(dmv
&DMV_X
); }
522 static int getDY (in ubyte dmv
) pure nothrow @safe @nogc { pragma(inline
, true); return !!(dmv
&DMV_Y
); }
523 static int getDZ (in ubyte dmv
) pure nothrow @safe @nogc { pragma(inline
, true); return !!(dmv
&DMV_Z
); }
525 static void incXYZ (in ubyte dmv
, ref int sx
, ref int sy
, ref int sz
) nothrow @safe @nogc {
526 pragma(inline
, true);
532 foreach (int y
; 0..vox
.ysize
) {
533 foreach (int x
; 0..vox
.xsize
) {
534 for (uint dofs
= vox
.getDOfs(x
, y
); dofs
; dofs
= vox
.data
[dofs
].nextz
) {
535 while (vox
.data
[dofs
].cull
) {
539 const int z
= cast(int)vox
.data
[dofs
].z
;
541 foreach (uint cidx
; 0..6) {
542 const ubyte cmask
= VoxelData
.cullmask(cidx
);
543 if ((vox
.data
[dofs
].cull
&cmask
) == 0) continue;
545 foreach (uint ndir
; 0..2) {
546 const ubyte dmv
= dmove
[cidx
>>1][ndir
];
548 int sx
= x
, sy
= y
, sz
= z
;
549 incXYZ(dmv
, ref sx
, ref sy
, ref sz
);
551 const ubyte vxc
= vox
.queryCull(sx
, sy
, sz
);
552 if ((vxc
&cmask
) == 0) break;
554 incXYZ(dmv
, ref sx
, ref sy
, ref sz
);
565 assert(clrdmv
== DMV_X || clrdmv
== DMV_Y || clrdmv
== DMV_Z
);
566 int sx
= x
, sy
= y
, sz
= z
;
567 for (uint f
= 0; f
< count
; ++f
) {
568 VoxPix
*vp
= vox
.queryVP(sx
, sy
, sz
);
570 assert(vp
.cull
&clrmask
);
572 incXYZ(clrdmv
, ref sx
, ref sy
, ref sz
);
574 addSlabFace(clrmask
, clrdmv
, x
-px
, y
-py
, z
-pz
, count
, slab
[0..count
]);
583 // 128 chars should be enough for everyone
584 static char[] milliToBuffer (uint msecs
, char[] dest
) nothrow @trusted @nogc {
585 import core
.stdc
.stdio
: snprintf
;
586 char[128] buf
= void;
587 //immutable uint micro = 0; msecs /= 1000;
588 immutable uint milli
= cast(uint)(msecs
%1000);
590 immutable uint seconds
= cast(uint)(msecs
%60);
592 immutable uint minutes
= cast(uint)(msecs
%60);
594 immutable uint hours
= cast(uint)msecs
;
596 if (hours
) len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%u:%02u:%02u.%03u", hours
, minutes
, seconds
, milli
);
597 else if (minutes
) len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%u:%02u.%03u", minutes
, seconds
, milli
);
598 else if (seconds
) len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%u.%03u", seconds
, milli
);
599 else if (milli
>= 10) len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%ums", milli
);
600 //else if (micro != 0) len = cast(uint)snprintf(buf.ptr, buf.length, "%ums:%umcs", milli, micro);
601 else len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%ums", milli
);
602 if (len
> dest
.length
) len
= cast(uint)dest
.length
;
603 dest
.ptr
[0..len
] = buf
.ptr
[0..len
];
604 return dest
.ptr
[0..len
];
607 // this tries to create big quads
608 void buildOpt4 (ref VoxelData vox
) {
609 const float px
= vox
.cx
;
610 const float py
= vox
.cy
;
611 const float pz
= vox
.cz
;
613 // try slabs in all 6 directions?
615 scope(exit
) { delete slab
; slab
= null; }
619 scope(exit
) bmp3d
.clear();
620 if (vox_optimiser_use_3dbmp
) vox
.create3DBitmap(ref bmp3d
);
622 VoxelDataSmall vxopt
;
623 scope(exit
) vxopt
.clear();
624 if (vox_optimiser_use_cvox
) vxopt
.createFrom(ref vox
);
626 const ulong tstart
= clockMilli();
627 ulong tlastreport
= 0;
628 auto tm
= iv
.timer
.Timer(true);
630 char[129] tbuf
= void;
631 char[129] tbuf2
= void;
632 bool wasPrint
= false;
635 for (uint cidx
= 0; cidx
< 6; ++cidx
) {
636 const ubyte cmask
= VoxelData
.cullmask(cidx
);
638 uint vwdt
, vhgt
, vlen
;
639 if (cmask
&Cull_ZAxisMask
) {
643 } else if (cmask
&Cull_XAxisMask
) {
652 bmp2d
.setSize(vwdt
, vhgt
);
654 for (uint vcrd
= 0; vcrd
< vlen
; ++vcrd
) {
655 //bmp2d.clearBmp(); // no need to, it is guaranteed
656 assert(bmp2d
.dotCount
== 0);
657 for (uint vdy
= 0; vdy
< vhgt
; ++vdy
) {
658 for (uint vdx
= 0; vdx
< vwdt
; ++vdx
) {
660 if (cmask
&Cull_ZAxisMask
) { vx
= vdx
; vy
= vdy
; vz
= vcrd
; }
661 else if (cmask
&Cull_XAxisMask
) { vx
= vcrd
; vy
= vdx
; vz
= vdy
; }
662 else { vx
= vdx
; vy
= vcrd
; vz
= vdy
; }
663 //conwriteln("*vcrd=", vcrd, "; vdx=", vdx, "; vdy=", vdy);
664 if (vox_optimiser_use_3dbmp
&& !bmp3d
.getPixel(vx
, vy
, vz
)) continue;
665 if (vox_optimiser_use_cvox
) {
666 const uint vd
= vxopt
.queryVox(vx
, vy
, vz
);
667 if (((vd
>>24)&cmask
) == 0) continue;
668 bmp2d
.setPixel(vdx
, vdy
, vd|
0xff000000U
);
670 auto vp
= vox
.queryVP(vx
, vy
, vz
);
671 if (!vp ||
((*vp
).cull
&cmask
) == 0) continue;
672 bmp2d
.setPixel(vdx
, vdy
, (*vp
).rgb());
673 if (((*vp
).cull ^
= cmask
) == 0) {
674 vox
.removeVoxel(vx
, vy
, vz
);
675 if (vox_optimiser_use_3dbmp
) bmp3d
.resetPixel(vx
, vy
, vz
);
680 //conwriteln(":: cidx=", cidx, "; vcrd=", vcrd, "; dotCount=", bmp2d.dotCount);
681 if (bmp2d
.dotCount
== 0) continue;
682 // ok, we have some dots, go create quads
684 while (bmp2d
.doOne(&x0
, &y0
, &x1
, &y1
)) {
685 const uint cwdt
= (x1
-x0
)+1;
686 const uint chgt
= (y1
-y0
)+1;
687 if (slab
.length
< cwdt
*chgt
) slab
.length
= ((cwdt
*chgt
)|
0xff)+1;
690 for (int dy
= y0
; dy
<= y1
; ++dy
) {
691 for (int dx
= x0
; dx
<= x1
; ++dx
) {
692 *dp
++ = bmp2d
.resetPixel(dx
, dy
);
696 if (cmask
&Cull_ZAxisMask
) { fx
= x0
; fy
= y0
; fz
= vcrd
; }
697 else if (cmask
&Cull_XAxisMask
) { fx
= vcrd
; fy
= x0
; fz
= y0
; }
698 else { fx
= x0
; fy
= vcrd
; fz
= y0
; }
699 addQuad(cmask
, fx
-px
, fy
-py
, fz
-pz
, cwdt
, chgt
, slab
[0..cwdt
*chgt
]);
702 const ulong ctt
= clockMilli();
703 if (ctt
-tlastreport
>= 1000) {
705 const uint curr
= vlen
*cidx
+vcrd
+1U;
706 const uint total
= vlen
*6U;
707 const uint stt
= cast(uint)(ctt
-tstart
);
708 const uint ett
= cast(uint)(cast(ulong)stt
*total
/curr
);
711 char[] bf
= milliToBuffer(stt
, tbuf
[0..$-1]);
712 char[] bf2
= milliToBuffer(ett
, tbuf2
[0..$-1]);
713 import core
.stdc
.stdio
: fprintf
, stdout
, fflush
;
714 fprintf(stdout
, " %3u%% %s (ETA: %s)\x1b[K\r", 100U*curr
/total
, bf
.ptr
, bf2
.ptr
);
723 import core
.stdc
.stdio
: fprintf
, stdout
, fflush
;
724 fprintf(stdout
, "\r\x1b[K"); fflush(stdout
);
726 conwriteln("*** ultra conversion done in ", tm
.toString());
730 void createFrom (ref VoxelData vox
) {
731 assert(vox
.xsize
&& vox
.ysize
&& vox
.zsize
);
733 conwriteln("building slabs...");
734 auto tm
= iv
.timer
.Timer(true);
735 switch (vox_optimisation
) {
736 case 0: buildOpt0(vox
); break;
737 case 1: buildOpt1(vox
); break;
738 case 2: buildOpt2(vox
); break;
739 case 3: buildOpt3(vox
); break;
740 case 4: default: buildOpt4(vox
); break;
743 conwriteln("basic conversion: ", quads
.length
, " quads (", quads
.length
*2, " tris).");
744 conwriteln("converted in ", tm
.toString(), "; optlevel is ", vox_optimisation
, "/", kvx_max_optim_level
);
751 delete quads
; quads
= null;
756 bool isEmpty () const pure { pragma(inline
, true); return (quads
.length
== 0); }