fixed atlas bug; some refactoring
[voxconv.git] / vox_mesh.d
blobb706b1a2921c9c45f44ef892db18575318dad1e8
1 module vox_mesh;
3 import iv.bclamp;
4 import iv.glbinds.utils;
5 import iv.cmdcongl;
6 import iv.strex;
7 import iv.vfs;
8 import iv.vfs.io;
9 import iv.vmath;
11 static import iv.timer;
12 import iv.pxclock;
14 import vox_texatlas;
15 import vox_2dbmp;
16 import vox_3dbmp;
17 import vox_data;
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 {
31 align(1):
32 float x, y, z;
33 float dx, dy, dz;
34 ubyte qtype; // Xn_Yn_Zn
37 // quad is always one texel strip
38 static align(1) struct VoxQuad {
39 VoxQuadVertex[4] vx;
40 uint cidx; // in colorpack's `citems`
41 VoxWH16 wh; // width and height
42 VoxQuadVertex normal;
43 int type = -1/*Invalid*/;
44 ubyte cull; // for which face this quad was created?
46 void calcNormal () {
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);
56 } else {
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;
62 } else {
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.
82 struct VoxelMesh {
83 // quad type
84 enum {
85 Invalid = -1,
86 Point,
87 XLong,
88 YLong,
89 ZLong,
90 Quad,
93 enum {
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),
106 enum {
107 DMV_X = 0b100,
108 DMV_Y = 0b010,
109 DMV_Z = 0b001,
112 // bitmasks, `DMV_n` can be used to check for `0` or `1`
113 enum {
114 X0_Y0_Z0,
115 X0_Y0_Z1,
116 X0_Y1_Z0,
117 X0_Y1_Z1,
118 X1_Y0_Z0,
119 X1_Y0_Z1,
120 X1_Y1_Z0,
121 X1_Y1_Z1,
124 VoxQuad[] quads;
125 // voxel center point
126 float cx, cy, cz;
127 // color atlas
128 VoxColorPack catlas;
130 static immutable ubyte[4][6] quadFaces = [
131 // right (&0x01) (right)
133 X1_Y1_Z0,
134 X1_Y0_Z0,
135 X1_Y0_Z1,
136 X1_Y1_Z1,
138 // left (&0x02) (left)
140 X0_Y0_Z0,
141 X0_Y1_Z0,
142 X0_Y1_Z1,
143 X0_Y0_Z1,
145 // top (&0x04) (near)
147 X0_Y0_Z0,
148 X0_Y0_Z1,
149 X1_Y0_Z1,
150 X1_Y0_Z0,
152 // bottom (&0x08) (far)
154 X1_Y1_Z0,
155 X1_Y1_Z1,
156 X0_Y1_Z1,
157 X0_Y1_Z0,
159 // back (&0x10) (top)
161 X0_Y1_Z1,
162 X1_Y1_Z1,
163 X1_Y0_Z1,
164 X0_Y0_Z1,
166 // front (&0x20) (bottom)
168 X0_Y0_Z0,
169 X1_Y0_Z0,
170 X1_Y1_Z0,
171 X0_Y1_Z0,
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, ")");
181 return;
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;
191 vx.qtype = type;
192 vx.dx = vx.dy = vx.dz = 0.0f;
193 vx.x = x;
194 vx.y = y;
195 vx.z = z;
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; }
199 return vx;
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)
207 if (len < 1) return;
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];
213 bool allsame = true;
214 foreach (auto cidx; 1..colors.length) {
215 if (colors[cidx] != colors[0]) {
216 allsame = false;
217 break;
220 if (allsame) colors = colors[0..1];
222 const int qtype =
223 colors.length == 1 ? Point :
224 (dmv&DMV_X) ? XLong :
225 (dmv&DMV_Y) ? YLong :
226 ZLong;
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);
230 uint qidx;
231 switch (cull) {
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;
238 default: assert(0);
240 VoxQuad vq;
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);
245 vq.type = qtype;
246 vq.cull = cull;
247 quads ~= vq;
250 void addCube (ubyte cull, float x, float y, float z, uint rgb) {
251 immutable uint[1] carr = [rgb];
252 // generate quads
253 foreach (uint qidx; 0..6) {
254 const ubyte cmask = VoxelData.cullmask(qidx);
255 if (cull&cmask) {
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];
271 bool allsame = true;
272 foreach (auto cidx; 1..colors.length) {
273 if (colors[cidx] != colors[0]) {
274 allsame = false;
275 break;
278 if (allsame) colors = colors[0..1];
280 const int qtype = Quad;
281 uint qidx;
282 switch (cull) {
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;
289 default: assert(0);
292 VoxQuad vq;
293 for (uint vidx = 0; vidx < 4; ++vidx) {
294 const ubyte vtype = quadFaces[qidx][vidx];
295 VoxQuadVertex vx = void;
296 vx.qtype = vtype;
297 vx.dx = vx.dy = vx.dz = 0.0f;
298 vx.x = x;
299 vx.y = y;
300 vx.z = z;
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;
313 } else {
314 assert(0);
316 vx.x += vx.dx;
317 vx.y += vx.dy;
318 vx.z += vx.dz;
319 vq.vx[vidx] = vx;
322 if (colors.length == 1) {
323 setColors(ref vq, colors[], 1, 1);
324 } else {
325 setColors(ref vq, colors[], wdt, hgt);
327 vq.type = qtype;
328 vq.cull = cull;
330 quads ~= vq;
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);
341 while (dofs) {
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);
360 if (!dofs) continue;
362 // long top and bottom quads
363 while (dofs) {
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);
378 while (dofs) {
379 while (dofs && (vox.data[dofs].cull&cmask) == 0) dofs = vox.data[dofs].nextz;
380 if (!dofs) break;
381 const int z = cast(int)vox.data[dofs].z;
382 int count = 0;
383 uint eofs = dofs;
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;
389 ++count;
390 if (count == cast(int)slab.length) break;
392 assert(count);
393 dofs = eofs;
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);
412 if (!dofs) continue;
414 // long top and bottom quads
415 while (dofs) {
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);
421 // by x
422 int xcount = 0;
423 while (x+xcount < cast(int)vox.xsize) {
424 const ubyte vcull = vox.queryCull(x+xcount, y, z);
425 if ((vcull&cmask) == 0) break;
426 ++xcount;
428 // by y
429 int ycount = 0;
430 while (y+ycount < cast(int)vox.ysize) {
431 const ubyte vcull = vox.queryCull(x, y+ycount, z);
432 if ((vcull&cmask) == 0) break;
433 ++ycount;
435 assert(xcount && ycount);
436 // now use the longest one
437 if (xcount >= ycount) {
438 xcount = 0;
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);
444 ++xcount;
446 assert(xcount);
447 addSlabFace(cmask, DMV_X, x-px, y-py, z-pz, xcount, slab[0..xcount]);
448 } else {
449 ycount = 0;
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);
455 ++ycount;
457 assert(ycount);
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);
468 while (dofs) {
469 while (dofs && (vox.data[dofs].cull&cmask) == 0) dofs = vox.data[dofs].nextz;
470 if (!dofs) break;
471 const int z = cast(int)vox.data[dofs].z;
472 int count = 0;
473 uint eofs = dofs;
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;
479 ++count;
480 if (count == cast(int)slab.length) break;
482 assert(count);
483 dofs = eofs;
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);
511 sx += getDX(dmv);
512 sy += getDY(dmv);
513 sz += getDZ(dmv);
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) {
520 uint count = 0;
521 ubyte clrdmv = 0;
522 ubyte clrmask = 0;
523 const int z = cast(int)vox.data[dofs].z;
524 // check all faces
525 foreach (uint cidx; 0..6) {
526 const ubyte cmask = VoxelData.cullmask(cidx);
527 if ((vox.data[dofs].cull&cmask) == 0) continue;
528 // try two dirs
529 foreach (uint ndir; 0..2) {
530 const ubyte dmv = dmove[cidx>>1][ndir];
531 int cnt = 1;
532 int sx = x, sy = y, sz = z;
533 incXYZ(dmv, ref sx, ref sy, ref sz);
534 for (;;) {
535 const ubyte vxc = vox.queryCull(sx, sy, sz);
536 if ((vxc&cmask) == 0) break;
537 ++cnt;
538 incXYZ(dmv, ref sx, ref sy, ref sz);
540 if (cnt > count) {
541 count = cnt;
542 clrdmv = dmv;
543 clrmask = cmask;
547 if (clrmask) {
548 assert(count);
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);
553 slab[f] = vp.rgb();
554 assert(vp.cull&clrmask);
555 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);
573 msecs /= 1000;
574 immutable uint seconds = cast(uint)(msecs%60);
575 msecs /= 60;
576 immutable uint minutes = cast(uint)(msecs%60);
577 msecs /= 60;
578 immutable uint hours = cast(uint)msecs;
579 uint len;
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?
598 uint[] slab = null;
599 scope(exit) { delete slab; slab = null; }
601 // for faster scans
602 Vox3DBitmap bmp3d;
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;
618 Vox2DBitmap bmp2d;
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) {
624 vwdt = vox.xsize;
625 vhgt = vox.ysize;
626 vlen = vox.zsize;
627 } else if (cmask&Cull_XAxisMask) {
628 vwdt = vox.ysize;
629 vhgt = vox.zsize;
630 vlen = vox.xsize;
631 } else {
632 vwdt = vox.xsize;
633 vhgt = vox.zsize;
634 vlen = vox.ysize;
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) {
643 uint vx, vy, vz;
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);
653 } else {
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
667 int x0, y0, x1, y1;
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;
672 // get colors
673 uint *dp = slab.ptr;
674 for (int dy = y0; dy <= y1; ++dy) {
675 for (int dx = x0; dx <= x1; ++dx) {
676 *dp++ = bmp2d.resetPixel(dx, dy);
679 float fx, fy, fz;
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) {
688 tlastreport = ctt;
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);
693 tbuf[] = 0;
694 tbuf2[] = 0;
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);
699 fflush(stdout);
700 wasPrint = true;
705 tm.stop();
706 if (wasPrint) {
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);
716 // now build cubes
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;
726 tm.stop();
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);
729 cx = vox.cx;
730 cy = vox.cy;
731 cz = vox.cz;
734 void clear () {
735 delete quads; quads = null;
736 catlas.clear();
737 cx = cy = cz = 0.0f;
740 bool isEmpty () const pure { pragma(inline, true); return (quads.length == 0); }