geometrydatafactory.cpp 22.1 KB
Newer Older
1
2
3
4
// ================================================================================================
// 
// This file is part of the CAMPVis Software Framework.
// 
5
// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved,
6
7
//      Christian Schulte zu Berge <christian.szb@in.tum.de>
//      Chair for Computer Aided Medical Procedures
8
9
//      Technische Universitaet Muenchen
//      Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
10
// 
11
12
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
// 
13
14
15
16
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 
// except in compliance with the License. You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
17
// 
18
19
20
21
// Unless required by applicable law or agreed to in writing, software distributed under the 
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
// either express or implied. See the License for the specific language governing permissions 
// and limitations under the License.
22
23
24
25
26
// 
// ================================================================================================

#include "geometrydatafactory.h"

27
#include "cgt/assert.h"
28

29
30
#include "core/tools/teapot.h"

31
32
namespace campvis {

33
    std::unique_ptr<FaceGeometry> GeometryDataFactory::createQuad(const cgt::vec3& llf, const cgt::vec3& urb, const cgt::vec3& texLlf, const cgt::vec3& texUrb) {
34
        std::vector<cgt::vec3> vertices, texCorods;
35

36
37
38
39
        vertices.push_back(cgt::vec3(llf.x, llf.y, llf.z));
        vertices.push_back(cgt::vec3(urb.x, llf.y, llf.z));
        vertices.push_back(cgt::vec3(urb.x, urb.y, llf.z));
        vertices.push_back(cgt::vec3(llf.x, urb.y, llf.z));
40

41
42
43
44
        texCorods.push_back(cgt::vec3(texLlf.x, texLlf.y, texLlf.z));
        texCorods.push_back(cgt::vec3(texUrb.x, texLlf.y, texLlf.z));
        texCorods.push_back(cgt::vec3(texUrb.x, texUrb.y, texLlf.z));
        texCorods.push_back(cgt::vec3(texLlf.x, texUrb.y, texLlf.z));
45

46
        return std::unique_ptr<FaceGeometry>(new FaceGeometry(vertices, texCorods));
47
48
    }

49
    std::unique_ptr<MultiIndexedGeometry> GeometryDataFactory::createGrid(const cgt::vec3& llf, const cgt::vec3& urb, const cgt::vec3& texLlf, const cgt::vec3& texUrb, int xSegments, int ySegments) {
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
        cgtAssert(xSegments > 0 && ySegments > 1, "Grid must have at least one segment in each direction");

        int numVertices = (xSegments + 1) * (ySegments + 1);
        std::vector<cgt::vec3> vertices(numVertices);
        std::vector<cgt::vec3> textureCoordinates(numVertices);
        std::vector<cgt::vec3> normals(numVertices);

        // Compute vertices of the grid in x-major order
        for (int y = 0; y <= ySegments; ++y) {
            for (int x = 0; x <= xSegments; ++x) {
                int idx = y * (xSegments + 1) + x;

                float ux = x / static_cast<float>(xSegments);
                float uy = y / static_cast<float>(ySegments);

                vertices[idx] = cgt::vec3(llf.x * (1-ux) + urb.x * ux,
                                          llf.y * (1-uy) + urb.y * uy,
                                          llf.z);
                textureCoordinates[idx] = cgt::vec3(texLlf.x * (1-ux) + texUrb.x * ux,
                                                    texLlf.y * (1-uy) + texUrb.y * uy,
                                                    texLlf.z);
                normals[idx] = cgt::vec3(0, 0, 1);
            }
        }

75
        auto result = new MultiIndexedGeometry(vertices, textureCoordinates, std::vector<cgt::vec4>(), normals);
76

Jakob Weiss's avatar
Jakob Weiss committed
77
        // For each horizontal stripe, construct the indices for triangle strips
78
79
        int verticesPerStrip = (xSegments + 1) * 2;
        for (int y = 0; y < ySegments; ++y) {
Jakob Weiss's avatar
Jakob Weiss committed
80
81
            std::vector<MultiIndexedGeometry::index_t> indices(verticesPerStrip);
            for (MultiIndexedGeometry::index_t x = 0; x <= static_cast<MultiIndexedGeometry::index_t>(xSegments); ++x) {
82
83
84
85
86
87
88
                indices[x*2 + 0] = (y + 0) * (xSegments + 1) + x;
                indices[x*2 + 1] = (y + 1) * (xSegments + 1) + x;
            }

            result->addPrimitive(indices);
        }

89
        return std::unique_ptr<MultiIndexedGeometry>(result);
90
91
92
    }


93
    std::unique_ptr<MeshGeometry> GeometryDataFactory::createCube(const cgt::Bounds& bounds, const cgt::Bounds& texBounds, const cgt::mat4& trans/* = cgt::mat4::identit*/) {
94
95
96
97
        const cgt::vec3& llf = bounds.getLLF();
        const cgt::vec3& urb = bounds.getURB();
        const cgt::vec3& tLlf = texBounds.getLLF();
        const cgt::vec3& tUrb = texBounds.getURB();
98
99

        // not the most elegant method, but it works...
100
        std::vector<cgt::vec3> vertices, texCoords;
101
102
103
        std::vector<FaceGeometry> faces;

        // front
104
        texCoords.push_back(cgt::vec3(tLlf.x, tUrb.y, tLlf.z));
105
        vertices.push_back(trans*cgt::vec3(llf.x, urb.y, llf.z));
106
        texCoords.push_back(cgt::vec3(tUrb.x, tUrb.y, tLlf.z));
107
        vertices.push_back(trans*cgt::vec3(urb.x, urb.y, llf.z));
108
        texCoords.push_back(cgt::vec3(tUrb.x, tLlf.y, tLlf.z));
109
        vertices.push_back(trans*cgt::vec3(urb.x, llf.y, llf.z));
110
        texCoords.push_back(cgt::vec3(tLlf.x, tLlf.y, tLlf.z));
111
        vertices.push_back(trans*cgt::vec3(llf.x, llf.y, llf.z));
112
        faces.push_back(FaceGeometry(vertices, texCoords, std::vector<cgt::vec4>(), std::vector<cgt::vec3>(4, cgt::vec3(0.f, 0.f, -1.f))));
113
114
115
116
        vertices.clear();
        texCoords.clear();

        // right
117
        texCoords.push_back(cgt::vec3(tUrb.x, tUrb.y, tLlf.z));
118
        vertices.push_back(trans*cgt::vec3(urb.x, urb.y, llf.z));
119
        texCoords.push_back(cgt::vec3(tUrb.x, tUrb.y, tUrb.z));
120
        vertices.push_back(trans*cgt::vec3(urb.x, urb.y, urb.z));
121
        texCoords.push_back(cgt::vec3(tUrb.x, tLlf.y, tUrb.z));
122
        vertices.push_back(trans*cgt::vec3(urb.x, llf.y, urb.z));
123
        texCoords.push_back(cgt::vec3(tUrb.x, tLlf.y, tLlf.z));
124
        vertices.push_back(trans*cgt::vec3(urb.x, llf.y, llf.z));
125
        faces.push_back(FaceGeometry(vertices, texCoords, std::vector<cgt::vec4>(), std::vector<cgt::vec3>(4, cgt::vec3(1.f, 0.f, 0.f))));
126
127
128
129
        vertices.clear();
        texCoords.clear();

        // top
130
        texCoords.push_back(cgt::vec3(tLlf.x, tUrb.y, tUrb.z));
131
        vertices.push_back(trans*cgt::vec3(llf.x, urb.y, urb.z));
132
        texCoords.push_back(cgt::vec3(tUrb.x, tUrb.y, tUrb.z));
133
        vertices.push_back(trans*cgt::vec3(urb.x, urb.y, urb.z));
134
        texCoords.push_back(cgt::vec3(tUrb.x, tUrb.y, tLlf.z));
135
        vertices.push_back(trans*cgt::vec3(urb.x, urb.y, llf.z));
136
        texCoords.push_back(cgt::vec3(tLlf.x, tUrb.y, tLlf.z));
137
        vertices.push_back(trans*cgt::vec3(llf.x, urb.y, llf.z));
138
        faces.push_back(FaceGeometry(vertices, texCoords, std::vector<cgt::vec4>(), std::vector<cgt::vec3>(4, cgt::vec3(0.f, 1.f, 0.f))));
139
140
141
142
        vertices.clear();
        texCoords.clear();

        // left
143
        texCoords.push_back(cgt::vec3(tLlf.x, tUrb.y, tUrb.z));
144
        vertices.push_back(trans*cgt::vec3(llf.x, urb.y, urb.z));
145
        texCoords.push_back(cgt::vec3(tLlf.x, tUrb.y, tLlf.z));
146
        vertices.push_back(trans*cgt::vec3(llf.x, urb.y, llf.z));
147
        texCoords.push_back(cgt::vec3(tLlf.x, tLlf.y, tLlf.z));
148
        vertices.push_back(trans*cgt::vec3(llf.x, llf.y, llf.z));
149
        texCoords.push_back(cgt::vec3(tLlf.x, tLlf.y, tUrb.z));
150
        vertices.push_back(trans*cgt::vec3(llf.x, llf.y, urb.z));
151
        faces.push_back(FaceGeometry(vertices, texCoords, std::vector<cgt::vec4>(), std::vector<cgt::vec3>(4, cgt::vec3(-1.f, 0.f, 0.f))));
152
153
154
155
        vertices.clear();
        texCoords.clear();

        // bottom
156
        texCoords.push_back(cgt::vec3(tLlf.x, tLlf.y, tLlf.z));
157
        vertices.push_back(trans*cgt::vec3(llf.x, llf.y, llf.z));
158
        texCoords.push_back(cgt::vec3(tUrb.x, tLlf.y, tLlf.z));
159
        vertices.push_back(trans*cgt::vec3(urb.x, llf.y, llf.z));
160
        texCoords.push_back(cgt::vec3(tUrb.x, tLlf.y, tUrb.z));
161
        vertices.push_back(trans*cgt::vec3(urb.x, llf.y, urb.z));
162
        texCoords.push_back(cgt::vec3(tLlf.x, tLlf.y, tUrb.z));
163
        vertices.push_back(trans*cgt::vec3(llf.x, llf.y, urb.z));
164
        faces.push_back(FaceGeometry(vertices, texCoords, std::vector<cgt::vec4>(), std::vector<cgt::vec3>(4, cgt::vec3(0.f, -1.f, 0.f))));
165
166
167
168
        vertices.clear();
        texCoords.clear();

        // back
169
        texCoords.push_back(cgt::vec3(tUrb.x, tUrb.y, tUrb.z));
170
        vertices.push_back(trans*cgt::vec3(urb.x, urb.y, urb.z));
171
        texCoords.push_back(cgt::vec3(tLlf.x, tUrb.y, tUrb.z));
172
        vertices.push_back(trans*cgt::vec3(llf.x, urb.y, urb.z));
173
        texCoords.push_back(cgt::vec3(tLlf.x, tLlf.y, tUrb.z));
174
        vertices.push_back(trans*cgt::vec3(llf.x, llf.y, urb.z));
175
        texCoords.push_back(cgt::vec3(tUrb.x, tLlf.y, tUrb.z));
176
        vertices.push_back(trans*cgt::vec3(urb.x, llf.y, urb.z));
177
        faces.push_back(FaceGeometry(vertices, texCoords, std::vector<cgt::vec4>(), std::vector<cgt::vec3>(4, cgt::vec3(0.f, 0.f, 1.f))));
178
179
180
        vertices.clear();
        texCoords.clear();

181
        return std::unique_ptr<MeshGeometry>(new MeshGeometry(faces));
182
183
    }

184
    std::unique_ptr<MultiIndexedGeometry> GeometryDataFactory::createTeapot() {
185
        std::vector<cgt::vec3> vertices, normals;
186
187
        vertices.reserve(Teapot::num_teapot_vertices);
        normals.reserve(Teapot::num_teapot_vertices);
188

189
        for (size_t i = 0; i < Teapot::num_teapot_vertices; ++i) {
190
191
            vertices.push_back(cgt::vec3(Teapot::teapot_vertices + 3*i));
            normals.push_back(cgt::vec3(Teapot::teapot_normals + 3*i));
192
193
        }

194
        MultiIndexedGeometry* toReturn = new MultiIndexedGeometry(vertices, std::vector<cgt::vec3>(), std::vector<cgt::vec4>(), normals);
195
196
197

        // convert indices and add primitives 
        int currentOffset = 0;
198
        while (currentOffset < Teapot::num_teapot_indices) {
Jakob Weiss's avatar
Jakob Weiss committed
199
200
            MultiIndexedGeometry::index_t count = Teapot::new_teapot_indicies[currentOffset];
            toReturn->addPrimitive(std::vector<MultiIndexedGeometry::index_t>(Teapot::new_teapot_indicies + currentOffset + 1, Teapot::new_teapot_indicies + count + currentOffset + 1));
201
202
203
            currentOffset += count + 1;
        }

204
        return std::unique_ptr<MultiIndexedGeometry>(toReturn);
205
206
    }

207
    std::unique_ptr<MultiIndexedGeometry> GeometryDataFactory::createSphere(uint16_t numStacks /*= 6*/, uint16_t numSlices /*= 12*/, const cgt::vec3& exponents /*= cgt::vec3(1.f)*/) {
208
209
210
        cgtAssert(numStacks > 1 && numSlices > 2, "Sphere must have minimum 2 stacks and 3 slices!");
        std::vector<cgt::vec3> vertices;
        std::vector<cgt::vec3> textureCoordinates;
211
212

        // add top vertex
213
214
        vertices.push_back(cgt::vec3(0.f, 0.f, 1.f));
        textureCoordinates.push_back(cgt::vec3(0.f));
215
216
217

        // add middle vertices
        for (int i = 1; i < numStacks; ++i) {
218
            float phi = static_cast<float>(i) * cgt::PIf / static_cast<float>(numStacks);
219
220

            for (int j = 0; j < numSlices; ++j) {
221
                float theta = static_cast<float>(j) * 2.f*cgt::PIf / static_cast<float>(numSlices);
222
223
224
225
226
227
228
229
230
231
232

                // apply exponents for supersphere
                cgt::vec3 theVertex(cos(theta) * sin(phi), sin(theta) * sin(phi), cos(phi));
                for (size_t e = 0; e < 3; ++e) {
                    if (theVertex[e] < 0)
                        theVertex[e] = -pow(-theVertex[e], exponents[e]);
                    else
                        theVertex[e] = pow(theVertex[e], exponents[e]);
                }

                vertices.push_back(theVertex);
233
                textureCoordinates.push_back(cgt::vec3(theta / (2.f * cgt::PIf), phi / cgt::PIf, 0.f));
234
235
236
237
            }
        }

        // add bottom vertex
238
239
        vertices.push_back(cgt::vec3(0.f, 0.f, -1.f));
        textureCoordinates.push_back(cgt::vec3(1.f, 0.f, 0.f));
240
241

        // create geometry (in a unit sphere vertices = normals)
242
        MultiIndexedGeometry* toReturn = new MultiIndexedGeometry(vertices, textureCoordinates, std::vector<cgt::vec4>(), vertices);
243
244
245
246

        // add indices for primitives to geometry:
        {
            // top stack:
Jakob Weiss's avatar
Jakob Weiss committed
247
248
            std::vector<MultiIndexedGeometry::index_t> indices;
            for (MultiIndexedGeometry::index_t j = 0; j < numSlices; ++j) {
249
250
251
252
253
254
255
256
257
258
                indices.push_back(0);
                indices.push_back(j+1);
            }
            indices.push_back(0);
            indices.push_back(1);

            toReturn->addPrimitive(indices);
        }
        {
            // middle stacks:
Jakob Weiss's avatar
Jakob Weiss committed
259
260
261
            std::vector<MultiIndexedGeometry::index_t> indices;
            for (MultiIndexedGeometry::index_t i = 1; i < static_cast<MultiIndexedGeometry::index_t>(numStacks-1); ++i) {
                MultiIndexedGeometry::index_t startIndex = 1 + (i-1) * numSlices;
262

Jakob Weiss's avatar
Jakob Weiss committed
263
                for (MultiIndexedGeometry::index_t j = 0; j < numSlices; ++j) {
264
265
266
267
268
269
270
271
272
273
274
                    indices.push_back(startIndex + j);
                    indices.push_back(startIndex + numSlices + j);
                }
                indices.push_back(startIndex);
                indices.push_back(startIndex + numSlices);
            }

            toReturn->addPrimitive(indices);
        }
        {
            // bottom stack:
Jakob Weiss's avatar
Jakob Weiss committed
275
276
            std::vector<MultiIndexedGeometry::index_t> indices;
            MultiIndexedGeometry::index_t endIndex = static_cast<MultiIndexedGeometry::index_t>(vertices.size() - 1);
277

Jakob Weiss's avatar
Jakob Weiss committed
278
            for (MultiIndexedGeometry::index_t j = 0; j < numSlices; ++j) {
279
280
281
282
283
284
285
286
287
                indices.push_back(endIndex);
                indices.push_back(endIndex - (j+1));
            }
            indices.push_back(endIndex);
            indices.push_back(endIndex - 1);

            toReturn->addPrimitive(indices);
        }

288
        return std::unique_ptr<MultiIndexedGeometry>(toReturn);
289
290
    }

291
    std::unique_ptr<MultiIndexedGeometry> GeometryDataFactory::createArrow(uint16_t numSlices, float tipLen, float cylRadius, float tipRadius) {
292
293
294
295
296
297
        cgtAssert(numSlices > 2, "Arrow shaft must have minimum 3 slices!");
        cgtAssert(tipRadius > cylRadius, "Tip radius must exceed cyclinder radius (for correct normals)!");
        cgtAssert(tipLen > 0, "Tip length must be between 0 and 1!");
        cgtAssert(tipLen < 1, "Tip length must be between 0 and 1!");
        std::vector<cgt::vec3> vertices;
        std::vector<cgt::vec3> normals;
298

299
        // add bottom vertex
300
301
        vertices.push_back(cgt::vec3(0.f, 0.f, 0.f));
        normals.push_back(cgt::vec3(0.f, 0.f, -1.f));
302

303
304
        // add shaft floor vertices
        for (int i = 0; i < numSlices; ++i) {
305
306
307
            float theta = static_cast<float>(i) * 2.f*cgt::PIf / static_cast<float>(numSlices);
            vertices.push_back(cgt::vec3(cylRadius * cos(theta), cylRadius * sin(theta), 0.f));
            normals.push_back(cgt::vec3(0.f, 0.f, -1.f));
308
309
        }
        for (int i = 0; i < numSlices; ++i) {
310
311
312
            float theta = static_cast<float>(i) * 2.f*cgt::PIf / static_cast<float>(numSlices);
            vertices.push_back(cgt::vec3(cylRadius * cos(theta), cylRadius * sin(theta), 0.f));
            normals.push_back(cgt::vec3(cos(theta), sin(theta), 0.f));
313
        }
314

315
316
        // add shaft top vertices
        for (int i = 0; i < numSlices; ++i) {
317
318
319
            float theta = static_cast<float>(i) * 2.f*cgt::PIf / static_cast<float>(numSlices);
            vertices.push_back(cgt::vec3(cylRadius * cos(theta), cylRadius * sin(theta), 1.f - tipLen));
            normals.push_back(cgt::vec3(cos(theta), sin(theta), 0.f));
320
321
        }
        for (int i = 0; i < numSlices; ++i) {
322
323
324
            float theta = static_cast<float>(i) * 2.f*cgt::PIf / static_cast<float>(numSlices);
            vertices.push_back(cgt::vec3(cylRadius * cos(theta), cylRadius * sin(theta), 1.f - tipLen));
            normals.push_back(cgt::vec3(0.f, 0.f, -1.f));
325
        }
326

327
328
        // add arrow tip outer cone vertices
        for (int i = 0; i < numSlices; ++i) {
329
330
331
            float theta = static_cast<float>(i) * 2.f*cgt::PIf / static_cast<float>(numSlices);
            vertices.push_back(cgt::vec3(tipRadius * cos(theta), tipRadius * sin(theta), 1.f - tipLen));
            normals.push_back(cgt::vec3(0.f, 0.f, -1.f));
332
333
334
        }
        float phi = atan2f(tipRadius, tipLen);
        for (int i = 0; i < numSlices; ++i) {
335
336
337
            float theta = static_cast<float>(i) * 2.f*cgt::PIf / static_cast<float>(numSlices);
            vertices.push_back(cgt::vec3(tipRadius * cos(theta), tipRadius * sin(theta), 1.f - tipLen));
            normals.push_back(cgt::vec3(cos(theta) * cos(phi), sin(theta) * cos(phi), sin(phi)));
338
        }
339

340
        // add top vertex
341
342
        vertices.push_back(cgt::vec3(0.f, 0.f, 1.f));
        normals.push_back(cgt::vec3(0.f, 0.f, 1.f));
343
344

        // create geometry
345
        MultiIndexedGeometry* toReturn = new MultiIndexedGeometry(vertices, std::vector<cgt::vec3>(), std::vector<cgt::vec4>(), normals);
346
347
348
349

        // add indices for primitives to geometry:
        {
            // cylinder floor
Jakob Weiss's avatar
Jakob Weiss committed
350
351
            std::vector<MultiIndexedGeometry::index_t> indices;
            for (MultiIndexedGeometry::index_t j = 0; j < numSlices; ++j) {
352
353
354
355
356
357
358
359
                indices.push_back(0);
                indices.push_back(j+1);
            }
            indices.push_back(0);
            indices.push_back(1);

            toReturn->addPrimitive(indices);
        }
360
        {
361
            // cylinder shaft
Jakob Weiss's avatar
Jakob Weiss committed
362
363
            std::vector<MultiIndexedGeometry::index_t> indices;
            for (MultiIndexedGeometry::index_t j = 0; j < numSlices; ++j) {
364
365
366
367
368
369
370
371
                indices.push_back(j+1+numSlices);
                indices.push_back(j+1+numSlices*2);
            }
            indices.push_back(1+numSlices);
            indices.push_back(1+numSlices*2);

            toReturn->addPrimitive(indices);
        }
372
        {
373
            // arrow tip bottom area
Jakob Weiss's avatar
Jakob Weiss committed
374
375
            std::vector<MultiIndexedGeometry::index_t> indices;
            for (MultiIndexedGeometry::index_t j = 0; j < numSlices; ++j) {
376
377
378
379
380
381
382
383
                indices.push_back(j+1+numSlices*3);
                indices.push_back(j+1+numSlices*4);
            }
            indices.push_back(1+numSlices*3);
            indices.push_back(1+numSlices*4);

            toReturn->addPrimitive(indices);
        }
384
        {
385
            // arrow tip cone
386
            uint16_t m = static_cast<uint16_t>(vertices.size() - 1);
Jakob Weiss's avatar
Jakob Weiss committed
387
388
            std::vector<MultiIndexedGeometry::index_t> indices;
            for (MultiIndexedGeometry::index_t j = 0; j < numSlices; ++j) {
389
                indices.push_back(j+1+numSlices*5);
390
                indices.push_back(m);
391
392
393
394
395
396
397
            }
            indices.push_back(1+numSlices*5);
            indices.push_back(m);

            toReturn->addPrimitive(indices);
        }

398
        return std::unique_ptr<MultiIndexedGeometry>(toReturn);
399
    }
400

401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
    std::unique_ptr<MultiIndexedGeometry> GeometryDataFactory::createCylinder(uint16_t numSlices, float cylRadius)
    {
        cgtAssert(numSlices > 2, "Cylinder must have minimum 3 slices!");

        std::vector<cgt::vec3> vertices;
        std::vector<cgt::vec3> normals;

        // add bottom vertex
        vertices.push_back(cgt::vec3(0.f, 0.f, 0.f));
        normals.push_back(cgt::vec3(0.f, 0.f, -1.f));

        // add shaft floor vertices
        for (int i = 0; i < numSlices; ++i) {
            float theta = static_cast<float>(i) * 2.f*cgt::PIf / static_cast<float>(numSlices);
            vertices.push_back(cgt::vec3(cylRadius * cos(theta), cylRadius * sin(theta), 0.f));
            normals.push_back(cgt::vec3(0.f, 0.f, -1.f));
        }
        for (int i = 0; i < numSlices; ++i) {
            float theta = static_cast<float>(i) * 2.f*cgt::PIf / static_cast<float>(numSlices);
            vertices.push_back(cgt::vec3(cylRadius * cos(theta), cylRadius * sin(theta), 0.f));
            normals.push_back(cgt::vec3(cos(theta), sin(theta), 0.f));
        }

        // add shaft top vertices
        for (int i = 0; i < numSlices; ++i) {
            float theta = static_cast<float>(i) * 2.f*cgt::PIf / static_cast<float>(numSlices);
            vertices.push_back(cgt::vec3(cylRadius * cos(theta), cylRadius * sin(theta), 1.f));
            normals.push_back(cgt::vec3(cos(theta), sin(theta), 0.f));
        }

        // add top center vertex
        MultiIndexedGeometry::index_t topVertexIndex = vertices.size();
        vertices.push_back(cgt::vec3(0.f, 0.f, 1.f));
        normals.push_back(cgt::vec3(0.f, 0.f, 1.f));
        // add rest of top vertices
        for (int i = 0; i < numSlices; ++i) {
            float theta = static_cast<float>(i) * 2.f*cgt::PIf / static_cast<float>(numSlices);
            vertices.push_back(cgt::vec3(cylRadius * cos(theta), cylRadius * sin(theta), 1.f));
            normals.push_back(cgt::vec3(0.f, 0.f, 1.f));
        }


        // create geometry
        MultiIndexedGeometry* toReturn = new MultiIndexedGeometry(vertices, std::vector<cgt::vec3>(), std::vector<cgt::vec4>(), normals);

        // add indices for primitives to geometry:
        {
            // cylinder floor
            std::vector<MultiIndexedGeometry::index_t> indices;
            for (MultiIndexedGeometry::index_t j = 0; j < numSlices; ++j) {
                indices.push_back(0);
                indices.push_back(j + 1);
            }
            indices.push_back(0);
            indices.push_back(1);

            toReturn->addPrimitive(indices);
        }
        {
            // cylinder shaft
            std::vector<MultiIndexedGeometry::index_t> indices;
            for (MultiIndexedGeometry::index_t j = 0; j < numSlices; ++j) {
                indices.push_back(j + 1 + numSlices);
                indices.push_back(j + 1 + numSlices * 2);
            }
            indices.push_back(1 + numSlices);
            indices.push_back(1 + numSlices * 2);

            toReturn->addPrimitive(indices);
        }
        {
            // cylinder top
            std::vector<MultiIndexedGeometry::index_t> indices;
            for (MultiIndexedGeometry::index_t j = 0; j < numSlices; ++j) {
                indices.push_back(topVertexIndex); // the last/top vertex
                indices.push_back(topVertexIndex + j + 1);
            }
            indices.push_back(topVertexIndex); // the last/top vertex
            indices.push_back(topVertexIndex + 1);

            toReturn->addPrimitive(indices);
        }

        return std::unique_ptr<MultiIndexedGeometry>(toReturn);
    }

487
}