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
60 uint[VVoxVertexEx
] vertcache
;
61 float[3] vmin
; // minimum vertex coords
62 float[3] vmax
; // maximum vertex coords
65 uint imgWidth
, imgHeight
;
68 uint appendVertex (in VVoxVertexEx gv
) {
69 if (auto vp
= gv
in vertcache
) return *vp
;
71 immutable uint res
= cast(uint)vertices
.length
;
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
;
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
;
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
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
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
144 void freeSortStructs () {
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]);
174 void putVertexToGrid (uint vidx
) {
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 {
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
195 for (uint f
= 0; f
< cast(uint)edges
.length
; ++f
) putEdgeToGrid(f
);
198 // create list of edges
199 void createEdges () {
201 edges
.length
= cast(uint)indicies
.length
/5U*4U; // one quad is 4 edges
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
);
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
);
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
);
225 e
.clo
= vertices
[e
.v0
].get(e
.axis
);
226 e
.chi
= vertices
[e
.v1
].get(e
.axis
);
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)
235 assert(eidx
== cast(uint)edges
.length
);
236 conwriteln(uqcount
, " unit quad", (uqcount
!= 1 ?
"s" : ""), " found.");
239 void fixEdgeWithVert (ref VoxEdge edge
, float crd
) {
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
;
248 nvx
.set(edge
.axis
, crd
);
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
;
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;
283 newind
.length
= newindcount
;
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)
302 firstGoodFace
= cast(int)c
;
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
;
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
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
;
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;
361 newind
[newindcount
++] = edges
[f
+noic
].v1
;
362 newind
[newindcount
++] = BreakIndex
;
366 newind
[newindcount
++] = BreakIndex
;
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
;
389 xequal
= xequal
&& (prevx
== vx
);
390 yequal
= yequal
&& (prevy
== vy
);
391 zequal
= zequal
&& (prevz
== vz
);
403 // determine quad orientation
405 if (xequal
) axis
= AXIS_X
;
406 else if (yequal
) axis
= AXIS_Y
;
407 else if (zequal
) axis
= AXIS_Z
;
411 //float s = vertices[edges[f+0].v0].s;
412 //float t = vertices[edges[f+0].v0].t;
416 // append center vertex
417 VVoxVertexEx nvx
= vertices
[edges
[f
+0].v0
];
425 newind
[newindcount
++] = appendVertex(nvx
);
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
;
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
;
449 indicies
= newind
[0..newindcount
];
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
;
458 conwriteln(edges
.length
, " edges found (", edges
.length
/4*2, " tris, ", vertices
.length
, " verts)...");
460 for (uint f
= 0; f
< cast(uint)edges
.length
; ++f
) fixEdgeNew(f
);
463 conwriteln(totaltadded
, " t-fix vertices added (", vertices
.length
-oldvtotal
, " unique).");
465 conwriteln("rebuilt model: ", countTris(), " tris, ", vertices
.length
, " vertices.");
469 foreach (ref VoxEdge ee
; edges
) {
470 delete ee
.moreverts
; ee
.moreverts
= null;
472 delete edges
; edges
= null;
477 delete vertices
; vertices
= null;
478 delete indicies
; indicies
= null;
479 delete lindicies
; lindicies
= null;
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;
495 // count the number of triangles in triangle fan data
496 // used for informational messages
500 while (ind
< cast(uint)indicies
.length
) {
501 assert(indicies
[ind
] != BreakIndex
);
503 while (end
< cast(uint)indicies
.length
&& indicies
[end
] != BreakIndex
) ++end
;
504 assert(end
< indicies
.length
);
505 assert(end
-ind
>= 3);
509 } else if (end
-ind
== 4) {
522 // create lines for wireframe view
523 void createLines () {
524 lindicies
.length
= 0;
525 lindicies
.assumeSafeAppend
;
528 while (ind
< cast(uint)indicies
.length
) {
529 assert(indicies
[ind
] != BreakIndex
);
531 while (end
< cast(uint)indicies
.length
&& indicies
[end
] != BreakIndex
) ++end
;
532 assert(end
< indicies
.length
);
533 assert(end
-ind
>= 3);
536 lindicies
~= indicies
[ind
+0];
537 lindicies
~= indicies
[ind
+1];
538 lindicies
~= indicies
[ind
+2];
539 lindicies
~= BreakIndex
;
540 } else if (end
-ind
== 4) {
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
;
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
;
566 void create (VoxelMesh vox
) {
570 imgWidth
= vox
.catlas
.getWidth();
571 imgHeight
= vox
.catlas
.getHeight();
572 img
= new uint[imgWidth
*imgHeight
];
573 img
[] = vox
.catlas
.colors
[];
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
]);
587 auto tm
= iv
.timer
.Timer(true);
589 // calculate quad normals
590 foreach (ref VoxelMesh
.VoxQuad vq
; vox
.quads
[]) {
593 conwriteln("color texture size: ", imgWidth
, "x", imgHeight
);
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;
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
];
615 if (vq
.type
== VoxelMesh
.ZLong
) {
616 gv
.s
= (vx
.dz ? u1
: u0
);
618 } else if (vq
.type
== VoxelMesh
.XLong
) {
619 gv
.s
= (vx
.dx ? u1
: u0
);
621 } else if (vq
.type
== VoxelMesh
.YLong
) {
622 gv
.s
= (vx
.dy ? u1
: u0
);
624 } else if (vq
.type
== VoxelMesh
.Point
) {
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
;
647 vxn
[nidx
] = appendVertex(gv
);
654 indicies
~= BreakIndex
;
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
) {
667 conwriteln("t-junctions fixed in ", tm
.toString());
668 conwriteln("fixed tris: ", countTris());
675 bool bufloaded
= false;
676 uint vvbo
, ivbo
, lvbo
;
677 uint ctxid
; // color texture
679 // release OpenGL resources
682 if (!bufloaded
) return;
684 glDeleteBuffers(1, &vvbo
);
686 glDeleteBuffers(1, &ivbo
);
687 glDeleteBuffers(1, &lvbo
);
689 glDeleteTextures(1, &ctxid
);
695 if (bufloaded
) return;
697 glEnable(GL_PRIMITIVE_RESTART
);
698 glPrimitiveRestartIndex(BreakIndex
);
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
);
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
);
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
);
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
);
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
);
748 glDrawElements(GL_LINE_LOOP
, cast(uint)lindicies
.length
, GL_UNSIGNED_INT
, cast(void*)0);
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);