4 import iv
.glbinds
.utils
;
11 static import iv
.timer
;
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.
34 static align(1) struct VVoxVertexEx
{
38 float nx
, ny
, nz
; // normal
40 float get (uint idx
) const nothrow @safe @nogc {
43 return (idx
== 0 ? x
: idx
== 1 ? y
: z
);
46 void set (uint idx
, const float v
) nothrow @safe @nogc {
50 else if (idx
== 1) y
= v
;
55 VVoxVertexEx
[] vertices
;
57 uint[] lindicies
; // lines
61 uint[VVoxVertexEx
] vertcache
;
62 float[3] vmin
; // minimum vertex coords
63 float[3] vmax
; // maximum vertex coords
66 uint imgWidth
, imgHeight
;
69 void save (VFile fl
) {
70 fl
.rawWriteExact("K8VOXGL0");
72 fl
.writeNum
!uint(cast(uint)BreakIndex
);
75 fl
.writeNum
!ushort(cast(ushort)imgWidth
);
76 fl
.writeNum
!ushort(cast(ushort)imgHeight
);
77 fl
.rawWriteExact(img
[0..imgWidth
*imgHeight
]);
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
);
93 fl
.writeNum
!uint(cast(uint)indicies
.length
);
94 fl
.rawWriteExact(indicies
[]);
97 fl
.writeNum
!uint(cast(uint)lindicies
.length
);
98 fl
.rawWriteExact(lindicies
[]);
101 void load (VFile fl
) {
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");
112 imgWidth
= fl
.readNum
!ushort;
113 imgHeight
= fl
.readNum
!ushort;
115 img
= new uint[imgWidth
*imgHeight
];
116 fl
.rawReadExact(img
[]);
119 uint tvcount
= fl
.readNum
!uint;
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;
134 uint indcount
= fl
.readNum
!uint;
136 indicies
= new uint[indcount
];
137 fl
.rawReadExact(indicies
[]);
140 uint lindcount
= fl
.readNum
!uint;
142 lindicies
= new uint[lindcount
];
143 fl
.rawReadExact(lindicies
[]);
149 uint appendVertex (in VVoxVertexEx gv
) {
151 if (auto vp
= gv
in vertcache
) return *vp
;
153 immutable uint res
= cast(uint)vertices
.length
;
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
;
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
;
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
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
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
226 void freeSortStructs () {
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]);
256 void putVertexToGrid (uint vidx
) {
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 {
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
277 for (uint f
= 0; f
< cast(uint)edges
.length
; ++f
) putEdgeToGrid(f
);
280 // create list of edges
281 void createEdges () {
283 edges
.length
= cast(uint)indicies
.length
/5U*4U; // one quad is 4 edges
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
);
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
);
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
);
307 e
.clo
= vertices
[e
.v0
].get(e
.axis
);
308 e
.chi
= vertices
[e
.v1
].get(e
.axis
);
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)
317 assert(eidx
== cast(uint)edges
.length
);
318 conwriteln(uqcount
, " unit quad", (uqcount
!= 1 ?
"s" : ""), " found.");
321 void fixEdgeWithVert (ref VoxEdge edge
, float crd
) {
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
;
330 nvx
.set(edge
.axis
, crd
);
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
);
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;
365 newind
.length
= newindcount
;
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)
384 firstGoodFace
= cast(int)c
;
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
;
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
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 // append additional vertices (they are already properly sorted)
428 for (uint tmpf
= 0; tmpf
< cast(uint)edges
[f
+eic
].moreverts
.length
; ++tmpf
) {
429 newind
[newindcount
++] = edges
[f
+eic
].moreverts
[tmpf
];
431 // and the last vertex
432 newind
[newindcount
++] = edges
[f
+eic
].v1
;
433 // if the opposite side is not modified, we can finish the fan right now
434 const uint loic
= (eic
+2)&3;
435 if (edges
[f
+loic
].moreverts
.length
== 0) {
436 const uint noic
= (eic
+1)&3;
441 newind
[newindcount
++] = edges
[f
+noic
].v1
;
442 newind
[newindcount
++] = BreakIndex
;
446 newind
[newindcount
++] = BreakIndex
;
451 // alas, this quad should be converted to "centroid quad"
452 // i.e. we will use quad center point to start a triangle fan
454 // calculate quad center point
455 float cx
= 0.0f, cy
= 0.0f, cz
= 0.0f;
456 float cs
= 0.0f, ct
= 0.0f;
457 float prevx
= 0.0f, prevy
= 0.0f, prevz
= 0.0f;
458 bool xequal
= true, yequal
= true, zequal
= true;
459 for (uint eic
= 0; eic
< 4; ++eic
) {
460 cs
+= (vertices
[edges
[f
+eic
].v0
].s
+vertices
[edges
[f
+eic
].v1
].s
)*0.5f;
461 ct
+= (vertices
[edges
[f
+eic
].v0
].t
+vertices
[edges
[f
+eic
].v1
].t
)*0.5f;
462 const float vx
= vertices
[edges
[f
+eic
].v0
].x
;
463 const float vy
= vertices
[edges
[f
+eic
].v0
].y
;
464 const float vz
= vertices
[edges
[f
+eic
].v0
].z
;
469 xequal
= xequal
&& (prevx
== vx
);
470 yequal
= yequal
&& (prevy
== vy
);
471 zequal
= zequal
&& (prevz
== vz
);
483 // determine quad orientation
486 if (xequal) axis = AXIS_X;
487 else if (yequal) axis = AXIS_Y;
488 else if (zequal) axis = AXIS_Z;
493 //float s = vertices[edges[f+0].v0].s;
494 //float t = vertices[edges[f+0].v0].t;
498 // append center vertex
499 VVoxVertexEx nvx
= vertices
[edges
[f
+0].v0
];
507 newind
[newindcount
++] = appendVertex(nvx
);
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
;
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
;
531 indicies
= newind
[0..newindcount
];
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
;
540 conwriteln(edges
.length
, " edges found (", edges
.length
/4*2, " tris, ", vertices
.length
, " verts)...");
542 for (uint f
= 0; f
< cast(uint)edges
.length
; ++f
) fixEdgeNew(f
);
545 conwriteln(totaltadded
, " t-fix vertices added (", vertices
.length
-oldvtotal
, " unique).");
547 conwriteln("rebuilt model: ", countTris(), " tris, ", vertices
.length
, " vertices.");
551 foreach (ref VoxEdge ee
; edges
) {
552 delete ee
.moreverts
; ee
.moreverts
= null;
554 delete edges
; edges
= null;
558 bool isEmpty () const pure { pragma(inline
, true); return (indicies
.length
== 0); }
561 delete vertices
; vertices
= null;
562 delete indicies
; indicies
= null;
563 delete lindicies
; lindicies
= null;
568 // our voxels are 1024x1024x1024 at max
569 vmin
[0] = vmin
[1] = vmin
[2] = +8192.0f;
570 vmax
[0] = vmax
[1] = vmax
[2] = -8192.0f;
572 delete img
; img
= null;
573 imgWidth
= imgHeight
= 0;
580 // count the number of triangles in triangle fan data
581 // used for informational messages
585 while (ind
< cast(uint)indicies
.length
) {
586 assert(indicies
[ind
] != BreakIndex
);
588 while (end
< cast(uint)indicies
.length
&& indicies
[end
] != BreakIndex
) ++end
;
589 assert(end
< indicies
.length
);
590 assert(end
-ind
>= 3);
594 } else if (end
-ind
== 4) {
607 // create lines for wireframe view
608 void createLines () {
609 lindicies
.length
= 0;
610 lindicies
.assumeSafeAppend
;
613 while (ind
< cast(uint)indicies
.length
) {
614 assert(indicies
[ind
] != BreakIndex
);
616 while (end
< cast(uint)indicies
.length
&& indicies
[end
] != BreakIndex
) ++end
;
617 assert(end
< indicies
.length
);
618 assert(end
-ind
>= 3);
621 lindicies
~= indicies
[ind
+0];
622 lindicies
~= indicies
[ind
+1];
623 lindicies
~= indicies
[ind
+2];
624 lindicies
~= BreakIndex
;
625 } else if (end
-ind
== 4) {
627 lindicies
~= indicies
[ind
+0];
628 lindicies
~= indicies
[ind
+1];
629 lindicies
~= indicies
[ind
+2];
630 lindicies
~= BreakIndex
;
632 lindicies
~= indicies
[ind
+2];
633 lindicies
~= indicies
[ind
+3];
634 lindicies
~= indicies
[ind
+0];
635 lindicies
~= BreakIndex
;
638 for (int f
= ind
+1; f
< end
-1; ++f
) {
639 lindicies
~= indicies
[ind
+0];
640 lindicies
~= indicies
[f
+0];
641 lindicies
~= indicies
[f
+1];
642 lindicies
~= BreakIndex
;
649 private static float calcTX (uint cp
, int pos
, uint sz
) nothrow @safe @nogc {
650 pragma(inline
, true);
651 float fp
= cast(float)cast(int)cp
;
652 if (pos
< 0) fp
+= 0.004f;
653 else if (pos
> 0) fp
+= 0.996f;
655 return fp
/cast(float)cast(int)sz
;
658 // pos: <0 is left; 0 is center; >0 is right
659 private float calcS (ref VoxelMesh vox
, const ref VoxQuad vq
, int pos
) const nothrow @safe @nogc {
660 uint cp
= vox
.catlas
.getTexX(vq
.cidx
);
661 if (pos
> 0) cp
+= vq
.wh
.getW()-1;
662 return calcTX(cp
, pos
, imgWidth
);
665 private float calcT (ref VoxelMesh vox
, const ref VoxQuad vq
, int pos
) const nothrow @safe @nogc {
666 uint cp
= vox
.catlas
.getTexY(vq
.cidx
);
667 if (pos
> 0) cp
+= vq
.wh
.getH()-1;
668 return calcTX(cp
, pos
, imgHeight
);
672 void create (ref VoxelMesh vox
) {
675 imgWidth
= vox
.catlas
.getWidth();
676 imgHeight
= vox
.catlas
.getHeight();
677 img
= new uint[imgWidth
*imgHeight
];
678 img
[] = vox
.catlas
.colors
[];
681 auto fo
= VFile("zzz.raw", "w");
682 fo
.writeln(imgWidth
, " ", imgHeight
);
683 foreach (uint dy
; 0..imgHeight
) {
684 foreach (uint dx
; 0..imgWidth
) {
685 if (dx
) fo
.write(" ");
686 fo
.writef("%08X", img
[dy
*imgWidth
+dx
]);
692 auto tm
= iv
.timer
.Timer(true);
694 // calculate quad normals
695 foreach (ref VoxQuad vq
; vox
.quads
[]) {
698 conwriteln("color texture size: ", imgWidth
, "x", imgHeight
);
701 foreach (ref VoxQuad vq
; vox
.quads
[]) {
704 VVoxVertexEx gv
= void;
705 foreach (uint nidx
; 0..4) {
706 const VoxQuadVertex
*vx
= &vq
.vx
[nidx
];
710 if (vq
.type
== VoxelMesh
.ZLong
) {
711 gv
.s
= calcS(vox
, vq
, (vx
.dz ?
1 : -1));
712 gv
.t
= calcT(vox
, vq
, 0);
713 } else if (vq
.type
== VoxelMesh
.XLong
) {
714 gv
.s
= calcS(vox
, vq
, (vx
.dx ?
1 : -1));
715 gv
.t
= calcT(vox
, vq
, 0);
716 } else if (vq
.type
== VoxelMesh
.YLong
) {
717 gv
.s
= calcS(vox
, vq
, (vx
.dy ?
1 : -1));
718 gv
.t
= calcT(vox
, vq
, 0);
719 } else if (vq
.type
== VoxelMesh
.Point
) {
720 gv
.s
= calcS(vox
, vq
, 0);
721 gv
.t
= calcT(vox
, vq
, 0);
723 int spos
= -1, tpos
= -1;
724 assert(vq
.type
== VoxelMesh
.Quad
);
725 if (vq
.cull
&VoxelMesh
.Cull_ZAxisMask
) {
726 if (vx
.qtype
&VoxelMesh
.DMV_X
) spos
= 1;
727 if (vx
.qtype
&VoxelMesh
.DMV_Y
) tpos
= 1;
728 } else if (vq
.cull
&VoxelMesh
.Cull_XAxisMask
) {
729 if (vx
.qtype
&VoxelMesh
.DMV_Y
) spos
= 1;
730 if (vx
.qtype
&VoxelMesh
.DMV_Z
) tpos
= 1;
731 } else if (vq
.cull
&VoxelMesh
.Cull_YAxisMask
) {
732 if (vx
.qtype
&VoxelMesh
.DMV_X
) spos
= 1;
733 if (vx
.qtype
&VoxelMesh
.DMV_Z
) tpos
= 1;
737 gv
.s
= calcS(vox
, vq
, spos
);
738 gv
.t
= calcT(vox
, vq
, tpos
);
743 vxn
[nidx
] = appendVertex(gv
);
750 indicies
~= BreakIndex
;
755 conwriteln(vox
.quads
.length
, " quads, ", countTris(), " tris, ",
756 uniqueVerts
, " unique vertices (of ", vox
.quads
.length
*4, ")");
757 conwriteln("OpenGL data created in ", tm
.toString());
759 if (vox_fix_tjunctions
) {
763 conwriteln("t-junctions fixed in ", tm
.toString());
764 conwriteln("fixed tris: ", countTris());
772 bool bufloaded
= false;
773 uint vvbo
, ivbo
, lvbo
;
774 uint ctxid
; // color texture
776 // release OpenGL resources
779 if (!bufloaded
) return;
781 glDeleteBuffers(1, &vvbo
);
783 glDeleteBuffers(1, &ivbo
);
784 glDeleteBuffers(1, &lvbo
);
786 glDeleteTextures(1, &ctxid
);
792 if (bufloaded
) return;
794 glEnable(GL_PRIMITIVE_RESTART
);
795 glPrimitiveRestartIndex(BreakIndex
);
798 glGenBuffers(1, &vvbo
);
799 glBindBuffer(GL_ARRAY_BUFFER
, vvbo
);
800 glBufferData(GL_ARRAY_BUFFER
, vertices
[0].sizeof
*vertices
.length
, vertices
.ptr
, GL_STATIC_DRAW
);
803 glGenBuffers(1, &ivbo
);
804 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, ivbo
);
805 glBufferData(GL_ELEMENT_ARRAY_BUFFER
, indicies
[0].sizeof
*indicies
.length
, indicies
.ptr
, GL_STATIC_DRAW
);
808 glGenBuffers(1, &lvbo
);
809 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, lvbo
);
810 glBufferData(GL_ELEMENT_ARRAY_BUFFER
, indicies
[0].sizeof
*lindicies
.length
, lindicies
.ptr
, GL_STATIC_DRAW
);
813 glGenTextures(1, &ctxid
);
814 glBindTexture(GL_TEXTURE_2D
, ctxid
);
815 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
816 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
817 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
818 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
819 GLfloat
[4] bclr
= 0.0;
820 glTexParameterfv(GL_TEXTURE_2D
, GL_TEXTURE_BORDER_COLOR
, bclr
.ptr
);
821 // create straight from image
822 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, imgWidth
, imgHeight
, 0, /*GL_RGBA*/GL_BGRA
, GL_UNSIGNED_BYTE
, img
.ptr
);
832 //glColor3f(1.0f, 0.5f, 0.0f);
833 //glColor3f(1.0f, 1.0f, 1.0f);
834 glColor3f(1.0f, 1.0f, 1.0f);
835 glBindBuffer(GL_ARRAY_BUFFER
, vvbo
);
836 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, (vox_wireframe ? lvbo
: ivbo
));
837 glBindTexture(GL_TEXTURE_2D
, ctxid
);
838 glEnable(GL_TEXTURE_2D
);
839 glEnableClientState(GL_VERTEX_ARRAY
);
840 glEnableClientState(GL_TEXTURE_COORD_ARRAY
);
841 //glEnableClientState(GL_NORMAL_ARRAY);
842 glVertexPointer(3, GL_FLOAT
, vertices
[0].sizeof
, cast(void*)0);
843 glTexCoordPointer(2, GL_FLOAT
, vertices
[0].sizeof
, cast(void*)vertices
[0].s
.offsetof
);
845 glDrawElements(GL_LINE_LOOP
, cast(uint)lindicies
.length
, GL_UNSIGNED_INT
, cast(void*)0);
847 glDrawElements(GL_TRIANGLE_FAN
, cast(uint)indicies
.length
, GL_UNSIGNED_INT
, cast(void*)0);
849 glBindBuffer(GL_ARRAY_BUFFER
, 0);
850 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, 0);
851 glBindTexture(GL_TEXTURE_2D
, 0);
852 glDisable(GL_TEXTURE_2D
);
853 glDisableClientState(GL_VERTEX_ARRAY
);
854 glDisableClientState(GL_TEXTURE_COORD_ARRAY
);
855 //glDisableClientState(GL_NORMAL_ARRAY);