2 // shamelessly ripped from FreeGLUT (except cube)
3 module iv
.glbinds
.shape
/*is aliced*/;
4 nothrow @trusted @nogc:
10 * Compute lookup table of cos and sin values forming a circle
11 * (or half circle if halfCircle==TRUE)
14 * It is the responsibility of the caller to free these tables
15 * The size of the table is (n+1) to form a connected loop
16 * The last entry is exactly the same as the first
17 * The sign of n can be flipped to get the reverse loop
19 private void fghCircleTable() (GLfloat
** sint
, GLfloat
** cost
, in int n
, in bool halfCircle
) {
20 import core
.stdc
.stdlib
: malloc
, free
;
23 // table size, the sign of n flips the circle direction
24 immutable int size
= abs(n
);
26 // determine the angle between samples
27 immutable GLfloat angle
= (halfCircle ?
1 : 2)*cast(GLfloat
)PI
/cast(GLfloat
)(n
== 0 ?
1 : n
);
29 // allocate memory for n samples, plus duplicate of first entry at the end
30 *sint
= cast(GLfloat
*)malloc(GLfloat
.sizeof
*(size
+1));
31 *cost
= cast(GLfloat
*)malloc(GLfloat
.sizeof
*(size
+1));
33 // bail out if memory allocation fails, fgError never returns
34 if (*sint
is null ||
*cost
is null) {
37 assert(0, "out of memory");
40 // compute cos and sin around the circle
44 foreach (immutable int i
; 1..size
) {
45 (*sint
)[i
] = cast(GLfloat
)sin(angle
*i
);
46 (*cost
)[i
] = cast(GLfloat
)cos(angle
*i
);
50 (*sint
)[size
] = 0.0f; // sin PI
51 (*cost
)[size
] = -1.0f; // cos PI
53 // last sample is duplicate of the first (sin or cos of 2 PI)
54 (*sint
)[size
] = (*sint
)[0];
55 (*cost
)[size
] = (*cost
)[0];
60 private void fghGenerateSphere() (GLfloat radius
, GLint slices
, GLint stacks
, GLfloat
** vertices
, GLfloat
** normals
, int* nVert
) {
61 import core
.stdc
.stdlib
: malloc
, free
;
63 int idx
= 0; // idx into vertex/normal buffer
66 // pre-computed circle
67 GLfloat
* sint1
, cost1
;
68 GLfloat
* sint2
, cost2
;
70 // number of unique vertices
71 if (slices
== 0 || stacks
< 2) {
72 // nothing to generate
77 *nVert
= slices
*(stacks
-1)+2;
80 // limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above
81 assert(0, "too many slices or stacks requested, indices will wrap");
84 // precompute values on unit circle
85 fghCircleTable(&sint1
, &cost1
, -slices
, false);
86 fghCircleTable(&sint2
, &cost2
, stacks
, true);
88 // allocate vertex and normal buffers, bail out if memory allocation fails
89 *vertices
= cast(GLfloat
*)malloc((*nVert
)*3*GLfloat
.sizeof
);
90 *normals
= cast(GLfloat
*)malloc((*nVert
)*3*GLfloat
.sizeof
);
91 if (*vertices
is null ||
*normals
is null) {
94 assert(0, "out of memory");
98 (*vertices
)[0] = 0.0f;
99 (*vertices
)[1] = 0.0f;
100 (*vertices
)[2] = radius
;
101 (*normals
)[0] = 0.0f;
102 (*normals
)[1] = 0.0f;
103 (*normals
)[2] = 1.0f;
107 foreach (immutable int i
; 1..stacks
) {
108 foreach (immutable int j
; 0..slices
) {
109 x
= cost1
[j
]*sint2
[i
];
110 y
= sint1
[j
]*sint2
[i
];
113 (*vertices
)[idx
+0] = x
*radius
;
114 (*vertices
)[idx
+1] = y
*radius
;
115 (*vertices
)[idx
+2] = z
*radius
;
116 (*normals
)[idx
+0] = x
;
117 (*normals
)[idx
+1] = y
;
118 (*normals
)[idx
+2] = z
;
125 (*vertices
)[idx
+0] = 0.0f;
126 (*vertices
)[idx
+1] = 0.0f;
127 (*vertices
)[idx
+2] = -radius
;
128 (*normals
)[idx
+0] = 0.0f;
129 (*normals
)[idx
+1] = 0.0f;
130 (*normals
)[idx
+2] = -1.0f;
132 // done creating vertices, release sin and cos tables
140 private void fghGenerateCone() (
141 GLfloat base
, GLfloat height
, GLint slices
, GLint stacks
, // input
142 GLfloat
** vertices
, GLfloat
** normals
, int* nVert
// output
144 import core
.stdc
.stdlib
: malloc
, free
;
147 int idx
= 0; // idx into vertex/normal buffer
149 // pre-computed circle
152 // step in z and radius as stacks are drawn.
154 GLfloat r
= cast(GLfloat
)base
;
156 immutable GLfloat zStep
= cast(GLfloat
)height
/(stacks
> 0 ? stacks
: 1);
157 immutable GLfloat rStep
= cast(GLfloat
)base
/(stacks
> 0 ? stacks
: 1);
159 // scaling factors for vertex normals
160 immutable GLfloat cosn
= cast(GLfloat
)(height
/sqrt(height
*height
+base
*base
));
161 immutable GLfloat sinn
= cast(GLfloat
)(base
/sqrt(height
*height
+base
*base
));
163 // number of unique vertices
164 if (slices
== 0 || stacks
< 1) {
165 // nothing to generate
169 *nVert
= slices
*(stacks
+2)+1; // need an extra stack for closing off bottom with correct normals
171 if (*nVert
> 65535) {
172 // limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above
173 assert(0, "too many slices or stacks requested, indices will wrap");
176 // pre-computed circle
177 fghCircleTable(&sint
, &cost
, -slices
, false);
179 // allocate vertex and normal buffers, bail out if memory allocation fails
180 *vertices
= cast(GLfloat
*)malloc((*nVert
)*3*GLfloat
.sizeof
);
181 *normals
= cast(GLfloat
*)malloc((*nVert
)*3*GLfloat
.sizeof
);
182 if (*vertices
is null ||
*normals
is null) {
185 assert(0, "out of memory");
189 (*vertices
)[0] = 0.0f;
190 (*vertices
)[1] = 0.0f;
192 (*normals
)[0] = 0.0f;
193 (*normals
)[1] = 0.0f;
194 (*normals
)[2] = -1.0f;
197 // other on bottom (get normals right)
198 foreach (immutable int j
; 0..slices
) {
199 (*vertices
)[idx
+0] = cost
[j
]*r
;
200 (*vertices
)[idx
+1] = sint
[j
]*r
;
201 (*vertices
)[idx
+2] = z
;
202 (*normals
)[idx
+0] = 0.0f;
203 (*normals
)[idx
+1] = 0.0f;
204 (*normals
)[idx
+2] = -1.0f;
209 foreach (immutable int i
; 0..stacks
+1) {
210 foreach (immutable int j
; 0..slices
) {
211 (*vertices
)[idx
+0] = cost
[j
]*r
;
212 (*vertices
)[idx
+1] = sint
[j
]*r
;
213 (*vertices
)[idx
+2] = z
;
214 (*normals
)[idx
+0] = cost
[j
]*cosn
;
215 (*normals
)[idx
+1] = sint
[j
]*cosn
;
216 (*normals
)[idx
+2] = sinn
;
223 // release sin and cos tables
229 private void fghGenerateCylinder() (
230 GLfloat radius
, GLfloat height
, GLint slices
, GLint stacks
, // input
231 GLfloat
** vertices
, GLfloat
** normals
, int* nVert
// output
233 import core
.stdc
.stdlib
: malloc
, free
;
236 int idx
= 0; // idx into vertex/normal buffer
238 // step in z as stacks are drawn
239 GLfloat radf
= cast(GLfloat
)radius
;
240 immutable GLfloat zStep
= cast(GLfloat
)height
/(stacks
> 0 ? stacks
: 1);
242 // pre-computed circle
245 // number of unique vertices
246 if (slices
== 0 || stacks
< 1) {
247 // nothing to generate
251 *nVert
= slices
*(stacks
+3)+2; // need two extra stacks for closing off top and bottom with correct normals
253 if (*nVert
> 65535) {
254 // limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above
255 assert(0, "too many slices or stacks requested, indices will wrap");
258 // pre-computed circle
259 fghCircleTable(&sint
, &cost
, -slices
, false);
261 // Allocate vertex and normal buffers, bail out if memory allocation fails
262 *vertices
= cast(GLfloat
*)malloc((*nVert
)*3*GLfloat
.sizeof
);
263 *normals
= cast(GLfloat
*)malloc((*nVert
)*3*GLfloat
.sizeof
);
264 if (*vertices
is null ||
*normals
is null) {
267 assert(0, "out of memory");
272 (*vertices
)[0] = 0.0f;
273 (*vertices
)[1] = 0.0f;
274 (*vertices
)[2] = 0.0f;
275 (*normals
)[0] = 0.0f;
276 (*normals
)[1] = 0.0f;
277 (*normals
)[2] = -1.0f;
279 // other on top (get normals right)
280 foreach (immutable int j
; 0..slices
) {
281 (*vertices
)[idx
+0] = cost
[j
]*radf
;
282 (*vertices
)[idx
+1] = sint
[j
]*radf
;
283 (*vertices
)[idx
+2] = z
;
284 (*normals
)[idx
+0] = 0.0f;
285 (*normals
)[idx
+1] = 0.0f;
286 (*normals
)[idx
+2] = -1.0f;
291 foreach (immutable int i
; 0..stacks
+1) {
292 foreach (immutable int j
; 0..slices
) {
293 (*vertices
)[idx
+0] = cost
[j
]*radf
;
294 (*vertices
)[idx
+1] = sint
[j
]*radf
;
295 (*vertices
)[idx
+2] = z
;
296 (*normals
)[idx
+0] = cost
[j
];
297 (*normals
)[idx
+1] = sint
[j
];
298 (*normals
)[idx
+2] = 0.0f;
304 // other on bottom (get normals right)
306 foreach (immutable int j
; 0..slices
) {
307 (*vertices
)[idx
+0] = cost
[j
]*radf
;
308 (*vertices
)[idx
+1] = sint
[j
]*radf
;
309 (*vertices
)[idx
+2] = z
;
310 (*normals
)[idx
+0] = 0.0f;
311 (*normals
)[idx
+1] = 0.0f;
312 (*normals
)[idx
+2] = 1.0f;
317 (*vertices
)[idx
+0] = 0.0f;
318 (*vertices
)[idx
+1] = 0.0f;
319 (*vertices
)[idx
+2] = height
;
320 (*normals
)[idx
+0] = 0.0f;
321 (*normals
)[idx
+1] = 0.0f;
322 (*normals
)[idx
+2] = 1.0f;
324 // Release sin and cos tables
330 private void fghGenerateTorus() (
331 double dInnerRadius
, double dOuterRadius
, GLint nSides
, GLint nRings
, // input
332 GLfloat
** vertices
, GLfloat
** normals
, int* nVert
// output
335 import core
.stdc
.stdlib
: malloc
, free
;
338 GLfloat iradius
= cast(GLfloat
)dInnerRadius
;
339 GLfloat oradius
= cast(GLfloat
)dOuterRadius
;
341 // pre-computed circle
345 // number of unique vertices
346 if (nSides
< 2 || nRings
< 2) {
347 // nothing to generate
351 *nVert
= nSides
*nRings
;
353 if (*nVert
> 65535) {
354 // limit of glushort, thats 256*256 subdivisions, should be enough in practice. See note above
355 assert(0, "too many slices or stacks requested, indices will wrap");
358 // precompute values on unit circle
359 fghCircleTable(&spsi
, &cpsi
, nRings
, false);
360 fghCircleTable(&sphi
, &cphi
, -nSides
, false);
362 // Allocate vertex and normal buffers, bail out if memory allocation fails
363 *vertices
= cast(GLfloat
*)malloc((*nVert
)*3*GLfloat
.sizeof
);
364 *normals
= cast(GLfloat
*)malloc((*nVert
)*3*GLfloat
.sizeof
);
365 if (*vertices
is null ||
*normals
is null) {
368 assert(0, "out of memory");
371 foreach (immutable int j
; 0..nRings
) {
372 foreach (immutable int i
; 0..nSides
) {
373 int offset
= 3*(j
*nSides
+i
);
375 (*vertices
)[offset
+0] = cpsi
[j
]*(oradius
+cphi
[i
]*iradius
);
376 (*vertices
)[offset
+1] = spsi
[j
]*(oradius
+cphi
[i
]*iradius
);
377 (*vertices
)[offset
+2] = sphi
[i
]*iradius
;
378 (*normals
)[offset
+0] = cpsi
[j
]*cphi
[i
];
379 (*normals
)[offset
+1] = spsi
[j
]*cphi
[i
];
380 (*normals
)[offset
+2] = sphi
[i
];
384 // release sin and cos tables
392 private void fghDrawGeometrySolid (
393 GLfloat
* vertices
, GLfloat
* normals
, GLfloat
* textcs
, GLsizei numVertices
,
394 GLushort
* vertIdxs
, GLsizei numParts
, GLsizei numVertIdxsPerPart
,
396 glEnableClientState(GL_VERTEX_ARRAY
);
397 glEnableClientState(GL_NORMAL_ARRAY
);
399 glVertexPointer(3, GL_FLOAT
, 0, vertices
);
400 glNormalPointer(GL_FLOAT
, 0, normals
);
402 if (textcs
!is null) {
403 glEnableClientState(GL_TEXTURE_COORD_ARRAY
);
404 glTexCoordPointer(2, GL_FLOAT
, 0, textcs
);
407 if (vertIdxs
is null) {
408 glDrawArrays(GL_TRIANGLES
, 0, numVertices
);
411 foreach (immutable i
; 0..numParts
) glDrawElements(GL_TRIANGLE_STRIP
, numVertIdxsPerPart
, GL_UNSIGNED_SHORT
, vertIdxs
+i
*numVertIdxsPerPart
);
413 glDrawElements(GL_TRIANGLES
, numVertIdxsPerPart
, GL_UNSIGNED_SHORT
, vertIdxs
);
417 glDisableClientState(GL_VERTEX_ARRAY
);
418 glDisableClientState(GL_NORMAL_ARRAY
);
419 if (textcs
!is null) glDisableClientState(GL_TEXTURE_COORD_ARRAY
);
423 public void oglShapeSolidCube (GLdouble dSize
) {
424 // vertex coordinates
425 static immutable GLfloat
[24] cube_verts
= [
437 static immutable GLfloat
[18] cube_norms
= [
446 // vertex indices, as quads, before triangulation
447 static immutable GLushort
[24] cube_vertIdxs
= [
457 foreach (immutable fidx
; 0..6) {
458 glNormal3fv(&cube_norms
[fidx
*3]);
459 foreach (immutable vidx
; fidx
*4..fidx
*4+4) {
460 int vpi
= cube_vertIdxs
[vidx
]*3;
461 GLfloat
[3] v
= cube_verts
[vpi
..vpi
+3];
462 foreach (ref vv
; v
[]) vv
*= dSize
;
470 public void oglShapeSolidSphere() (GLfloat radius
, GLint slices
, GLint stacks
) {
471 import core
.stdc
.stdlib
: malloc
, free
;
474 int i
, j
, idx
, nVert
;
475 GLfloat
* vertices
, normals
;
477 // generate vertices and normals
478 fghGenerateSphere(radius
, slices
, stacks
, &vertices
, &normals
, &nVert
);
480 if (nVert
== 0) return;
482 /* First, generate vertex index arrays for drawing with glDrawElements
483 * All stacks, including top and bottom are covered with a triangle strip.
486 // create index vector
489 // allocate buffers for indices, bail out if memory allocation fails
490 stripIdx
= cast(GLushort
*)malloc((slices
+1)*2*(stacks
)*GLushort
.sizeof
);
491 if (stripIdx
is null) assert(0, "out of memory");
494 for (j
= 0, idx
= 0; j
< slices
; ++j
, idx
+= 2) {
495 stripIdx
[idx
+0] = cast(GLushort
)(j
+1); // 0 is top vertex, 1 is first for first stack
498 stripIdx
[idx
+0] = 1; // repeat first slice's idx for closing off shape
503 // strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array
504 for (i
= 0; i
< stacks
-2; ++i
, idx
+= 2) {
505 offset
= 1+i
*slices
; // triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along
506 for (j
= 0; j
< slices
; ++j
, idx
+= 2) {
507 stripIdx
[idx
+0] = cast(GLushort
)(offset
+j
+slices
);
508 stripIdx
[idx
+1] = cast(GLushort
)(offset
+j
);
510 stripIdx
[idx
+0] = cast(GLushort
)(offset
+slices
); // repeat first slice's idx for closing off shape
511 stripIdx
[idx
+1] = cast(GLushort
)(offset
);
515 offset
= 1+(stacks
-2)*slices
; // triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along
516 for (j
= 0; j
< slices
; ++j
, idx
+= 2) {
517 stripIdx
[idx
+0] = cast(GLushort
)(nVert
-1); // zero based index, last element in array (bottom vertex)...
518 stripIdx
[idx
+1] = cast(GLushort
)(offset
+j
);
520 stripIdx
[idx
+0] = cast(GLushort
)(nVert
-1); // repeat first slice's idx for closing off shape
521 stripIdx
[idx
+1] = cast(GLushort
)(offset
);
524 fghDrawGeometrySolid(vertices
, normals
, null, nVert
, stripIdx
, stacks
, (slices
+1)*2);
526 // cleanup allocated memory
529 // cleanup allocated memory
535 public void oglShapeSolidCone() (GLfloat base
, GLfloat height
, GLint slices
, GLint stacks
) {
536 import core
.stdc
.stdlib
: malloc
, free
;
539 int i
, j
, idx
, nVert
;
540 GLfloat
* vertices
, normals
;
542 // generate vertices and normals
543 // note, (stacks+1)*slices vertices for side of object, slices+1 for top and bottom closures
544 fghGenerateCone(base
, height
, slices
, stacks
, &vertices
, &normals
, &nVert
);
546 if (nVert
== 0) return;
548 /* First, generate vertex index arrays for drawing with glDrawElements
549 * All stacks, including top and bottom are covered with a triangle
553 // create index vector
556 /* Allocate buffers for indices, bail out if memory allocation fails */
557 stripIdx
= cast(GLushort
*)malloc((slices
+1)*2*(stacks
+1)*GLushort
.sizeof
); // stacks+1 because of closing off bottom
558 if (stripIdx
is null) assert(0, "out of memory");
561 for (j
= 0, idx
= 0; j
< slices
; ++j
, idx
+= 2) {
563 stripIdx
[idx
+1] = cast(GLushort
)(j
+1); // 0 is top vertex, 1 is first for first stack
565 stripIdx
[idx
+0] = 0; // repeat first slice's idx for closing off shape
570 // strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array
571 for (i
= 0; i
< stacks
; ++i
, idx
+= 2) {
572 offset
= 1+(i
+1)*slices
; // triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along
573 for (j
= 0; j
< slices
; ++j
, idx
+= 2) {
574 stripIdx
[idx
+0] = cast(GLushort
)(offset
+j
);
575 stripIdx
[idx
+1] = cast(GLushort
)(offset
+j
+slices
);
577 stripIdx
[idx
+0] = cast(GLushort
)(offset
); // repeat first slice's idx for closing off shape
578 stripIdx
[idx
+1] = cast(GLushort
)(offset
+slices
);
582 fghDrawGeometrySolid(vertices
, normals
, null, nVert
, stripIdx
, stacks
+1, (slices
+1)*2);
584 // cleanup allocated memory
587 // cleanup allocated memory
593 public void oglShapeSolidCylinder() (GLfloat radius
, GLfloat height
, GLint slices
, GLint stacks
) {
594 import core
.stdc
.stdlib
: malloc
, free
;
597 int i
, j
, idx
, nVert
;
598 GLfloat
* vertices
, normals
;
600 // generate vertices and normals
601 // note, (stacks+1)*slices vertices for side of object, 2*slices+2 for top and bottom closures
602 fghGenerateCylinder(radius
, height
, slices
, stacks
, &vertices
, &normals
, &nVert
);
604 if (nVert
== 0) return;
606 /* First, generate vertex index arrays for drawing with glDrawElements
607 * All stacks, including top and bottom are covered with a triangle
611 // create index vector
614 // allocate buffers for indices, bail out if memory allocation fails
615 stripIdx
= cast(GLushort
*)malloc((slices
+1)*2*(stacks
+2)*GLushort
.sizeof
); // stacks+2 because of closing off bottom and top
616 if (stripIdx
is null) assert(0, "out of memory");
619 for (j
= 0, idx
= 0; j
< slices
; ++j
, idx
+= 2) {
621 stripIdx
[idx
+1] = cast(GLushort
)(j
+1); // 0 is top vertex, 1 is first for first stack
623 stripIdx
[idx
+0] = 0; // repeat first slice's idx for closing off shape
628 // strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array
629 for (i
= 0; i
< stacks
; ++i
, idx
+= 2) {
630 offset
= 1+(i
+1)*slices
; // triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along
631 for (j
= 0; j
< slices
; ++j
, idx
+= 2) {
632 stripIdx
[idx
+0] = cast(GLushort
)(offset
+j
);
633 stripIdx
[idx
+1] = cast(GLushort
)(offset
+j
+slices
);
635 stripIdx
[idx
+0] = cast(GLushort
)(offset
); // repeat first slice's idx for closing off shape
636 stripIdx
[idx
+1] = cast(GLushort
)(offset
+slices
);
640 offset
= 1+(stacks
+2)*slices
;
641 for (j
= 0; j
< slices
; ++j
, idx
+= 2) {
642 stripIdx
[idx
+0] = cast(GLushort
)(offset
+j
);
643 stripIdx
[idx
+1] = cast(GLushort
)(nVert
-1); // zero based index, last element in array (bottom vertex)...
645 stripIdx
[idx
+0] = cast(GLushort
)(offset
);
646 stripIdx
[idx
+1] = cast(GLushort
)(nVert
-1); // repeat first slice's idx for closing off shape
649 fghDrawGeometrySolid(vertices
, normals
, null, nVert
, stripIdx
, stacks
+2, (slices
+1)*2);
651 // cleanup allocated memory
654 // cleanup allocated memory
660 public void oglShapeSolidTorus() (GLfloat dInnerRadius
, GLfloat dOuterRadius
, GLint nSides
, GLint nRings
) {
661 import core
.stdc
.stdlib
: malloc
, free
;
664 int i
, j
, idx
, nVert
;
665 GLfloat
* vertices
, normals
;
667 // generate vertices and normals
668 fghGenerateTorus(dInnerRadius
, dOuterRadius
, nSides
, nRings
, &vertices
, &normals
, &nVert
);
670 if (nVert
== 0) return;
672 /* First, generate vertex index arrays for drawing with glDrawElements
673 * All stacks, including top and bottom are covered with a triangle
678 /* Allocate buffers for indices, bail out if memory allocation fails */
679 stripIdx
= cast(GLushort
*)malloc((nRings
+1)*2*nSides
*GLushort
.sizeof
);
680 if (stripIdx
is null) assert(0, "out of memory");
682 for (i
= 0, idx
= 0; i
< nSides
; ++i
) {
684 if (i
== nSides
-1) ioff
= -i
;
685 for (j
= 0; j
< nRings
; ++j
, idx
+= 2) {
686 int offset
= j
*nSides
+i
;
687 stripIdx
[idx
+0] = cast(GLushort
)(offset
);
688 stripIdx
[idx
+1] = cast(GLushort
)(offset
+ioff
);
690 // repeat first to close off shape
691 stripIdx
[idx
+0] = cast(GLushort
)(i
);
692 stripIdx
[idx
+1] = cast(GLushort
)(i
+ioff
);
697 fghDrawGeometrySolid(vertices
, normals
, null, nVert
, stripIdx
, nSides
, (nRings
+1)*2);
699 // cleanup allocated memory
702 // cleanup allocated memory