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[4][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
) {
169 vq
.normal
.x
= vq
.normal
.dx
= quadNormals
[vq
.cull
][0];
170 vq
.normal
.y
= vq
.normal
.dy
= quadNormals
[vq
.cull
][1];
171 vq
.normal
.z
= vq
.normal
.dz
= quadNormals
[vq
.cull
][2];
173 vq
.normal
.x
= vq
.normal
.dx
= 0.0f;
174 vq
.normal
.y
= vq
.normal
.dy
= 0.0f;
175 vq
.normal
.z
= vq
.normal
.dz
= 1.0f;
179 private void setColors (ref VoxQuad vq
, const(uint)[] clrs
, uint wdt
, uint hgt
) {
180 if (catlas
.findRect(clrs
, wdt
, hgt
, &vq
.cidx
, &vq
.wh
)) {
182 conwriteln(" ...reused rect: (", catlas.getTexX(vq.cidx, vq.rc), ",",
183 catlas.getTexY(vq.cidx, vq.rc), ")-(", wdt, "x", hgt, ")");
187 vq
.cidx
= catlas
.addNewRect(clrs
, wdt
, hgt
);
188 vq
.wh
= VoxWH16(wdt
, hgt
);
191 private static VoxQuadVertex
genVertex (ubyte type
, const float x
, const float y
, const float z
,
192 const float xlen
, const float ylen
, const float zlen
)
194 VoxQuadVertex vx
= void;
196 vx
.dx
= vx
.dy
= vx
.dz
= 0.0f;
200 if (type
&DMV_X
) { vx
.x
+= xlen
; vx
.dx
= 1.0f; }
201 if (type
&DMV_Y
) { vx
.y
+= ylen
; vx
.dy
= 1.0f; }
202 if (type
&DMV_Z
) { vx
.z
+= zlen
; vx
.dz
= 1.0f; }
206 // dmv: bit 2 means XLong, bit 1 means YLong, bit 0 means ZLong
207 void addSlabFace (ubyte cull
, ubyte dmv
,
208 float x
, float y
, float z
,
209 int len
, const(uint)[] colors
)
212 assert(dmv
== DMV_X || dmv
== DMV_Y || dmv
== DMV_Z
);
213 assert(cull
== 0x01 || cull
== 0x02 || cull
== 0x04 || cull
== 0x08 || cull
== 0x10 || cull
== 0x20);
214 assert(colors
.length
>= len
);
215 colors
= colors
[0..len
];
218 foreach (auto cidx
; 1..colors
.length
) {
219 if (colors
[cidx
] != colors
[0]) {
224 if (allsame
) colors
= colors
[0..1];
227 colors
.length
== 1 ? Point
:
228 (dmv
&DMV_X
) ? XLong
:
229 (dmv
&DMV_Y
) ? YLong
:
231 const float dx
= (dmv
&DMV_X ?
cast(float)len
: 1.0f);
232 const float dy
= (dmv
&DMV_Y ?
cast(float)len
: 1.0f);
233 const float dz
= (dmv
&DMV_Z ?
cast(float)len
: 1.0f);
236 case 0x01: qidx
= 0; break;
237 case 0x02: qidx
= 1; break;
238 case 0x04: qidx
= 2; break;
239 case 0x08: qidx
= 3; break;
240 case 0x10: qidx
= 4; break;
241 case 0x20: qidx
= 5; break;
245 foreach (uint vidx
; 0..4) {
246 vq
.vx
[vidx
] = genVertex(quadFaces
[qidx
][vidx
], x
, y
, z
, dx
, dy
, dz
);
248 setColors(ref vq
, colors
[], cast(uint)colors
.length
, 1);
255 void addCube (ubyte cull
, float x
, float y
, float z
, uint rgb
) {
256 immutable uint[1] carr
= [rgb
];
258 foreach (uint qidx
; 0..6) {
259 const ubyte cmask
= VoxelData
.cullmask(qidx
);
261 addSlabFace(cmask
, DMV_X
/*doesn't matter*/, x
, y
, z
, 1, carr
[]);
266 void addQuad (ubyte cull
,
267 float x
, float y
, float z
,
268 int wdt
, int hgt
, // quad size
269 const(uint)[] colors
)
271 assert(wdt
> 0 && hgt
> 0);
272 assert(cull
== 0x01 || cull
== 0x02 || cull
== 0x04 || cull
== 0x08 || cull
== 0x10 || cull
== 0x20);
273 assert(colors
.length
>= wdt
*hgt
);
274 colors
= colors
[0..wdt
*hgt
];
277 foreach (auto cidx
; 1..colors
.length
) {
278 if (colors
[cidx
] != colors
[0]) {
283 if (allsame
) colors
= colors
[0..1];
285 const int qtype
= Quad
;
288 case 0x01: qidx
= 0; break;
289 case 0x02: qidx
= 1; break;
290 case 0x04: qidx
= 2; break;
291 case 0x08: qidx
= 3; break;
292 case 0x10: qidx
= 4; break;
293 case 0x20: qidx
= 5; break;
298 for (uint vidx
= 0; vidx
< 4; ++vidx
) {
299 const ubyte vtype
= quadFaces
[qidx
][vidx
];
300 VoxQuadVertex vx
= void;
302 vx
.dx
= vx
.dy
= vx
.dz
= 0.0f;
306 if (cull
&Cull_ZAxisMask
) {
307 if (vtype
&DMV_X
) vx
.dx
= cast(float)wdt
;
308 if (vtype
&DMV_Y
) vx
.dy
= cast(float)hgt
;
309 if (vtype
&DMV_Z
) vx
.dz
= 1.0f;
310 } else if (cull
&Cull_XAxisMask
) {
311 if (vtype
&DMV_X
) vx
.dx
= 1.0f;
312 if (vtype
&DMV_Y
) vx
.dy
= cast(float)wdt
;
313 if (vtype
&DMV_Z
) vx
.dz
= cast(float)hgt
;
314 } else if (cull
&Cull_YAxisMask
) {
315 if (vtype
&DMV_X
) vx
.dx
= cast(float)wdt
;
316 if (vtype
&DMV_Y
) vx
.dy
= 1.0f;
317 if (vtype
&DMV_Z
) vx
.dz
= cast(float)hgt
;
327 if (colors
.length
== 1) {
328 setColors(ref vq
, colors
[], 1, 1);
330 setColors(ref vq
, colors
[], wdt
, hgt
);
340 void buildOpt0 (ref VoxelData vox
) {
341 const float px
= vox
.cx
;
342 const float py
= vox
.cy
;
343 const float pz
= vox
.cz
;
344 foreach (int y
; 0..vox
.ysize
) {
345 foreach (int x
; 0..vox
.xsize
) {
346 uint dofs
= vox
.getDOfs(x
, y
);
348 addCube(vox
.data
[dofs
].cull
, x
-px
, y
-py
, vox
.data
[dofs
].z
-pz
, vox
.data
[dofs
].rgb());
349 dofs
= vox
.data
[dofs
].nextz
;
355 void buildOpt1 (ref VoxelData vox
) {
356 const float px
= vox
.cx
;
357 const float py
= vox
.cy
;
358 const float pz
= vox
.cz
;
360 uint[1024] slab
= void;
362 foreach (int y
; 0..vox
.ysize
) {
363 foreach (int x
; 0..vox
.xsize
) {
364 // try slabs in all 6 directions?
365 uint dofs
= vox
.getDOfs(x
, y
);
368 // long top and bottom quads
370 foreach (uint cidx
; 4..6) {
371 const ubyte cmask
= VoxelData
.cullmask(cidx
);
372 if ((vox
.data
[dofs
].cull
&cmask
) == 0) continue;
373 const int z
= cast(int)vox
.data
[dofs
].z
;
374 slab
[0] = vox
.data
[dofs
].rgb();
375 addSlabFace(cmask
, DMV_X
, x
-px
, y
-py
, z
-pz
, 1, slab
[0..1]);
377 dofs
= vox
.data
[dofs
].nextz
;
380 // build long quads for each side
381 foreach (uint cidx
; 0..4) {
382 const ubyte cmask
= VoxelData
.cullmask(cidx
);
383 dofs
= vox
.getDOfs(x
, y
);
385 while (dofs
&& (vox
.data
[dofs
].cull
&cmask
) == 0) dofs
= vox
.data
[dofs
].nextz
;
387 const int z
= cast(int)vox
.data
[dofs
].z
;
390 while (eofs
&& (vox
.data
[eofs
].cull
&cmask
)) {
391 if (cast(int)vox
.data
[eofs
].z
!= z
+count
) break;
392 vox
.data
[eofs
].cull ^
= cmask
;
393 slab
[count
] = vox
.data
[eofs
].rgb();
394 eofs
= vox
.data
[eofs
].nextz
;
396 if (count
== cast(int)slab
.length
) break;
400 addSlabFace(cmask
, DMV_Z
, x
-px
, y
-py
, z
-pz
, count
, slab
[0..count
]);
407 void buildOpt2 (ref VoxelData vox
) {
408 const float px
= vox
.cx
;
409 const float py
= vox
.cy
;
410 const float pz
= vox
.cz
;
412 uint[1024] slab
= void;
414 foreach (int y
; 0..vox
.ysize
) {
415 foreach (int x
; 0..vox
.xsize
) {
416 // try slabs in all 6 directions?
417 uint dofs
= vox
.getDOfs(x
, y
);
420 // long top and bottom quads
422 foreach (uint cidx
; 4..6) {
423 const ubyte cmask
= VoxelData
.cullmask(cidx
);
424 if ((vox
.data
[dofs
].cull
&cmask
) == 0) continue;
425 const int z
= cast(int)vox
.data
[dofs
].z
;
426 assert(vox
.queryCull(x
, y
, z
) == vox
.data
[dofs
].cull
);
429 while (x
+xcount
< cast(int)vox
.xsize
) {
430 const ubyte vcull
= vox
.queryCull(x
+xcount
, y
, z
);
431 if ((vcull
&cmask
) == 0) break;
436 while (y
+ycount
< cast(int)vox
.ysize
) {
437 const ubyte vcull
= vox
.queryCull(x
, y
+ycount
, z
);
438 if ((vcull
&cmask
) == 0) break;
441 assert(xcount
&& ycount
);
442 // now use the longest one
443 if (xcount
>= ycount
) {
445 while (x
+xcount
< cast(int)vox
.xsize
) {
446 const uint vrgb
= vox
.query(x
+xcount
, y
, z
);
447 if (((vrgb
>>24)&cmask
) == 0) break;
448 slab
[xcount
] = vrgb|
0xff000000U
;
449 vox
.setVoxelCull(x
+xcount
, y
, z
, (vrgb
>>24)^cmask
);
453 addSlabFace(cmask
, DMV_X
, x
-px
, y
-py
, z
-pz
, xcount
, slab
[0..xcount
]);
456 while (y
+ycount
< cast(int)vox
.ysize
) {
457 const uint vrgb
= vox
.query(x
, y
+ycount
, z
);
458 if (((vrgb
>>24)&cmask
) == 0) break;
459 slab
[ycount
] = vrgb|
0xff000000U
;
460 vox
.setVoxelCull(x
, y
+ycount
, z
, (vrgb
>>24)^cmask
);
464 addSlabFace(cmask
, DMV_Y
, x
-px
, y
-py
, z
-pz
, ycount
, slab
[0..ycount
]);
467 dofs
= vox
.data
[dofs
].nextz
;
470 // build long quads for each side
471 foreach (uint cidx
; 0..4) {
472 const ubyte cmask
= VoxelData
.cullmask(cidx
);
473 dofs
= vox
.getDOfs(x
, y
);
475 while (dofs
&& (vox
.data
[dofs
].cull
&cmask
) == 0) dofs
= vox
.data
[dofs
].nextz
;
477 const int z
= cast(int)vox
.data
[dofs
].z
;
480 while (eofs
&& (vox
.data
[eofs
].cull
&cmask
)) {
481 if (cast(int)vox
.data
[eofs
].z
!= z
+count
) break;
482 vox
.data
[eofs
].cull ^
= cmask
;
483 slab
[count
] = vox
.data
[eofs
].rgb();
484 eofs
= vox
.data
[eofs
].nextz
;
486 if (count
== cast(int)slab
.length
) break;
490 addSlabFace(cmask
, DMV_Z
, x
-px
, y
-py
, z
-pz
, count
, slab
[0..count
]);
497 void buildOpt3 (ref VoxelData vox
) {
498 const float px
= vox
.cx
;
499 const float py
= vox
.cy
;
500 const float pz
= vox
.cz
;
502 // try slabs in all 6 directions?
503 uint[1024] slab
= void;
505 immutable ubyte[2][3] dmove
= [
506 [DMV_Y
, DMV_Z
], // left, right
507 [DMV_X
, DMV_Z
], // near, far
508 [DMV_X
, DMV_Y
], // top, bottom
511 static int getDX (in ubyte dmv
) pure nothrow @safe @nogc { pragma(inline
, true); return !!(dmv
&DMV_X
); }
512 static int getDY (in ubyte dmv
) pure nothrow @safe @nogc { pragma(inline
, true); return !!(dmv
&DMV_Y
); }
513 static int getDZ (in ubyte dmv
) pure nothrow @safe @nogc { pragma(inline
, true); return !!(dmv
&DMV_Z
); }
515 static void incXYZ (in ubyte dmv
, ref int sx
, ref int sy
, ref int sz
) nothrow @safe @nogc {
516 pragma(inline
, true);
522 foreach (int y
; 0..vox
.ysize
) {
523 foreach (int x
; 0..vox
.xsize
) {
524 for (uint dofs
= vox
.getDOfs(x
, y
); dofs
; dofs
= vox
.data
[dofs
].nextz
) {
525 while (vox
.data
[dofs
].cull
) {
529 const int z
= cast(int)vox
.data
[dofs
].z
;
531 foreach (uint cidx
; 0..6) {
532 const ubyte cmask
= VoxelData
.cullmask(cidx
);
533 if ((vox
.data
[dofs
].cull
&cmask
) == 0) continue;
535 foreach (uint ndir
; 0..2) {
536 const ubyte dmv
= dmove
[cidx
>>1][ndir
];
538 int sx
= x
, sy
= y
, sz
= z
;
539 incXYZ(dmv
, ref sx
, ref sy
, ref sz
);
541 const ubyte vxc
= vox
.queryCull(sx
, sy
, sz
);
542 if ((vxc
&cmask
) == 0) break;
544 incXYZ(dmv
, ref sx
, ref sy
, ref sz
);
555 assert(clrdmv
== DMV_X || clrdmv
== DMV_Y || clrdmv
== DMV_Z
);
556 int sx
= x
, sy
= y
, sz
= z
;
557 for (uint f
= 0; f
< count
; ++f
) {
558 VoxPix
*vp
= vox
.queryVP(sx
, sy
, sz
);
560 assert(vp
.cull
&clrmask
);
562 incXYZ(clrdmv
, ref sx
, ref sy
, ref sz
);
564 addSlabFace(clrmask
, clrdmv
, x
-px
, y
-py
, z
-pz
, count
, slab
[0..count
]);
573 // 128 chars should be enough for everyone
574 static char[] milliToBuffer (uint msecs
, char[] dest
) nothrow @trusted @nogc {
575 import core
.stdc
.stdio
: snprintf
;
576 char[128] buf
= void;
577 //immutable uint micro = 0; msecs /= 1000;
578 immutable uint milli
= cast(uint)(msecs
%1000);
580 immutable uint seconds
= cast(uint)(msecs
%60);
582 immutable uint minutes
= cast(uint)(msecs
%60);
584 immutable uint hours
= cast(uint)msecs
;
586 if (hours
) len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%u:%02u:%02u.%03u", hours
, minutes
, seconds
, milli
);
587 else if (minutes
) len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%u:%02u.%03u", minutes
, seconds
, milli
);
588 else if (seconds
) len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%u.%03u", seconds
, milli
);
589 else if (milli
>= 10) len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%ums", milli
);
590 //else if (micro != 0) len = cast(uint)snprintf(buf.ptr, buf.length, "%ums:%umcs", milli, micro);
591 else len
= cast(uint)snprintf(buf
.ptr
, buf
.length
, "%ums", milli
);
592 if (len
> dest
.length
) len
= cast(uint)dest
.length
;
593 dest
.ptr
[0..len
] = buf
.ptr
[0..len
];
594 return dest
.ptr
[0..len
];
597 // this tries to create big quads
598 void buildOpt4 (ref VoxelData vox
) {
599 const float px
= vox
.cx
;
600 const float py
= vox
.cy
;
601 const float pz
= vox
.cz
;
603 // try slabs in all 6 directions?
605 scope(exit
) { delete slab
; slab
= null; }
609 scope(exit
) bmp3d
.clear();
610 if (vox_optimiser_use_3dbmp
) vox
.create3DBitmap(ref bmp3d
);
612 VoxelDataSmall vxopt
;
613 scope(exit
) vxopt
.clear();
614 if (vox_optimiser_use_cvox
) vxopt
.createFrom(ref vox
);
616 const ulong tstart
= clockMilli();
617 ulong tlastreport
= 0;
618 auto tm
= iv
.timer
.Timer(true);
620 char[129] tbuf
= void;
621 char[129] tbuf2
= void;
622 bool wasPrint
= false;
625 for (uint cidx
= 0; cidx
< 6; ++cidx
) {
626 const ubyte cmask
= VoxelData
.cullmask(cidx
);
628 uint vwdt
, vhgt
, vlen
;
629 if (cmask
&Cull_ZAxisMask
) {
633 } else if (cmask
&Cull_XAxisMask
) {
642 bmp2d
.setSize(vwdt
, vhgt
);
644 for (uint vcrd
= 0; vcrd
< vlen
; ++vcrd
) {
645 //bmp2d.clearBmp(); // no need to, it is guaranteed
646 assert(bmp2d
.dotCount
== 0);
647 for (uint vdy
= 0; vdy
< vhgt
; ++vdy
) {
648 for (uint vdx
= 0; vdx
< vwdt
; ++vdx
) {
650 if (cmask
&Cull_ZAxisMask
) { vx
= vdx
; vy
= vdy
; vz
= vcrd
; }
651 else if (cmask
&Cull_XAxisMask
) { vx
= vcrd
; vy
= vdx
; vz
= vdy
; }
652 else { vx
= vdx
; vy
= vcrd
; vz
= vdy
; }
653 //conwriteln("*vcrd=", vcrd, "; vdx=", vdx, "; vdy=", vdy);
654 if (vox_optimiser_use_3dbmp
&& !bmp3d
.getPixel(vx
, vy
, vz
)) continue;
655 if (vox_optimiser_use_cvox
) {
656 const uint vd
= vxopt
.queryVox(vx
, vy
, vz
);
657 if (((vd
>>24)&cmask
) == 0) continue;
658 bmp2d
.setPixel(vdx
, vdy
, vd|
0xff000000U
);
660 auto vp
= vox
.queryVP(vx
, vy
, vz
);
661 if (!vp ||
((*vp
).cull
&cmask
) == 0) continue;
662 bmp2d
.setPixel(vdx
, vdy
, (*vp
).rgb());
663 if (((*vp
).cull ^
= cmask
) == 0) {
664 vox
.removeVoxel(vx
, vy
, vz
);
665 if (vox_optimiser_use_3dbmp
) bmp3d
.resetPixel(vx
, vy
, vz
);
670 //conwriteln(":: cidx=", cidx, "; vcrd=", vcrd, "; dotCount=", bmp2d.dotCount);
671 if (bmp2d
.dotCount
== 0) continue;
672 // ok, we have some dots, go create quads
674 while (bmp2d
.doOne(&x0
, &y0
, &x1
, &y1
)) {
675 const uint cwdt
= (x1
-x0
)+1;
676 const uint chgt
= (y1
-y0
)+1;
677 if (slab
.length
< cwdt
*chgt
) slab
.length
= ((cwdt
*chgt
)|
0xff)+1;
680 for (int dy
= y0
; dy
<= y1
; ++dy
) {
681 for (int dx
= x0
; dx
<= x1
; ++dx
) {
682 *dp
++ = bmp2d
.resetPixel(dx
, dy
);
686 if (cmask
&Cull_ZAxisMask
) { fx
= x0
; fy
= y0
; fz
= vcrd
; }
687 else if (cmask
&Cull_XAxisMask
) { fx
= vcrd
; fy
= x0
; fz
= y0
; }
688 else { fx
= x0
; fy
= vcrd
; fz
= y0
; }
689 addQuad(cmask
, fx
-px
, fy
-py
, fz
-pz
, cwdt
, chgt
, slab
[0..cwdt
*chgt
]);
692 const ulong ctt
= clockMilli();
693 if (ctt
-tlastreport
>= 1000) {
695 const uint curr
= vlen
*cidx
+vcrd
+1U;
696 const uint total
= vlen
*6U;
697 const uint stt
= cast(uint)(ctt
-tstart
);
698 const uint ett
= cast(uint)(cast(ulong)stt
*total
/curr
);
701 char[] bf
= milliToBuffer(stt
, tbuf
[0..$-1]);
702 char[] bf2
= milliToBuffer(ett
, tbuf2
[0..$-1]);
703 import core
.stdc
.stdio
: fprintf
, stdout
, fflush
;
704 fprintf(stdout
, " %3u%% %s (ETA: %s)\x1b[K\r", 100U*curr
/total
, bf
.ptr
, bf2
.ptr
);
713 import core
.stdc
.stdio
: fprintf
, stdout
, fflush
;
714 fprintf(stdout
, "\r\x1b[K"); fflush(stdout
);
716 conwriteln("*** ultra conversion done in ", tm
.toString());
720 void createFrom (ref VoxelData vox
) {
721 assert(vox
.xsize
&& vox
.ysize
&& vox
.zsize
);
723 conwriteln("building slabs...");
724 auto tm
= iv
.timer
.Timer(true);
725 switch (vox_optimisation
) {
726 case 0: buildOpt0(vox
); break;
727 case 1: buildOpt1(vox
); break;
728 case 2: buildOpt2(vox
); break;
729 case 3: buildOpt3(vox
); break;
730 case 4: default: buildOpt4(vox
); break;
733 conwriteln("basic conversion: ", quads
.length
, " quads (", quads
.length
*2, " tris).");
734 conwriteln("converted in ", tm
.toString(), "; optlevel is ", vox_optimisation
, "/", kvx_max_optim_level
);
741 delete quads
; quads
= null;
746 bool isEmpty () const pure { pragma(inline
, true); return (quads
.length
== 0); }