faster quad normals generation (no need to calculate them)
[voxconv.git] / vox_mesh.d
blob3f98524fcd4063f3deefd824ea5de1d5aad04c86
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?
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.
59 struct VoxelMesh {
60 // quad type
61 enum {
62 Invalid = -1,
63 Point,
64 XLong,
65 YLong,
66 ZLong,
67 Quad,
70 enum {
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),
83 enum {
84 DMV_X = 0b100,
85 DMV_Y = 0b010,
86 DMV_Z = 0b001,
89 // bitmasks, `DMV_n` can be used to check for `0` or `1`
90 enum {
91 X0_Y0_Z0,
92 X0_Y0_Z1,
93 X0_Y1_Z0,
94 X0_Y1_Z1,
95 X1_Y0_Z0,
96 X1_Y0_Z1,
97 X1_Y1_Z0,
98 X1_Y1_Z1,
101 VoxQuad[] quads;
102 // voxel center point
103 float cx, cy, cz;
104 // color atlas
105 VoxColorPack catlas;
107 static immutable ubyte[4][6] quadFaces = [
108 // right (&0x01) (right)
110 X1_Y1_Z0,
111 X1_Y0_Z0,
112 X1_Y0_Z1,
113 X1_Y1_Z1,
115 // left (&0x02) (left)
117 X0_Y0_Z0,
118 X0_Y1_Z0,
119 X0_Y1_Z1,
120 X0_Y0_Z1,
122 // top (&0x04) (near)
124 X0_Y0_Z0,
125 X0_Y0_Z1,
126 X1_Y0_Z1,
127 X1_Y0_Z0,
129 // bottom (&0x08) (far)
131 X1_Y1_Z0,
132 X1_Y1_Z1,
133 X0_Y1_Z1,
134 X0_Y1_Z0,
136 // back (&0x10) (top)
138 X0_Y1_Z1,
139 X1_Y1_Z1,
140 X1_Y0_Z1,
141 X0_Y0_Z1,
143 // front (&0x20) (bottom)
145 X0_Y0_Z0,
146 X1_Y0_Z0,
147 X1_Y1_Z0,
148 X0_Y1_Z0,
152 static immutable float[4][6] quadNormals = [
153 // right (&0x01) (right)
154 [ 1.0f, 0.0f, 0.0f],
155 // left (&0x02) (left)
156 [-1.0f, 0.0f, 0.0f],
157 // top (&0x04) (near)
158 [ 0.0f,-1.0f, 0.0f],
159 // bottom (&0x08) (far)
160 [ 0.0f, 1.0f, 0.0f],
161 // back (&0x10) (top)
162 [ 0.0f, 0.0f, 1.0f],
163 // front (&0x20) (bottom)
164 [ 0.0f, 0.0f,-1.0f],
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];
172 } else {
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, ")");
185 return;
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;
195 vx.qtype = type;
196 vx.dx = vx.dy = vx.dz = 0.0f;
197 vx.x = x;
198 vx.y = y;
199 vx.z = z;
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; }
203 return vx;
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)
211 if (len < 1) return;
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];
217 bool allsame = true;
218 foreach (auto cidx; 1..colors.length) {
219 if (colors[cidx] != colors[0]) {
220 allsame = false;
221 break;
224 if (allsame) colors = colors[0..1];
226 const int qtype =
227 colors.length == 1 ? Point :
228 (dmv&DMV_X) ? XLong :
229 (dmv&DMV_Y) ? YLong :
230 ZLong;
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);
234 uint qidx;
235 switch (cull) {
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;
242 default: assert(0);
244 VoxQuad vq;
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);
249 vq.type = qtype;
250 vq.cull = cull;
251 quadCalcNormal(vq);
252 quads ~= vq;
255 void addCube (ubyte cull, float x, float y, float z, uint rgb) {
256 immutable uint[1] carr = [rgb];
257 // generate quads
258 foreach (uint qidx; 0..6) {
259 const ubyte cmask = VoxelData.cullmask(qidx);
260 if (cull&cmask) {
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];
276 bool allsame = true;
277 foreach (auto cidx; 1..colors.length) {
278 if (colors[cidx] != colors[0]) {
279 allsame = false;
280 break;
283 if (allsame) colors = colors[0..1];
285 const int qtype = Quad;
286 uint qidx;
287 switch (cull) {
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;
294 default: assert(0);
297 VoxQuad vq;
298 for (uint vidx = 0; vidx < 4; ++vidx) {
299 const ubyte vtype = quadFaces[qidx][vidx];
300 VoxQuadVertex vx = void;
301 vx.qtype = vtype;
302 vx.dx = vx.dy = vx.dz = 0.0f;
303 vx.x = x;
304 vx.y = y;
305 vx.z = z;
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;
318 } else {
319 assert(0);
321 vx.x += vx.dx;
322 vx.y += vx.dy;
323 vx.z += vx.dz;
324 vq.vx[vidx] = vx;
327 if (colors.length == 1) {
328 setColors(ref vq, colors[], 1, 1);
329 } else {
330 setColors(ref vq, colors[], wdt, hgt);
333 vq.type = qtype;
334 vq.cull = cull;
335 quadCalcNormal(vq);
336 quads ~= vq;
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);
347 while (dofs) {
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);
366 if (!dofs) continue;
368 // long top and bottom quads
369 while (dofs) {
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);
384 while (dofs) {
385 while (dofs && (vox.data[dofs].cull&cmask) == 0) dofs = vox.data[dofs].nextz;
386 if (!dofs) break;
387 const int z = cast(int)vox.data[dofs].z;
388 int count = 0;
389 uint eofs = dofs;
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;
395 ++count;
396 if (count == cast(int)slab.length) break;
398 assert(count);
399 dofs = eofs;
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);
418 if (!dofs) continue;
420 // long top and bottom quads
421 while (dofs) {
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);
427 // by x
428 int xcount = 0;
429 while (x+xcount < cast(int)vox.xsize) {
430 const ubyte vcull = vox.queryCull(x+xcount, y, z);
431 if ((vcull&cmask) == 0) break;
432 ++xcount;
434 // by y
435 int ycount = 0;
436 while (y+ycount < cast(int)vox.ysize) {
437 const ubyte vcull = vox.queryCull(x, y+ycount, z);
438 if ((vcull&cmask) == 0) break;
439 ++ycount;
441 assert(xcount && ycount);
442 // now use the longest one
443 if (xcount >= ycount) {
444 xcount = 0;
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);
450 ++xcount;
452 assert(xcount);
453 addSlabFace(cmask, DMV_X, x-px, y-py, z-pz, xcount, slab[0..xcount]);
454 } else {
455 ycount = 0;
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);
461 ++ycount;
463 assert(ycount);
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);
474 while (dofs) {
475 while (dofs && (vox.data[dofs].cull&cmask) == 0) dofs = vox.data[dofs].nextz;
476 if (!dofs) break;
477 const int z = cast(int)vox.data[dofs].z;
478 int count = 0;
479 uint eofs = dofs;
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;
485 ++count;
486 if (count == cast(int)slab.length) break;
488 assert(count);
489 dofs = eofs;
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);
517 sx += getDX(dmv);
518 sy += getDY(dmv);
519 sz += getDZ(dmv);
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) {
526 uint count = 0;
527 ubyte clrdmv = 0;
528 ubyte clrmask = 0;
529 const int z = cast(int)vox.data[dofs].z;
530 // check all faces
531 foreach (uint cidx; 0..6) {
532 const ubyte cmask = VoxelData.cullmask(cidx);
533 if ((vox.data[dofs].cull&cmask) == 0) continue;
534 // try two dirs
535 foreach (uint ndir; 0..2) {
536 const ubyte dmv = dmove[cidx>>1][ndir];
537 int cnt = 1;
538 int sx = x, sy = y, sz = z;
539 incXYZ(dmv, ref sx, ref sy, ref sz);
540 for (;;) {
541 const ubyte vxc = vox.queryCull(sx, sy, sz);
542 if ((vxc&cmask) == 0) break;
543 ++cnt;
544 incXYZ(dmv, ref sx, ref sy, ref sz);
546 if (cnt > count) {
547 count = cnt;
548 clrdmv = dmv;
549 clrmask = cmask;
553 if (clrmask) {
554 assert(count);
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);
559 slab[f] = vp.rgb();
560 assert(vp.cull&clrmask);
561 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);
579 msecs /= 1000;
580 immutable uint seconds = cast(uint)(msecs%60);
581 msecs /= 60;
582 immutable uint minutes = cast(uint)(msecs%60);
583 msecs /= 60;
584 immutable uint hours = cast(uint)msecs;
585 uint len;
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?
604 uint[] slab = null;
605 scope(exit) { delete slab; slab = null; }
607 // for faster scans
608 Vox3DBitmap bmp3d;
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;
624 Vox2DBitmap bmp2d;
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) {
630 vwdt = vox.xsize;
631 vhgt = vox.ysize;
632 vlen = vox.zsize;
633 } else if (cmask&Cull_XAxisMask) {
634 vwdt = vox.ysize;
635 vhgt = vox.zsize;
636 vlen = vox.xsize;
637 } else {
638 vwdt = vox.xsize;
639 vhgt = vox.zsize;
640 vlen = vox.ysize;
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) {
649 uint vx, vy, vz;
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);
659 } else {
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
673 int x0, y0, x1, y1;
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;
678 // get colors
679 uint *dp = slab.ptr;
680 for (int dy = y0; dy <= y1; ++dy) {
681 for (int dx = x0; dx <= x1; ++dx) {
682 *dp++ = bmp2d.resetPixel(dx, dy);
685 float fx, fy, fz;
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) {
694 tlastreport = ctt;
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);
699 tbuf[] = 0;
700 tbuf2[] = 0;
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);
705 fflush(stdout);
706 wasPrint = true;
711 tm.stop();
712 if (wasPrint) {
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);
722 // now build cubes
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;
732 tm.stop();
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);
735 cx = vox.cx;
736 cy = vox.cy;
737 cz = vox.cz;
740 void clear () {
741 delete quads; quads = null;
742 catlas.clear();
743 cx = cy = cz = 0.0f;
746 bool isEmpty () const pure { pragma(inline, true); return (quads.length == 0); }