integrated "simple_color_hash" branch
[voxconv.git] / vox_meshgl.d
blob199d0e0340da64b5ce61454c3ee766ba115e7dda
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 uint[VVoxVertexEx] vertcache;
61 float[3] vmin; // minimum vertex coords
62 float[3] vmax; // maximum vertex coords
64 uint[] img;
65 uint imgWidth, imgHeight;
67 public:
68 uint appendVertex (in VVoxVertexEx gv) {
69 if (auto vp = gv in vertcache) return *vp;
70 ++uniqueVerts;
71 immutable uint res = cast(uint)vertices.length;
72 vertices ~= gv;
73 vertcache[gv] = res;
74 // fix min coords
75 if (vmin[0] > gv.x) vmin[0] = gv.x;
76 if (vmin[1] > gv.y) vmin[1] = gv.y;
77 if (vmin[2] > gv.z) vmin[2] = gv.z;
78 // fix max coords
79 if (vmax[0] < gv.x) vmax[0] = gv.x;
80 if (vmax[1] < gv.y) vmax[1] = gv.y;
81 if (vmax[2] < gv.z) vmax[2] = gv.z;
82 return res;
85 private:
86 enum {
87 AXIS_X = 0,
88 AXIS_Y = 1,
89 AXIS_Z = 2,
93 here starts the t-junction fixing part
94 probably the most complex piece of code here
95 (because almost everything else is done elsewhere)
97 the algorithm is very simple and fast, tho, because i can
98 abuse the fact that vertices are always snapped onto the grid.
99 so i simply created a bitmap that tells if there is any vertex
100 at the given grid coords, and then walk over the edge, checking
101 the bitmap, and adding the vertices. this is easy too, because
102 all vertices are parallel to one of the coordinate axes. so no
103 complex math required at all.
105 another somewhat complex piece of code is triangle fan creator.
106 there are four basic cases here.
108 first: normal quad without any added vertices.
109 we can simply copy its vertices, because such quad makes a valid fan.
111 second: quad that have at least two edges without added vertices.
112 we can use the shared vertex of those two edges as a starting point of
113 a fan.
115 third: two opposite edges have no added vertices.
116 this can happen in "run conversion", and we can create two fans here.
118 fourth: no above conditions are satisfied.
119 this is the most complex case: to create a fan without degenerate triangles,
120 we have to add a vertex in the center of the quad, and used it as a start of
121 a triangle fan.
123 note that we can always convert our triangle fans into triangle soup, so i
124 didn't bothered to create a separate triangle soup code.
127 static struct VoxEdge {
128 uint v0, v1; // start and end vertex
129 float dir; // by the axis, not normalized
130 float clo, chi; // low and high coords
131 uint[] moreverts; // added vertices
132 ubyte axis; // AXIS_n
136 VoxEdge[] edges;
137 uint totaltadded;
139 Vox3DBitmap gridbmp;
140 int[3] gridmin;
141 int[3] gridmax;
144 void freeSortStructs () {
145 gridbmp.clear();
148 void createGrid () {
149 // create the grid
150 for (uint f = 0; f < 3; ++f) {
151 gridmin[f] = cast(int)vmin[f];
152 gridmax[f] = cast(int)vmax[f];
154 uint gxs = cast(uint)(gridmax[0]-gridmin[0]+1);
155 uint gys = cast(uint)(gridmax[1]-gridmin[1]+1);
156 uint gzs = cast(uint)(gridmax[2]-gridmin[2]+1);
157 conwriteln("vox dims: (", gridmin[0], ",", gridmin[1], ",", gridmin[2], ")-(",
158 gridmax[0], ",", gridmax[1], ",", gridmax[2], ")");
159 conwriteln("grid size: (", gxs, ",", gys, ",", gzs, ")");
160 gridbmp.setSize(gxs, gys, gzs);
163 void gridCoords (float fx, float fy, float fz, int *gx, int *gy, int *gz) const nothrow @trusted @nogc {
164 int vx = cast(int)fx;
165 int vy = cast(int)fy;
166 int vz = cast(int)fz;
167 assert(vx >= gridmin[0] && vy >= gridmin[1] && vz >= gridmin[2]);
168 assert(vx <= gridmax[0] && vy <= gridmax[1] && vz <= gridmax[2]);
169 *gx = vx-gridmin[0];
170 *gy = vy-gridmin[1];
171 *gz = vz-gridmin[2];
174 void putVertexToGrid (uint vidx) {
175 int vx, vy, vz;
176 gridCoords(vertices[vidx].x, vertices[vidx].y, vertices[vidx].z, &vx, &vy, &vz);
177 gridbmp.setPixel(vx, vy, vz);
180 uint hasVertexAt (float fx, float fy, float fz) const nothrow @trusted @nogc {
181 int vx, vy, vz;
182 gridCoords(fx, fy, fz, &vx, &vy, &vz);
183 return gridbmp.getPixel(vx, vy, vz);
186 void putEdgeToGrid (uint eidx) {
187 VoxEdge *e = &edges[eidx];
188 putVertexToGrid(e.v0);
189 putVertexToGrid(e.v1);
192 // create 3d grid, put edges into it
193 void sortEdges () {
194 createGrid();
195 for (uint f = 0; f < cast(uint)edges.length; ++f) putEdgeToGrid(f);
198 // create list of edges
199 void createEdges () {
200 totaltadded = 0;
201 edges.length = cast(uint)indicies.length/5U*4U; // one quad is 4 edges
202 uint eidx = 0;
203 uint uqcount = 0;
204 for (uint f = 0; f < cast(uint)indicies.length; f += 5) {
205 bool unitquad = true;
206 for (uint vx0 = 0; vx0 < 4; ++vx0) {
207 const uint vx1 = (vx0+1)&3;
208 VoxEdge *e = &edges[eidx++];
209 e.v0 = indicies[f+vx0];
210 e.v1 = indicies[f+vx1];
211 if (vertices[e.v0].x != vertices[e.v1].x) {
212 assert(vertices[e.v0].y == vertices[e.v1].y);
213 assert(vertices[e.v0].z == vertices[e.v1].z);
214 e.axis = AXIS_X;
215 } else if (vertices[e.v0].y != vertices[e.v1].y) {
216 assert(vertices[e.v0].x == vertices[e.v1].x);
217 assert(vertices[e.v0].z == vertices[e.v1].z);
218 e.axis = AXIS_Y;
219 } else {
220 assert(vertices[e.v0].x == vertices[e.v1].x);
221 assert(vertices[e.v0].y == vertices[e.v1].y);
222 assert(vertices[e.v0].z != vertices[e.v1].z);
223 e.axis = AXIS_Z;
225 e.clo = vertices[e.v0].get(e.axis);
226 e.chi = vertices[e.v1].get(e.axis);
227 e.dir = e.chi-e.clo;
228 if (unitquad) unitquad = (e.dir == +1.0f || e.dir == -1.0f);
230 // "unit quads" can be ignored, they aren't interesting
231 // also, each quad always have at least one "unit edge"
232 // (this will be used to build triangle strips)
233 uqcount += unitquad;
235 assert(eidx == cast(uint)edges.length);
236 conwriteln(uqcount, " unit quad", (uqcount != 1 ? "s" : ""), " found.");
239 void fixEdgeWithVert (ref VoxEdge edge, float crd) {
240 // calculate time
241 const float tm = (crd-edge.clo)/edge.dir;
243 const VVoxVertexEx *evx0 = &vertices[edge.v0];
244 const VVoxVertexEx *evx1 = &vertices[edge.v1];
246 VVoxVertexEx nvx = *evx0;
247 // set coord
248 nvx.set(edge.axis, crd);
249 // calc new (s,t)
250 nvx.s += (evx1.s-evx0.s)*tm;
251 nvx.t += (evx1.t-evx0.t)*tm;
253 edge.moreverts ~= appendVertex(nvx);
254 edge.moreverts.assumeSafeAppend;
255 ++totaltadded;
258 // fix one edge
259 void fixEdgeNew (uint eidx) {
260 VoxEdge *edge = &edges[eidx];
261 if (edge.dir == +1.0f || edge.dir == -1.0f) return; // and here
262 // check grid by the edge axis
263 float[3] gxyz = void;
264 for (uint f = 0; f < 3; ++f) gxyz[f] = vertices[edge.v0].get(f);
265 const float step = (edge.dir < 0.0f ? -1.0f : +1.0f);
266 gxyz[edge.axis] += step;
267 while (gxyz[edge.axis] != edge.chi) {
268 if (hasVertexAt(gxyz[0], gxyz[1], gxyz[2])) {
269 fixEdgeWithVert(*edge, gxyz[edge.axis]);
271 gxyz[edge.axis] += step;
275 void rebuildEdges () {
276 // now we have to rebuild quads
277 // each quad will have at least two unmodified edges of unit length
278 uint newindcount = cast(uint)edges.length*5;
279 for (uint f = 0; f < cast(uint)edges.length; ++f) {
280 newindcount += cast(uint)edges[f].moreverts.length+8;
282 uint[] newind;
283 newind.length = newindcount;
285 newindcount = 0;
286 for (uint f = 0; f < cast(uint)edges.length; f += 4) {
287 // check if this quad is modified at all
288 if (edges[f+0].moreverts.length ||
289 edges[f+1].moreverts.length ||
290 edges[f+2].moreverts.length ||
291 edges[f+3].moreverts.length)
293 // this can be a quad that needs to be converted into a "centroid fan"
294 // if we have at least two adjacent edges without extra points, we can use them
295 // otherwise, we need to append a centroid
296 int firstGoodFace = -1;
297 for (uint c = 0; c < 4; ++c) {
298 if (edges[f+c].moreverts.length == 0 &&
299 edges[f+((c+1)&3)].moreverts.length == 0)
301 // i found her!
302 firstGoodFace = cast(int)c;
303 break;
307 // have two good faces?
308 if (firstGoodFace >= 0) {
309 // yay, we can use v1 of the first face as the start of the fan
310 assert(edges[f+firstGoodFace].moreverts.length == 0);
311 newind[newindcount++] = edges[f+firstGoodFace].v1;
312 // then v1 of the second good face
313 firstGoodFace = (firstGoodFace+1)&3;
314 assert(edges[f+firstGoodFace].moreverts.length == 0);
315 newind[newindcount++] = edges[f+firstGoodFace].v1;
316 // then add points of the other two faces (ignoring v0)
317 for (uint c = 0; c < 2; ++c) {
318 firstGoodFace = (firstGoodFace+1)&3;
319 for (uint midx = 0; midx < edges[f+firstGoodFace].moreverts.length; ++midx) {
320 newind[newindcount++] = edges[f+firstGoodFace].moreverts[midx];
322 newind[newindcount++] = edges[f+firstGoodFace].v1;
324 // we're done with this quad
325 newind[newindcount++] = BreakIndex;
326 continue;
329 // check if we have two opposite quads without extra points
330 if ((edges[f+0].moreverts.length == 0 && edges[f+2].moreverts.length == 0) ||
331 (edges[f+1].moreverts.length == 0 && edges[f+3].moreverts.length == 0) ||
332 (edges[f+2].moreverts.length == 0 && edges[f+0].moreverts.length == 0) ||
333 (edges[f+3].moreverts.length == 0 && edges[f+1].moreverts.length == 0))
335 // yes, we can use the algo for the strips here
336 for (uint eic = 0; eic < 4; ++eic) {
337 if (!edges[f+eic].moreverts.length) continue;
338 const uint oic = (eic+3)&3; // previous edge
339 // sanity checks
340 assert(edges[f+oic].moreverts.length == 0);
341 assert(edges[f+oic].v1 == edges[f+eic].v0);
342 // create triangle fan
343 newind[newindcount++] = edges[f+oic].v0;
344 newind[newindcount++] = edges[f+eic].v0;
345 // lines
346 uint lvx = edges[f+oic].v0;
347 // append additional vertices (they are already properly sorted)
348 for (uint tmpf = 0; tmpf < cast(uint)edges[f+eic].moreverts.length; ++tmpf) {
349 newind[newindcount++] = edges[f+eic].moreverts[tmpf];
351 // and the last vertex
352 newind[newindcount++] = edges[f+eic].v1;
353 // if the opposite side is not modified, we can finish the fan right now
354 const uint loic = (eic+2)&3;
355 if (edges[f+loic].moreverts.length == 0) {
356 const uint noic = (eic+1)&3;
357 // oic: prev
358 // eic: current
359 // noic: next
360 // loic: last
361 newind[newindcount++] = edges[f+noic].v1;
362 newind[newindcount++] = BreakIndex;
363 // we're done here
364 break;
366 newind[newindcount++] = BreakIndex;
368 continue;
371 // alas, this quad should be converted to "centroid quad"
372 // i.e. we will use quad center point to start a triangle fan
374 // calculate quad center point
375 float cx = 0.0f, cy = 0.0f, cz = 0.0f;
376 float cs = 0.0f, ct = 0.0f;
377 float prevx = 0.0f, prevy = 0.0f, prevz = 0.0f;
378 bool xequal = true, yequal = true, zequal = true;
379 for (uint eic = 0; eic < 4; ++eic) {
380 cs += (vertices[edges[f+eic].v0].s+vertices[edges[f+eic].v1].s)*0.5f;
381 ct += (vertices[edges[f+eic].v0].t+vertices[edges[f+eic].v1].t)*0.5f;
382 const float vx = vertices[edges[f+eic].v0].x;
383 const float vy = vertices[edges[f+eic].v0].y;
384 const float vz = vertices[edges[f+eic].v0].z;
385 cx += vx;
386 cy += vy;
387 cz += vz;
388 if (eic) {
389 xequal = xequal && (prevx == vx);
390 yequal = yequal && (prevy == vy);
391 zequal = zequal && (prevz == vz);
393 prevx = vx;
394 prevy = vy;
395 prevz = vz;
397 cx /= 4.0f;
398 cy /= 4.0f;
399 cz /= 4.0f;
400 cs /= 4.0f;
401 ct /= 4.0f;
403 // determine quad orientation
404 ubyte axis;
405 if (xequal) axis = AXIS_X;
406 else if (yequal) axis = AXIS_Y;
407 else if (zequal) axis = AXIS_Z;
408 else assert(0);
410 // calculate s and t
411 //float s = vertices[edges[f+0].v0].s;
412 //float t = vertices[edges[f+0].v0].t;
413 float s = cs;
414 float t = ct;
416 // append center vertex
417 VVoxVertexEx nvx = vertices[edges[f+0].v0];
418 // set coord
419 nvx.x = cx;
420 nvx.y = cy;
421 nvx.z = cz;
422 // calc new (s,t)
423 nvx.s = s;
424 nvx.t = t;
425 newind[newindcount++] = appendVertex(nvx);
426 ++totaltadded;
428 // append v0 of the first edge
429 newind[newindcount++] = edges[f+0].v0;
430 // append all vertices except v0 for all edges
431 for (uint eic = 0; eic < 4; ++eic) {
432 for (uint midx = 0; midx < edges[f+eic].moreverts.length; ++midx) {
433 newind[newindcount++] = edges[f+eic].moreverts[midx];
435 newind[newindcount++] = edges[f+eic].v1;
437 newind[newindcount++] = BreakIndex;
438 } else {
439 // easy deal, just copy it
440 newind[newindcount++] = edges[f+0].v0;
441 newind[newindcount++] = edges[f+1].v0;
442 newind[newindcount++] = edges[f+2].v0;
443 newind[newindcount++] = edges[f+3].v0;
444 newind[newindcount++] = BreakIndex;
448 delete indicies;
449 indicies = newind[0..newindcount];
450 newind = null;
453 // t-junction fixer entry point
454 // this will also convert vertex data to triangle strips
455 void fixTJunctions () {
456 const uint oldvtotal = cast(uint)vertices.length;
457 createEdges();
458 conwriteln(edges.length, " edges found (", edges.length/4*2, " tris, ", vertices.length, " verts)...");
459 sortEdges();
460 for (uint f = 0; f < cast(uint)edges.length; ++f) fixEdgeNew(f);
461 freeSortStructs();
462 if (totaltadded) {
463 conwriteln(totaltadded, " t-fix vertices added (", vertices.length-oldvtotal, " unique).");
464 rebuildEdges();
465 conwriteln("rebuilt model: ", countTris(), " tris, ", vertices.length, " vertices.");
468 // cleanup
469 foreach (ref VoxEdge ee; edges) {
470 delete ee.moreverts; ee.moreverts = null;
472 delete edges; edges = null;
475 public:
476 void clear () {
477 delete vertices; vertices = null;
478 delete indicies; indicies = null;
479 delete lindicies; lindicies = null;
481 vertcache.clear();
482 uniqueVerts = 0;
484 // our voxels are 1024x1024x1024 at max
485 vmin[0] = vmin[1] = vmin[2] = +8192.0f;
486 vmax[0] = vmax[1] = vmax[2] = -8192.0f;
488 delete img; img = null;
489 imgWidth = imgHeight = 0;
491 glRelease();
495 // count the number of triangles in triangle fan data
496 // used for informational messages
497 uint countTris () {
498 uint res = 0;
499 uint ind = 0;
500 while (ind < cast(uint)indicies.length) {
501 assert(indicies[ind] != BreakIndex);
502 uint end = ind+1;
503 while (end < cast(uint)indicies.length && indicies[end] != BreakIndex) ++end;
504 assert(end < indicies.length);
505 assert(end-ind >= 3);
506 if (end-ind == 3) {
507 // simple triangle
508 res += 1;
509 } else if (end-ind == 4) {
510 // quad
511 res += 2;
512 } else {
513 // triangle fan
514 res += end-ind-2;
516 ind = end+1;
518 return res;
522 // create lines for wireframe view
523 void createLines () {
524 lindicies.length = 0;
525 lindicies.assumeSafeAppend;
527 uint ind = 0;
528 while (ind < cast(uint)indicies.length) {
529 assert(indicies[ind] != BreakIndex);
530 uint end = ind+1;
531 while (end < cast(uint)indicies.length && indicies[end] != BreakIndex) ++end;
532 assert(end < indicies.length);
533 assert(end-ind >= 3);
534 if (end-ind == 3) {
535 // simple triangle
536 lindicies ~= indicies[ind+0];
537 lindicies ~= indicies[ind+1];
538 lindicies ~= indicies[ind+2];
539 lindicies ~= BreakIndex;
540 } else if (end-ind == 4) {
541 // quad
542 lindicies ~= indicies[ind+0];
543 lindicies ~= indicies[ind+1];
544 lindicies ~= indicies[ind+2];
545 lindicies ~= BreakIndex;
547 lindicies ~= indicies[ind+2];
548 lindicies ~= indicies[ind+3];
549 lindicies ~= indicies[ind+0];
550 lindicies ~= BreakIndex;
551 } else {
552 // triangle fan
553 for (int f = ind+1; f < end-1; ++f) {
554 lindicies ~= indicies[ind+0];
555 lindicies ~= indicies[f+0];
556 lindicies ~= indicies[f+1];
557 lindicies ~= BreakIndex;
560 ind = end+1;
565 // main entry point
566 void create (VoxelMesh vox) {
567 assert(vox);
568 clear();
570 imgWidth = vox.catlas.getWidth();
571 imgHeight = vox.catlas.getHeight();
572 img = new uint[imgWidth*imgHeight];
573 img[] = vox.catlas.colors[];
575 version(none) {
576 auto fo = VFile("zzz.raw", "w");
577 fo.writeln(imgWidth, " ", imgHeight);
578 foreach (uint dy; 0..imgHeight) {
579 foreach (uint dx; 0..imgWidth) {
580 if (dx) fo.write(" ");
581 fo.writef("%08X", img[dy*imgWidth+dx]);
583 fo.writeln();
587 auto tm = iv.timer.Timer(true);
589 // calculate quad normals
590 foreach (ref VoxelMesh.VoxQuad vq; vox.quads[]) {
591 vq.calcNormal();
593 conwriteln("color texture size: ", imgWidth, "x", imgHeight);
595 // create arrays
596 foreach (ref VoxelMesh.VoxQuad vq; vox.quads[]) {
597 immutable uint imgx0 = vox.catlas.getTexX(vq.cidx);
598 immutable uint imgx1 = vox.catlas.getTexX(vq.cidx)+vq.wh.getW()-1;
599 immutable uint imgy0 = vox.catlas.getTexY(vq.cidx);
600 immutable uint imgy1 = vox.catlas.getTexY(vq.cidx)+vq.wh.getH()-1;
601 uint[4] vxn = void;
603 VVoxVertexEx gv = void;
604 immutable float u = (cast(float)imgx0+0.5f)/cast(float)imgWidth;
605 immutable float v = (cast(float)imgy0+0.5f)/cast(float)imgHeight;
606 immutable float u0 = (cast(float)imgx0+0.0f)/cast(float)imgWidth;
607 immutable float u1 = (cast(float)imgx1+1.0f)/cast(float)imgWidth;
608 immutable float v0 = (cast(float)imgy0+0.0f)/cast(float)imgHeight;
609 immutable float v1 = (cast(float)imgy1+1.0f)/cast(float)imgHeight;
610 foreach (uint nidx; 0..4) {
611 const VoxelMesh.Vertex *vx = &vq.vx[nidx];
612 gv.x = vx.x;
613 gv.y = vx.y;
614 gv.z = vx.z;
615 if (vq.type == VoxelMesh.ZLong) {
616 gv.s = (vx.dz ? u1 : u0);
617 gv.t = v;
618 } else if (vq.type == VoxelMesh.XLong) {
619 gv.s = (vx.dx ? u1 : u0);
620 gv.t = v;
621 } else if (vq.type == VoxelMesh.YLong) {
622 gv.s = (vx.dy ? u1 : u0);
623 gv.t = v;
624 } else if (vq.type == VoxelMesh.Point) {
625 gv.s = u;
626 gv.t = v;
627 } else {
628 gv.s = u0;
629 gv.t = v0;
630 assert(vq.type == VoxelMesh.Quad);
631 if (vq.cull&VoxelMesh.Cull_ZAxisMask) {
632 if (vx.qtype&VoxelMesh.DMV_X) gv.s = u1;
633 if (vx.qtype&VoxelMesh.DMV_Y) gv.t = v1;
634 } else if (vq.cull&VoxelMesh.Cull_XAxisMask) {
635 if (vx.qtype&VoxelMesh.DMV_Y) gv.s = u1;
636 if (vx.qtype&VoxelMesh.DMV_Z) gv.t = v1;
637 } else if (vq.cull&VoxelMesh.Cull_YAxisMask) {
638 if (vx.qtype&VoxelMesh.DMV_X) gv.s = u1;
639 if (vx.qtype&VoxelMesh.DMV_Z) gv.t = v1;
640 } else {
641 assert(0);
644 gv.nx = vq.normal.x;
645 gv.ny = vq.normal.y;
646 gv.nz = vq.normal.z;
647 vxn[nidx] = appendVertex(gv);
650 indicies ~= vxn[0];
651 indicies ~= vxn[1];
652 indicies ~= vxn[2];
653 indicies ~= vxn[3];
654 indicies ~= BreakIndex;
657 tm.stop();
659 conwriteln(vox.quads.length, " quads, ", countTris(), " tris, ",
660 uniqueVerts, " unique vertices (of ", vox.quads.length*4, ")");
661 conwriteln("OpenGL data created in ", tm.toString());
663 if (vox_fix_tjunctions) {
664 tm.restart();
665 fixTJunctions();
666 tm.stop();
667 conwriteln("t-junctions fixed in ", tm.toString());
668 conwriteln("fixed tris: ", countTris());
671 createLines();
675 bool bufloaded = false;
676 uint vvbo, ivbo, lvbo;
677 uint ctxid; // color texture
679 // release OpenGL resources
680 void glRelease () {
681 import iv.glbinds;
682 if (!bufloaded) return;
683 // vertex data
684 glDeleteBuffers(1, &vvbo);
685 // index data
686 glDeleteBuffers(1, &ivbo);
687 glDeleteBuffers(1, &lvbo);
688 // texture
689 glDeleteTextures(1, &ctxid);
690 bufloaded = false;
693 void glUpload () {
694 import iv.glbinds;
695 if (bufloaded) return;
697 glEnable(GL_PRIMITIVE_RESTART);
698 glPrimitiveRestartIndex(BreakIndex);
700 // vertex data
701 glGenBuffers(1, &vvbo);
702 glBindBuffer(GL_ARRAY_BUFFER, vvbo);
703 glBufferData(GL_ARRAY_BUFFER, vertices[0].sizeof*vertices.length, vertices.ptr, GL_STATIC_DRAW);
705 // index data
706 glGenBuffers(1, &ivbo);
707 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ivbo);
708 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicies[0].sizeof*indicies.length, indicies.ptr, GL_STATIC_DRAW);
710 // line index data
711 glGenBuffers(1, &lvbo);
712 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lvbo);
713 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicies[0].sizeof*lindicies.length, lindicies.ptr, GL_STATIC_DRAW);
715 // texture
716 glGenTextures(1, &ctxid);
717 glBindTexture(GL_TEXTURE_2D, ctxid);
718 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
719 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
720 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
721 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
722 GLfloat[4] bclr = 0.0;
723 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
724 // create straight from image
725 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imgWidth, imgHeight, 0, /*GL_RGBA*/GL_BGRA, GL_UNSIGNED_BYTE, img.ptr);
727 bufloaded = true;
730 void draw () {
731 import iv.glbinds;
733 glUpload();
735 //glColor3f(1.0f, 0.5f, 0.0f);
736 //glColor3f(1.0f, 1.0f, 1.0f);
737 glColor3f(1.0f, 1.0f, 1.0f);
738 glBindBuffer(GL_ARRAY_BUFFER, vvbo);
739 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (vox_wireframe ? lvbo : ivbo));
740 glBindTexture(GL_TEXTURE_2D, ctxid);
741 glEnable(GL_TEXTURE_2D);
742 glEnableClientState(GL_VERTEX_ARRAY);
743 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
744 //glEnableClientState(GL_NORMAL_ARRAY);
745 glVertexPointer(3, GL_FLOAT, vertices[0].sizeof, cast(void*)0);
746 glTexCoordPointer(2, GL_FLOAT, vertices[0].sizeof, cast(void*)vertices[0].s.offsetof);
747 if (vox_wireframe) {
748 glDrawElements(GL_LINE_LOOP, cast(uint)lindicies.length, GL_UNSIGNED_INT, cast(void*)0);
749 } else {
750 glDrawElements(GL_TRIANGLE_FAN, cast(uint)indicies.length, GL_UNSIGNED_INT, cast(void*)0);
752 glBindBuffer(GL_ARRAY_BUFFER, 0);
753 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
754 glBindTexture(GL_TEXTURE_2D, 0);
755 glDisable(GL_TEXTURE_2D);
756 glDisableClientState(GL_VERTEX_ARRAY);
757 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
758 //glDisableClientState(GL_NORMAL_ARRAY);