egra: prepare agg mini before calling `onPaint()` (and cleanup afterwards)
[iv.d.git] / glbinds / shape.d
blob0a9ec2ca40e1bb69cfb0a4a7054b25231637f898
1 // GPLv3
2 // shamelessly ripped from FreeGLUT (except cube)
3 module iv.glbinds.shape /*is aliced*/;
4 nothrow @trusted @nogc:
6 import iv.glbinds;
9 /*
10 * Compute lookup table of cos and sin values forming a circle
11 * (or half circle if halfCircle==TRUE)
13 * Notes:
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;
21 import std.math;
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) {
35 free(*sint);
36 free(*cost);
37 assert(0, "out of memory");
40 // compute cos and sin around the circle
41 (*sint)[0] = 0.0f;
42 (*cost)[0] = 1.0f;
44 foreach (immutable int i; 1..size) {
45 (*sint)[i] = cast(GLfloat)sin(angle*i);
46 (*cost)[i] = cast(GLfloat)cos(angle*i);
49 if (halfCircle) {
50 (*sint)[size] = 0.0f; // sin PI
51 (*cost)[size] = -1.0f; // cos PI
52 } else {
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
64 GLfloat x, y, z;
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
73 *nVert = 0;
74 return;
77 *nVert = slices*(stacks-1)+2;
79 if (*nVert > 65535) {
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) {
92 free(*vertices);
93 free(*normals);
94 assert(0, "out of memory");
97 // top
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;
104 idx = 3;
106 // each stack
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];
111 z = cost2[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;
120 idx += 3;
124 // bottom
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
133 free(sint1);
134 free(cost1);
135 free(sint2);
136 free(cost2);
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;
145 import std.math;
147 int idx = 0; // idx into vertex/normal buffer
149 // pre-computed circle
150 GLfloat* sint, cost;
152 // step in z and radius as stacks are drawn.
153 GLfloat z = 0;
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
166 *nVert = 0;
167 return;
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) {
183 free(*vertices);
184 free(*normals);
185 assert(0, "out of memory");
188 // bottom
189 (*vertices)[0] = 0.0f;
190 (*vertices)[1] = 0.0f;
191 (*vertices)[2] = z;
192 (*normals)[0] = 0.0f;
193 (*normals)[1] = 0.0f;
194 (*normals)[2] = -1.0f;
196 idx = 3;
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;
205 idx += 3;
208 // each stack
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;
217 idx += 3;
219 z += zStep;
220 r -= rStep;
223 // release sin and cos tables
224 free(sint);
225 free(cost);
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;
234 import std.math;
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
243 GLfloat* sint, cost;
245 // number of unique vertices
246 if (slices == 0 || stacks < 1) {
247 // nothing to generate
248 *nVert = 0;
249 return;
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) {
265 free(*vertices);
266 free(*normals);
267 assert(0, "out of memory");
270 GLfloat z = 0;
271 // top on Z-axis
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;
278 idx = 3;
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;
287 idx += 3;
290 // each stack
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;
299 idx += 3;
301 z += zStep;
304 // other on bottom (get normals right)
305 z -= zStep;
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;
313 idx += 3;
316 // bottom
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
325 free(sint);
326 free(cost);
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;
336 import std.math;
338 GLfloat iradius = cast(GLfloat)dInnerRadius;
339 GLfloat oradius = cast(GLfloat)dOuterRadius;
341 // pre-computed circle
342 GLfloat* spsi, cpsi;
343 GLfloat* sphi, cphi;
345 // number of unique vertices
346 if (nSides < 2 || nRings < 2) {
347 // nothing to generate
348 *nVert = 0;
349 return;
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) {
366 free(*vertices);
367 free(*normals);
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
385 free(spsi);
386 free(cpsi);
387 free(sphi);
388 free(cphi);
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);
409 } else {
410 if (numParts > 1) {
411 foreach (immutable i; 0..numParts) glDrawElements(GL_TRIANGLE_STRIP, numVertIdxsPerPart, GL_UNSIGNED_SHORT, vertIdxs+i*numVertIdxsPerPart);
412 } else {
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 = [
426 0.5f, 0.5f, 0.5f,
427 -0.5f, 0.5f, 0.5f,
428 -0.5f,-0.5f, 0.5f,
429 0.5f,-0.5f, 0.5f,
430 0.5f,-0.5f,-0.5f,
431 0.5f, 0.5f,-0.5f,
432 -0.5f, 0.5f,-0.5f,
433 -0.5f,-0.5f,-0.5f,
436 // normal vectors
437 static immutable GLfloat[18] cube_norms = [
438 0.0f, 0.0f, 1.0f,
439 1.0f, 0.0f, 0.0f,
440 0.0f, 1.0f, 0.0f,
441 -1.0f, 0.0f, 0.0f,
442 0.0f,-1.0f, 0.0f,
443 0.0f, 0.0f,-1.0f,
446 // vertex indices, as quads, before triangulation
447 static immutable GLushort[24] cube_vertIdxs = [
448 0,1,2,3,
449 0,3,4,5,
450 0,5,6,1,
451 1,6,7,2,
452 7,4,3,2,
453 4,7,6,5,
456 glBegin(GL_QUADS);
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;
463 glVertex3fv(v.ptr);
466 glEnd();
470 public void oglShapeSolidSphere() (GLfloat radius, GLint slices, GLint stacks) {
471 import core.stdc.stdlib : malloc, free;
472 import std.math;
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.
485 GLushort* stripIdx;
486 // create index vector
487 int offset;
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");
493 // top stack
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
496 stripIdx[idx+1] = 0;
498 stripIdx[idx+0] = 1; // repeat first slice's idx for closing off shape
499 stripIdx[idx+1] = 0;
500 idx += 2;
502 // middle stacks
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);
514 // bottom stack
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);
523 // draw */
524 fghDrawGeometrySolid(vertices, normals, null, nVert, stripIdx, stacks, (slices+1)*2);
526 // cleanup allocated memory
527 free(stripIdx);
529 // cleanup allocated memory
530 free(vertices);
531 free(normals);
535 public void oglShapeSolidCone() (GLfloat base, GLfloat height, GLint slices, GLint stacks) {
536 import core.stdc.stdlib : malloc, free;
537 import std.math;
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
550 * strip.
552 GLushort* stripIdx;
553 // create index vector
554 int offset;
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");
560 // top stack
561 for (j = 0, idx = 0; j < slices; ++j, idx += 2) {
562 stripIdx[idx+0] = 0;
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
566 stripIdx[idx+1] = 1;
567 idx += 2;
569 // middle stacks
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);
581 // draw
582 fghDrawGeometrySolid(vertices, normals, null, nVert, stripIdx, stacks+1, (slices+1)*2);
584 // cleanup allocated memory
585 free(stripIdx);
587 // cleanup allocated memory
588 free(vertices);
589 free(normals);
593 public void oglShapeSolidCylinder() (GLfloat radius, GLfloat height, GLint slices, GLint stacks) {
594 import core.stdc.stdlib : malloc, free;
595 import std.math;
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
608 * strip.
610 GLushort* stripIdx;
611 // create index vector
612 int offset;
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");
618 // top stack
619 for (j = 0, idx = 0; j < slices; ++j, idx += 2) {
620 stripIdx[idx+0] = 0;
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
624 stripIdx[idx+1] = 1;
625 idx += 2;
627 // middle stacks
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);
639 // top stack
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
648 // draw
649 fghDrawGeometrySolid(vertices, normals, null, nVert, stripIdx, stacks+2, (slices+1)*2);
651 // cleanup allocated memory
652 free(stripIdx);
654 // cleanup allocated memory
655 free(vertices);
656 free(normals);
660 public void oglShapeSolidTorus() (GLfloat dInnerRadius, GLfloat dOuterRadius, GLint nSides, GLint nRings) {
661 import core.stdc.stdlib : malloc, free;
662 import std.math;
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
674 * strip.
676 GLushort* stripIdx;
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) {
683 int ioff = 1;
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);
693 idx += 2;
696 // draw
697 fghDrawGeometrySolid(vertices, normals, null, nVert, stripIdx, nSides, (nRings+1)*2);
699 // cleanup allocated memory
700 free(stripIdx);
702 // cleanup allocated memory
703 free(vertices);
704 free(normals);