added progress indicator for long conversions
[voxconv.git] / vox_meshgl.d
blob31d0518068d5074e33ef8f28e17d80861cc7327a
1 module vox_meshgl;
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;
13 import vox_3dbmp;
14 import vox_mesh;
17 // ////////////////////////////////////////////////////////////////////////// //
18 public __gshared bool vox_fix_tjunctions = false;
19 public __gshared bool vox_wireframe = false;
22 // ////////////////////////////////////////////////////////////////////////// //
23 enum BreakIndex = 6553500;
26 // ////////////////////////////////////////////////////////////////////////// //
28 this builds the OpenGL data structures, ready to be uploaded to the GPU.
29 it also can perform t-junction fixing. it is using quad data from `VoxelMesh`
30 as a source, and creates triangle fans from those, calucluating the proper
31 texture mapping coordinates.
33 struct GLVoxelMesh {
34 static align(1) struct VVoxVertexEx {
35 align(1):
36 float x, y, z;
37 float s, t;
38 float nx, ny, nz; // normal
40 float get (uint idx) const nothrow @safe @nogc {
41 pragma(inline, true);
42 assert(idx < 3);
43 return (idx == 0 ? x : idx == 1 ? y : z);
46 void set (uint idx, const float v) nothrow @safe @nogc {
47 pragma(inline, true);
48 assert(idx < 3);
49 if (idx == 0) x = v;
50 else if (idx == 1) y = v;
51 else z = v;
55 VVoxVertexEx[] vertices;
56 uint[] indicies;
57 uint[] lindicies; // lines
58 uint uniqueVerts;
60 bool sealed;
61 uint[VVoxVertexEx] vertcache;
62 float[3] vmin; // minimum vertex coords
63 float[3] vmax; // maximum vertex coords
65 uint[] img;
66 uint imgWidth, imgHeight;
68 public:
69 void save (VFile fl) {
70 fl.rawWriteExact("K8VOXGL0");
72 fl.writeNum!uint(cast(uint)BreakIndex);
74 // texture
75 fl.writeNum!ushort(cast(ushort)imgWidth);
76 fl.writeNum!ushort(cast(ushort)imgHeight);
77 fl.rawWriteExact(img[0..imgWidth*imgHeight]);
79 // vertices
80 fl.writeNum!uint(cast(uint)vertices.length);
81 for (uint f = 0; f < cast(uint)vertices.length; ++f) {
82 fl.writeNum(vertices[f].x);
83 fl.writeNum(vertices[f].y);
84 fl.writeNum(vertices[f].z);
85 fl.writeNum(vertices[f].s);
86 fl.writeNum(vertices[f].t);
87 fl.writeNum(vertices[f].nx);
88 fl.writeNum(vertices[f].ny);
89 fl.writeNum(vertices[f].nz);
92 // indicies
93 fl.writeNum!uint(cast(uint)indicies.length);
94 fl.rawWriteExact(indicies[]);
96 // line indicies
97 fl.writeNum!uint(cast(uint)lindicies.length);
98 fl.rawWriteExact(lindicies[]);
101 void load (VFile fl) {
102 clear();
104 char[8] sign = void;
105 fl.rawReadExact(sign[]);
106 if (sign[] != "K8VOXGL0") throw new Exception("invalid gl voxel data");
108 const uint bidx = fl.readNum!uint;
109 if (bidx != BreakIndex) throw new Exception("invalid break index");
111 // texture
112 imgWidth = fl.readNum!ushort;
113 imgHeight = fl.readNum!ushort;
114 delete img;
115 img = new uint[imgWidth*imgHeight];
116 fl.rawReadExact(img[]);
118 // vertices
119 uint tvcount = fl.readNum!uint;
120 delete vertices;
121 vertices = new VVoxVertexEx[tvcount];
122 for (uint f = 0; f < tvcount; ++f) {
123 vertices[f].x = fl.readNum!float;
124 vertices[f].y = fl.readNum!float;
125 vertices[f].z = fl.readNum!float;
126 vertices[f].s = fl.readNum!float;
127 vertices[f].t = fl.readNum!float;
128 vertices[f].nx = fl.readNum!float;
129 vertices[f].ny = fl.readNum!float;
130 vertices[f].nz = fl.readNum!float;
133 // indicies
134 uint indcount = fl.readNum!uint;
135 delete indicies;
136 indicies = new uint[indcount];
137 fl.rawReadExact(indicies[]);
139 // line indicies
140 uint lindcount = fl.readNum!uint;
141 delete lindicies;
142 lindicies = new uint[lindcount];
143 fl.rawReadExact(lindicies[]);
145 sealed = true;
148 public:
149 uint appendVertex (in VVoxVertexEx gv) {
150 assert(!sealed);
151 if (auto vp = gv in vertcache) return *vp;
152 ++uniqueVerts;
153 immutable uint res = cast(uint)vertices.length;
154 vertices ~= gv;
155 vertcache[gv] = res;
156 // fix min coords
157 if (vmin[0] > gv.x) vmin[0] = gv.x;
158 if (vmin[1] > gv.y) vmin[1] = gv.y;
159 if (vmin[2] > gv.z) vmin[2] = gv.z;
160 // fix max coords
161 if (vmax[0] < gv.x) vmax[0] = gv.x;
162 if (vmax[1] < gv.y) vmax[1] = gv.y;
163 if (vmax[2] < gv.z) vmax[2] = gv.z;
164 return res;
167 private:
168 enum {
169 AXIS_X = 0,
170 AXIS_Y = 1,
171 AXIS_Z = 2,
175 here starts the t-junction fixing part
176 probably the most complex piece of code here
177 (because almost everything else is done elsewhere)
179 the algorithm is very simple and fast, tho, because i can
180 abuse the fact that vertices are always snapped onto the grid.
181 so i simply created a bitmap that tells if there is any vertex
182 at the given grid coords, and then walk over the edge, checking
183 the bitmap, and adding the vertices. this is easy too, because
184 all vertices are parallel to one of the coordinate axes. so no
185 complex math required at all.
187 another somewhat complex piece of code is triangle fan creator.
188 there are four basic cases here.
190 first: normal quad without any added vertices.
191 we can simply copy its vertices, because such quad makes a valid fan.
193 second: quad that have at least two edges without added vertices.
194 we can use the shared vertex of those two edges as a starting point of
195 a fan.
197 third: two opposite edges have no added vertices.
198 this can happen in "run conversion", and we can create two fans here.
200 fourth: no above conditions are satisfied.
201 this is the most complex case: to create a fan without degenerate triangles,
202 we have to add a vertex in the center of the quad, and used it as a start of
203 a triangle fan.
205 note that we can always convert our triangle fans into triangle soup, so i
206 didn't bothered to create a separate triangle soup code.
209 static struct VoxEdge {
210 uint v0, v1; // start and end vertex
211 float dir; // by the axis, not normalized
212 float clo, chi; // low and high coords
213 uint[] moreverts; // added vertices
214 ubyte axis; // AXIS_n
218 VoxEdge[] edges;
219 uint totaltadded;
221 Vox3DBitmap gridbmp;
222 int[3] gridmin;
223 int[3] gridmax;
226 void freeSortStructs () {
227 gridbmp.clear();
230 void createGrid () {
231 // create the grid
232 for (uint f = 0; f < 3; ++f) {
233 gridmin[f] = cast(int)vmin[f];
234 gridmax[f] = cast(int)vmax[f];
236 uint gxs = cast(uint)(gridmax[0]-gridmin[0]+1);
237 uint gys = cast(uint)(gridmax[1]-gridmin[1]+1);
238 uint gzs = cast(uint)(gridmax[2]-gridmin[2]+1);
239 conwriteln("vox dims: (", gridmin[0], ",", gridmin[1], ",", gridmin[2], ")-(",
240 gridmax[0], ",", gridmax[1], ",", gridmax[2], ")");
241 conwriteln("grid size: (", gxs, ",", gys, ",", gzs, ")");
242 gridbmp.setSize(gxs, gys, gzs);
245 void gridCoords (float fx, float fy, float fz, int *gx, int *gy, int *gz) const nothrow @trusted @nogc {
246 int vx = cast(int)fx;
247 int vy = cast(int)fy;
248 int vz = cast(int)fz;
249 assert(vx >= gridmin[0] && vy >= gridmin[1] && vz >= gridmin[2]);
250 assert(vx <= gridmax[0] && vy <= gridmax[1] && vz <= gridmax[2]);
251 *gx = vx-gridmin[0];
252 *gy = vy-gridmin[1];
253 *gz = vz-gridmin[2];
256 void putVertexToGrid (uint vidx) {
257 int vx, vy, vz;
258 gridCoords(vertices[vidx].x, vertices[vidx].y, vertices[vidx].z, &vx, &vy, &vz);
259 gridbmp.setPixel(vx, vy, vz);
262 uint hasVertexAt (float fx, float fy, float fz) const nothrow @trusted @nogc {
263 int vx, vy, vz;
264 gridCoords(fx, fy, fz, &vx, &vy, &vz);
265 return gridbmp.getPixel(vx, vy, vz);
268 void putEdgeToGrid (uint eidx) {
269 VoxEdge *e = &edges[eidx];
270 putVertexToGrid(e.v0);
271 putVertexToGrid(e.v1);
274 // create 3d grid, put edges into it
275 void sortEdges () {
276 createGrid();
277 for (uint f = 0; f < cast(uint)edges.length; ++f) putEdgeToGrid(f);
280 // create list of edges
281 void createEdges () {
282 totaltadded = 0;
283 edges.length = cast(uint)indicies.length/5U*4U; // one quad is 4 edges
284 uint eidx = 0;
285 uint uqcount = 0;
286 for (uint f = 0; f < cast(uint)indicies.length; f += 5) {
287 bool unitquad = true;
288 for (uint vx0 = 0; vx0 < 4; ++vx0) {
289 const uint vx1 = (vx0+1)&3;
290 VoxEdge *e = &edges[eidx++];
291 e.v0 = indicies[f+vx0];
292 e.v1 = indicies[f+vx1];
293 if (vertices[e.v0].x != vertices[e.v1].x) {
294 assert(vertices[e.v0].y == vertices[e.v1].y);
295 assert(vertices[e.v0].z == vertices[e.v1].z);
296 e.axis = AXIS_X;
297 } else if (vertices[e.v0].y != vertices[e.v1].y) {
298 assert(vertices[e.v0].x == vertices[e.v1].x);
299 assert(vertices[e.v0].z == vertices[e.v1].z);
300 e.axis = AXIS_Y;
301 } else {
302 assert(vertices[e.v0].x == vertices[e.v1].x);
303 assert(vertices[e.v0].y == vertices[e.v1].y);
304 assert(vertices[e.v0].z != vertices[e.v1].z);
305 e.axis = AXIS_Z;
307 e.clo = vertices[e.v0].get(e.axis);
308 e.chi = vertices[e.v1].get(e.axis);
309 e.dir = e.chi-e.clo;
310 if (unitquad) unitquad = (e.dir == +1.0f || e.dir == -1.0f);
312 // "unit quads" can be ignored, they aren't interesting
313 // also, each quad always have at least one "unit edge"
314 // (this will be used to build triangle strips)
315 uqcount += unitquad;
317 assert(eidx == cast(uint)edges.length);
318 conwriteln(uqcount, " unit quad", (uqcount != 1 ? "s" : ""), " found.");
321 void fixEdgeWithVert (ref VoxEdge edge, float crd) {
322 // calculate time
323 const float tm = (crd-edge.clo)/edge.dir;
325 const VVoxVertexEx *evx0 = &vertices[edge.v0];
326 const VVoxVertexEx *evx1 = &vertices[edge.v1];
328 VVoxVertexEx nvx = *evx0;
329 // set coord
330 nvx.set(edge.axis, crd);
331 // calc new (s,t)
332 nvx.s += (evx1.s-evx0.s)*tm;
333 nvx.t += (evx1.t-evx0.t)*tm;
335 edge.moreverts.assumeSafeAppend;
336 edge.moreverts ~= appendVertex(nvx);
337 ++totaltadded;
340 // fix one edge
341 void fixEdgeNew (uint eidx) {
342 VoxEdge *edge = &edges[eidx];
343 if (edge.dir == +1.0f || edge.dir == -1.0f) return; // and here
344 // check grid by the edge axis
345 float[3] gxyz = void;
346 for (uint f = 0; f < 3; ++f) gxyz[f] = vertices[edge.v0].get(f);
347 const float step = (edge.dir < 0.0f ? -1.0f : +1.0f);
348 gxyz[edge.axis] += step;
349 while (gxyz[edge.axis] != edge.chi) {
350 if (hasVertexAt(gxyz[0], gxyz[1], gxyz[2])) {
351 fixEdgeWithVert(*edge, gxyz[edge.axis]);
353 gxyz[edge.axis] += step;
357 void rebuildEdges () {
358 // now we have to rebuild quads
359 // each quad will have at least two unmodified edges of unit length
360 uint newindcount = cast(uint)edges.length*5;
361 for (uint f = 0; f < cast(uint)edges.length; ++f) {
362 newindcount += cast(uint)edges[f].moreverts.length+8;
364 uint[] newind;
365 newind.length = newindcount;
367 newindcount = 0;
368 for (uint f = 0; f < cast(uint)edges.length; f += 4) {
369 // check if this quad is modified at all
370 if (edges[f+0].moreverts.length ||
371 edges[f+1].moreverts.length ||
372 edges[f+2].moreverts.length ||
373 edges[f+3].moreverts.length)
375 // this can be a quad that needs to be converted into a "centroid fan"
376 // if we have at least two adjacent edges without extra points, we can use them
377 // otherwise, we need to append a centroid
378 int firstGoodFace = -1;
379 for (uint c = 0; c < 4; ++c) {
380 if (edges[f+c].moreverts.length == 0 &&
381 edges[f+((c+1)&3)].moreverts.length == 0)
383 // i found her!
384 firstGoodFace = cast(int)c;
385 break;
389 // have two good faces?
390 if (firstGoodFace >= 0) {
391 // yay, we can use v1 of the first face as the start of the fan
392 assert(edges[f+firstGoodFace].moreverts.length == 0);
393 newind[newindcount++] = edges[f+firstGoodFace].v1;
394 // then v1 of the second good face
395 firstGoodFace = (firstGoodFace+1)&3;
396 assert(edges[f+firstGoodFace].moreverts.length == 0);
397 newind[newindcount++] = edges[f+firstGoodFace].v1;
398 // then add points of the other two faces (ignoring v0)
399 for (uint c = 0; c < 2; ++c) {
400 firstGoodFace = (firstGoodFace+1)&3;
401 for (uint midx = 0; midx < edges[f+firstGoodFace].moreverts.length; ++midx) {
402 newind[newindcount++] = edges[f+firstGoodFace].moreverts[midx];
404 newind[newindcount++] = edges[f+firstGoodFace].v1;
406 // we're done with this quad
407 newind[newindcount++] = BreakIndex;
408 continue;
411 // check if we have two opposite quads without extra points
412 if ((edges[f+0].moreverts.length == 0 && edges[f+2].moreverts.length == 0) ||
413 (edges[f+1].moreverts.length == 0 && edges[f+3].moreverts.length == 0) ||
414 (edges[f+2].moreverts.length == 0 && edges[f+0].moreverts.length == 0) ||
415 (edges[f+3].moreverts.length == 0 && edges[f+1].moreverts.length == 0))
417 // yes, we can use the algo for the strips here
418 for (uint eic = 0; eic < 4; ++eic) {
419 if (!edges[f+eic].moreverts.length) continue;
420 const uint oic = (eic+3)&3; // previous edge
421 // sanity checks
422 assert(edges[f+oic].moreverts.length == 0);
423 assert(edges[f+oic].v1 == edges[f+eic].v0);
424 // create triangle fan
425 newind[newindcount++] = edges[f+oic].v0;
426 newind[newindcount++] = edges[f+eic].v0;
427 // lines
428 uint lvx = edges[f+oic].v0;
429 // append additional vertices (they are already properly sorted)
430 for (uint tmpf = 0; tmpf < cast(uint)edges[f+eic].moreverts.length; ++tmpf) {
431 newind[newindcount++] = edges[f+eic].moreverts[tmpf];
433 // and the last vertex
434 newind[newindcount++] = edges[f+eic].v1;
435 // if the opposite side is not modified, we can finish the fan right now
436 const uint loic = (eic+2)&3;
437 if (edges[f+loic].moreverts.length == 0) {
438 const uint noic = (eic+1)&3;
439 // oic: prev
440 // eic: current
441 // noic: next
442 // loic: last
443 newind[newindcount++] = edges[f+noic].v1;
444 newind[newindcount++] = BreakIndex;
445 // we're done here
446 break;
448 newind[newindcount++] = BreakIndex;
450 continue;
453 // alas, this quad should be converted to "centroid quad"
454 // i.e. we will use quad center point to start a triangle fan
456 // calculate quad center point
457 float cx = 0.0f, cy = 0.0f, cz = 0.0f;
458 float cs = 0.0f, ct = 0.0f;
459 float prevx = 0.0f, prevy = 0.0f, prevz = 0.0f;
460 bool xequal = true, yequal = true, zequal = true;
461 for (uint eic = 0; eic < 4; ++eic) {
462 cs += (vertices[edges[f+eic].v0].s+vertices[edges[f+eic].v1].s)*0.5f;
463 ct += (vertices[edges[f+eic].v0].t+vertices[edges[f+eic].v1].t)*0.5f;
464 const float vx = vertices[edges[f+eic].v0].x;
465 const float vy = vertices[edges[f+eic].v0].y;
466 const float vz = vertices[edges[f+eic].v0].z;
467 cx += vx;
468 cy += vy;
469 cz += vz;
470 if (eic) {
471 xequal = xequal && (prevx == vx);
472 yequal = yequal && (prevy == vy);
473 zequal = zequal && (prevz == vz);
475 prevx = vx;
476 prevy = vy;
477 prevz = vz;
479 cx /= 4.0f;
480 cy /= 4.0f;
481 cz /= 4.0f;
482 cs /= 4.0f;
483 ct /= 4.0f;
485 // determine quad orientation
486 ubyte axis;
487 if (xequal) axis = AXIS_X;
488 else if (yequal) axis = AXIS_Y;
489 else if (zequal) axis = AXIS_Z;
490 else assert(0);
492 // calculate s and t
493 //float s = vertices[edges[f+0].v0].s;
494 //float t = vertices[edges[f+0].v0].t;
495 float s = cs;
496 float t = ct;
498 // append center vertex
499 VVoxVertexEx nvx = vertices[edges[f+0].v0];
500 // set coord
501 nvx.x = cx;
502 nvx.y = cy;
503 nvx.z = cz;
504 // calc new (s,t)
505 nvx.s = s;
506 nvx.t = t;
507 newind[newindcount++] = appendVertex(nvx);
508 ++totaltadded;
510 // append v0 of the first edge
511 newind[newindcount++] = edges[f+0].v0;
512 // append all vertices except v0 for all edges
513 for (uint eic = 0; eic < 4; ++eic) {
514 for (uint midx = 0; midx < edges[f+eic].moreverts.length; ++midx) {
515 newind[newindcount++] = edges[f+eic].moreverts[midx];
517 newind[newindcount++] = edges[f+eic].v1;
519 newind[newindcount++] = BreakIndex;
520 } else {
521 // easy deal, just copy it
522 newind[newindcount++] = edges[f+0].v0;
523 newind[newindcount++] = edges[f+1].v0;
524 newind[newindcount++] = edges[f+2].v0;
525 newind[newindcount++] = edges[f+3].v0;
526 newind[newindcount++] = BreakIndex;
530 delete indicies;
531 indicies = newind[0..newindcount];
532 newind = null;
535 // t-junction fixer entry point
536 // this will also convert vertex data to triangle strips
537 void fixTJunctions () {
538 const uint oldvtotal = cast(uint)vertices.length;
539 createEdges();
540 conwriteln(edges.length, " edges found (", edges.length/4*2, " tris, ", vertices.length, " verts)...");
541 sortEdges();
542 for (uint f = 0; f < cast(uint)edges.length; ++f) fixEdgeNew(f);
543 freeSortStructs();
544 if (totaltadded) {
545 conwriteln(totaltadded, " t-fix vertices added (", vertices.length-oldvtotal, " unique).");
546 rebuildEdges();
547 conwriteln("rebuilt model: ", countTris(), " tris, ", vertices.length, " vertices.");
550 // cleanup
551 foreach (ref VoxEdge ee; edges) {
552 delete ee.moreverts; ee.moreverts = null;
554 delete edges; edges = null;
557 public:
558 void clear () {
559 delete vertices; vertices = null;
560 delete indicies; indicies = null;
561 delete lindicies; lindicies = null;
563 vertcache.clear();
564 uniqueVerts = 0;
566 // our voxels are 1024x1024x1024 at max
567 vmin[0] = vmin[1] = vmin[2] = +8192.0f;
568 vmax[0] = vmax[1] = vmax[2] = -8192.0f;
570 delete img; img = null;
571 imgWidth = imgHeight = 0;
573 glRelease();
574 sealed = false;
578 // count the number of triangles in triangle fan data
579 // used for informational messages
580 uint countTris () {
581 uint res = 0;
582 uint ind = 0;
583 while (ind < cast(uint)indicies.length) {
584 assert(indicies[ind] != BreakIndex);
585 uint end = ind+1;
586 while (end < cast(uint)indicies.length && indicies[end] != BreakIndex) ++end;
587 assert(end < indicies.length);
588 assert(end-ind >= 3);
589 if (end-ind == 3) {
590 // simple triangle
591 res += 1;
592 } else if (end-ind == 4) {
593 // quad
594 res += 2;
595 } else {
596 // triangle fan
597 res += end-ind-2;
599 ind = end+1;
601 return res;
605 // create lines for wireframe view
606 void createLines () {
607 lindicies.length = 0;
608 lindicies.assumeSafeAppend;
610 uint ind = 0;
611 while (ind < cast(uint)indicies.length) {
612 assert(indicies[ind] != BreakIndex);
613 uint end = ind+1;
614 while (end < cast(uint)indicies.length && indicies[end] != BreakIndex) ++end;
615 assert(end < indicies.length);
616 assert(end-ind >= 3);
617 if (end-ind == 3) {
618 // simple triangle
619 lindicies ~= indicies[ind+0];
620 lindicies ~= indicies[ind+1];
621 lindicies ~= indicies[ind+2];
622 lindicies ~= BreakIndex;
623 } else if (end-ind == 4) {
624 // quad
625 lindicies ~= indicies[ind+0];
626 lindicies ~= indicies[ind+1];
627 lindicies ~= indicies[ind+2];
628 lindicies ~= BreakIndex;
630 lindicies ~= indicies[ind+2];
631 lindicies ~= indicies[ind+3];
632 lindicies ~= indicies[ind+0];
633 lindicies ~= BreakIndex;
634 } else {
635 // triangle fan
636 for (int f = ind+1; f < end-1; ++f) {
637 lindicies ~= indicies[ind+0];
638 lindicies ~= indicies[f+0];
639 lindicies ~= indicies[f+1];
640 lindicies ~= BreakIndex;
643 ind = end+1;
648 // main entry point
649 void create (VoxelMesh vox) {
650 assert(vox);
651 clear();
653 imgWidth = vox.catlas.getWidth();
654 imgHeight = vox.catlas.getHeight();
655 img = new uint[imgWidth*imgHeight];
656 img[] = vox.catlas.colors[];
658 version(none) {
659 auto fo = VFile("zzz.raw", "w");
660 fo.writeln(imgWidth, " ", imgHeight);
661 foreach (uint dy; 0..imgHeight) {
662 foreach (uint dx; 0..imgWidth) {
663 if (dx) fo.write(" ");
664 fo.writef("%08X", img[dy*imgWidth+dx]);
666 fo.writeln();
670 auto tm = iv.timer.Timer(true);
672 // calculate quad normals
673 foreach (ref VoxelMesh.VoxQuad vq; vox.quads[]) {
674 vq.calcNormal();
676 conwriteln("color texture size: ", imgWidth, "x", imgHeight);
678 // create arrays
679 foreach (ref VoxelMesh.VoxQuad vq; vox.quads[]) {
680 immutable uint imgx0 = vox.catlas.getTexX(vq.cidx);
681 immutable uint imgx1 = vox.catlas.getTexX(vq.cidx)+vq.wh.getW()-1;
682 immutable uint imgy0 = vox.catlas.getTexY(vq.cidx);
683 immutable uint imgy1 = vox.catlas.getTexY(vq.cidx)+vq.wh.getH()-1;
684 uint[4] vxn = void;
686 VVoxVertexEx gv = void;
687 immutable float u = (cast(float)imgx0+0.5f)/cast(float)imgWidth;
688 immutable float v = (cast(float)imgy0+0.5f)/cast(float)imgHeight;
689 immutable float u0 = (cast(float)imgx0+0.04f)/cast(float)imgWidth;
690 immutable float u1 = (cast(float)imgx1+0.96f)/cast(float)imgWidth;
691 immutable float v0 = (cast(float)imgy0+0.04f)/cast(float)imgHeight;
692 immutable float v1 = (cast(float)imgy1+0.96f)/cast(float)imgHeight;
693 foreach (uint nidx; 0..4) {
694 const VoxelMesh.Vertex *vx = &vq.vx[nidx];
695 gv.x = vx.x;
696 gv.y = vx.y;
697 gv.z = vx.z;
698 if (vq.type == VoxelMesh.ZLong) {
699 gv.s = (vx.dz ? u1 : u0);
700 gv.t = v;
701 } else if (vq.type == VoxelMesh.XLong) {
702 gv.s = (vx.dx ? u1 : u0);
703 gv.t = v;
704 } else if (vq.type == VoxelMesh.YLong) {
705 gv.s = (vx.dy ? u1 : u0);
706 gv.t = v;
707 } else if (vq.type == VoxelMesh.Point) {
708 gv.s = u;
709 gv.t = v;
710 } else {
711 gv.s = u0;
712 gv.t = v0;
713 assert(vq.type == VoxelMesh.Quad);
714 if (vq.cull&VoxelMesh.Cull_ZAxisMask) {
715 if (vx.qtype&VoxelMesh.DMV_X) gv.s = u1;
716 if (vx.qtype&VoxelMesh.DMV_Y) gv.t = v1;
717 } else if (vq.cull&VoxelMesh.Cull_XAxisMask) {
718 if (vx.qtype&VoxelMesh.DMV_Y) gv.s = u1;
719 if (vx.qtype&VoxelMesh.DMV_Z) gv.t = v1;
720 } else if (vq.cull&VoxelMesh.Cull_YAxisMask) {
721 if (vx.qtype&VoxelMesh.DMV_X) gv.s = u1;
722 if (vx.qtype&VoxelMesh.DMV_Z) gv.t = v1;
723 } else {
724 assert(0);
727 gv.nx = vq.normal.x;
728 gv.ny = vq.normal.y;
729 gv.nz = vq.normal.z;
730 vxn[nidx] = appendVertex(gv);
733 indicies ~= vxn[0];
734 indicies ~= vxn[1];
735 indicies ~= vxn[2];
736 indicies ~= vxn[3];
737 indicies ~= BreakIndex;
740 tm.stop();
742 conwriteln(vox.quads.length, " quads, ", countTris(), " tris, ",
743 uniqueVerts, " unique vertices (of ", vox.quads.length*4, ")");
744 conwriteln("OpenGL data created in ", tm.toString());
746 if (vox_fix_tjunctions) {
747 tm.restart();
748 fixTJunctions();
749 tm.stop();
750 conwriteln("t-junctions fixed in ", tm.toString());
751 conwriteln("fixed tris: ", countTris());
754 createLines();
755 vertcache.clear();
759 bool bufloaded = false;
760 uint vvbo, ivbo, lvbo;
761 uint ctxid; // color texture
763 // release OpenGL resources
764 void glRelease () {
765 import iv.glbinds;
766 if (!bufloaded) return;
767 // vertex data
768 glDeleteBuffers(1, &vvbo);
769 // index data
770 glDeleteBuffers(1, &ivbo);
771 glDeleteBuffers(1, &lvbo);
772 // texture
773 glDeleteTextures(1, &ctxid);
774 bufloaded = false;
777 void glUpload () {
778 import iv.glbinds;
779 if (bufloaded) return;
781 glEnable(GL_PRIMITIVE_RESTART);
782 glPrimitiveRestartIndex(BreakIndex);
784 // vertex data
785 glGenBuffers(1, &vvbo);
786 glBindBuffer(GL_ARRAY_BUFFER, vvbo);
787 glBufferData(GL_ARRAY_BUFFER, vertices[0].sizeof*vertices.length, vertices.ptr, GL_STATIC_DRAW);
789 // index data
790 glGenBuffers(1, &ivbo);
791 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ivbo);
792 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicies[0].sizeof*indicies.length, indicies.ptr, GL_STATIC_DRAW);
794 // line index data
795 glGenBuffers(1, &lvbo);
796 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lvbo);
797 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicies[0].sizeof*lindicies.length, lindicies.ptr, GL_STATIC_DRAW);
799 // texture
800 glGenTextures(1, &ctxid);
801 glBindTexture(GL_TEXTURE_2D, ctxid);
802 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
803 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
804 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
805 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
806 GLfloat[4] bclr = 0.0;
807 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
808 // create straight from image
809 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imgWidth, imgHeight, 0, /*GL_RGBA*/GL_BGRA, GL_UNSIGNED_BYTE, img.ptr);
811 bufloaded = true;
814 void draw () {
815 import iv.glbinds;
817 glUpload();
819 //glColor3f(1.0f, 0.5f, 0.0f);
820 //glColor3f(1.0f, 1.0f, 1.0f);
821 glColor3f(1.0f, 1.0f, 1.0f);
822 glBindBuffer(GL_ARRAY_BUFFER, vvbo);
823 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (vox_wireframe ? lvbo : ivbo));
824 glBindTexture(GL_TEXTURE_2D, ctxid);
825 glEnable(GL_TEXTURE_2D);
826 glEnableClientState(GL_VERTEX_ARRAY);
827 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
828 //glEnableClientState(GL_NORMAL_ARRAY);
829 glVertexPointer(3, GL_FLOAT, vertices[0].sizeof, cast(void*)0);
830 glTexCoordPointer(2, GL_FLOAT, vertices[0].sizeof, cast(void*)vertices[0].s.offsetof);
831 if (vox_wireframe) {
832 glDrawElements(GL_LINE_LOOP, cast(uint)lindicies.length, GL_UNSIGNED_INT, cast(void*)0);
833 } else {
834 glDrawElements(GL_TRIANGLE_FAN, cast(uint)indicies.length, GL_UNSIGNED_INT, cast(void*)0);
836 glBindBuffer(GL_ARRAY_BUFFER, 0);
837 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
838 glBindTexture(GL_TEXTURE_2D, 0);
839 glDisable(GL_TEXTURE_2D);
840 glDisableClientState(GL_VERTEX_ARRAY);
841 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
842 //glDisableClientState(GL_NORMAL_ARRAY);