1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 #include "misc/SoGenerate.h"
34
35 #include <Inventor/SoPrimitiveVertex.h>
36 #include <Inventor/details/SoCubeDetail.h>
37 #include <Inventor/details/SoCylinderDetail.h>
38 #include <Inventor/details/SoConeDetail.h>
39
40 #include <Inventor/nodes/SoCone.h>
41 #include <Inventor/nodes/SoCylinder.h>
42
43 // generate a 3d circle in the x-z plane
44 static void
sogenerate_generate_3d_circle(SbVec3f * coords,const int num,const float radius,const float y)45 sogenerate_generate_3d_circle(SbVec3f *coords, const int num, const float radius, const float y)
46 {
47 // FIXME: this function completely duplicates
48 // sogl_generate_3d_circle() in SoGL.cpp, AFAICS. Should avoid
49 // unnecessary duplication. 20010909 mortene.
50
51 float delta = (float) ((2.0*M_PI)/(double)num);
52 float angle = 0.0f;
53 for (int i = 0; i < num; i++) {
54 coords[i][0] = (float) (-sin(angle) * radius);
55 coords[i][1] = y;
56 coords[i][2] = (float) (-cos(angle) * radius);
57 angle += delta;
58 }
59 }
60
61 // generate a 2d circle
62 static void
sogenerate_generate_2d_circle(SbVec2f * coords,const int num,const float radius)63 sogenerate_generate_2d_circle(SbVec2f *coords, const int num, const float radius)
64 {
65 // FIXME: this function completely duplicates
66 // sogl_generate_3d_circle() in SoGL.cpp, AFAICS. Should avoid
67 // unnecessary duplication. 20010909 mortene.
68
69 float delta = (float) (2.0*M_PI/(double)num);
70 float angle = 0.0f;
71 for (int i = 0; i < num; i++) {
72 coords[i][0] = (float) (-sin(angle) * radius);
73 coords[i][1] = (float) (-cos(angle) * radius);
74 angle += delta;
75 }
76 }
77
78 //
79 // the 12 triangles in the cube
80 //
81 static int sogenerate_cube_vindices[] =
82 {
83 0, 1, 3, 2,
84 5, 4, 6, 7,
85 1, 5, 7, 3,
86 4, 0, 2, 6,
87 4, 5, 1, 0,
88 2, 3, 7, 6
89 };
90
91 static const SbVec2f sogenerate_cube_texcoords[] =
92 {
93 SbVec2f(1.0f, 1.0f),
94 SbVec2f(0.0f, 1.0f),
95 SbVec2f(0.0f, 0.0f),
96 SbVec2f(1.0f, 0.0f)
97 };
98
99 //
100 // a cube needs 6 normals
101 //
102 static const SbVec3f sogenerate_cube_normals[] =
103 {
104 SbVec3f(0.0f, 0.0f, 1.0f),
105 SbVec3f(0.0f, 0.0f, -1.0f),
106 SbVec3f(-1.0f, 0.0f, 0.0f),
107 SbVec3f(1.0f, 0.0f, 0.0f),
108 SbVec3f(0.0f, 1.0f, 0.0f),
109 SbVec3f(0.0f, -1.0f, 0.0f)
110 };
111
112 static void
sogenerate_generate_cube_vertices(SbVec3f * varray,const float w,const float h,const float d)113 sogenerate_generate_cube_vertices(SbVec3f *varray,
114 const float w,
115 const float h,
116 const float d)
117 {
118 for (int i = 0; i < 8; i++) {
119 varray[i].setValue((i&1) ? -w : w,
120 (i&2) ? -h : h,
121 (i&4) ? -d : d);
122 }
123 }
124
125 class so_generate_prim_private {
126 public:
generate_cone(const float radius,const float height,const int numslices,const unsigned int flags,SoShape * const shape,SoAction * const action)127 static void generate_cone(const float radius,
128 const float height,
129 const int numslices,
130 const unsigned int flags,
131 SoShape * const shape,
132 SoAction * const action) {
133 int i;
134 int slices = numslices;
135 if (slices > 128) slices = 128;
136 if (slices < 4) slices = 4;
137
138 float h2 = height * 0.5f;
139
140 // put coordinates on the stack
141 SbVec3f coords[129];
142 SbVec3f normals[130];
143 SbVec2f texcoords[129];
144
145 sogenerate_generate_3d_circle(coords, slices, radius, -h2);
146 coords[slices] = coords[0];
147
148 double a = atan(height/radius);
149 sogenerate_generate_3d_circle(normals, slices, (float) sin(a), (float) cos(a));
150 normals[slices] = normals[0];
151 normals[slices+1] = normals[1];
152
153 int matnr = 0;
154
155 SoPrimitiveVertex vertex;
156 SoConeDetail sideDetail;
157 SoConeDetail bottomDetail;
158 sideDetail.setPart(SoCone::SIDES);
159 bottomDetail.setPart(SoCone::BOTTOM);
160
161 // FIXME: the texture coordinate generation for cone sides is of
162 // sub-par quality. The textures comes out looking "skewed" and
163 // "compressed". 20010926 mortene.
164
165 if (flags & SOGEN_GENERATE_SIDE) {
166 vertex.setDetail(&sideDetail);
167 vertex.setMaterialIndex(matnr);
168
169 shape->beginShape(action, SoShape::TRIANGLES);
170 i = 0;
171
172 float t = 1.0;
173 float delta = 1.0f / slices;
174
175 while (i < slices) {
176 vertex.setTextureCoords(SbVec2f(t - delta*0.5f, 1.0f));
177 vertex.setNormal((normals[i] + normals[i+1])*0.5f);
178 vertex.setPoint(SbVec3f(0.0f, h2, 0.0f));
179 shape->shapeVertex(&vertex);
180
181 vertex.setTextureCoords(SbVec2f(t, 0.0f));
182 vertex.setNormal(normals[i]);
183 vertex.setPoint(coords[i]);
184 shape->shapeVertex(&vertex);
185
186 vertex.setTextureCoords(SbVec2f(t-delta, 0.0f));
187 vertex.setNormal(normals[i+1]);
188 vertex.setPoint(coords[i+1]);
189 shape->shapeVertex(&vertex);
190
191 i++;
192 t -= delta;
193 }
194 if (flags & SOGEN_MATERIAL_PER_PART) matnr++;
195 shape->endShape();
196 }
197
198 if (flags & SOGEN_GENERATE_BOTTOM) {
199 vertex.setDetail(&bottomDetail);
200 vertex.setMaterialIndex(matnr);
201
202 sogenerate_generate_2d_circle(texcoords, slices, 0.5f);
203 texcoords[slices] = texcoords[0];
204
205 shape->beginShape(action, SoShape::TRIANGLE_FAN);
206 vertex.setNormal(SbVec3f(0.0f, -1.0f, 0.0f));
207 for (i = slices-1; i >= 0; i--) {
208 vertex.setTextureCoords(texcoords[i]+SbVec2f(0.5f, 0.5f));
209 vertex.setPoint(coords[i]);
210 shape->shapeVertex(&vertex);
211 }
212 shape->endShape();
213 }
214 }
215
generate_cylinder(const float radius,const float height,const int numslices,const unsigned int flags,SoShape * const shape,SoAction * const action)216 static void generate_cylinder(const float radius,
217 const float height,
218 const int numslices,
219 const unsigned int flags,
220 SoShape * const shape,
221 SoAction * const action) {
222 int i;
223 int slices = numslices;
224 if (slices > 128) slices = 128;
225 if (slices < 4) slices = 4;
226
227 float h2 = height * 0.5f;
228
229 SbVec3f coords[129];
230 SbVec3f normals[130];
231 SbVec2f texcoords[129];
232
233 sogenerate_generate_3d_circle(coords, slices, radius, -h2);
234 coords[slices] = coords[0];
235
236 sogenerate_generate_3d_circle(normals, slices, 1.0f, 0.0f);
237 normals[slices] = normals[0];
238 normals[slices+1] = normals[1];
239
240 int matnr = 0;
241
242 SoPrimitiveVertex vertex;
243 SoCylinderDetail sideDetail;
244 SoCylinderDetail bottomDetail;
245 SoCylinderDetail topDetail;
246 sideDetail.setPart(SoCylinder::SIDES);
247 bottomDetail.setPart(SoCylinder::BOTTOM);
248 topDetail.setPart(SoCylinder::TOP);
249
250 if (flags & SOGEN_GENERATE_SIDE) {
251 shape->beginShape(action, SoShape::QUAD_STRIP);
252 vertex.setDetail(&sideDetail);
253 vertex.setMaterialIndex(matnr);
254 i = 0;
255
256 float t = 0.0;
257 float inc = 1.0f / slices;
258
259 while (i <= slices) {
260 vertex.setTextureCoords(SbVec2f(t, 1.0f));
261 vertex.setNormal(normals[i]);
262 SbVec3f c = coords[i];
263 vertex.setPoint(SbVec3f(c[0], h2, c[2]));
264 shape->shapeVertex(&vertex);
265
266 vertex.setTextureCoords(SbVec2f(t, 0.0f));
267 vertex.setPoint(c);
268 shape->shapeVertex(&vertex);
269 i++;
270 t += inc;
271 }
272 if (flags & SOGEN_MATERIAL_PER_PART) matnr++;
273 shape->endShape();
274 }
275
276 if (flags & (SOGEN_GENERATE_BOTTOM | SOGEN_GENERATE_TOP)) {
277 sogenerate_generate_2d_circle(texcoords, slices, 0.5f);
278 texcoords[slices] = texcoords[0];
279 }
280
281 if (flags & SOGEN_GENERATE_TOP) {
282 vertex.setMaterialIndex(matnr);
283 vertex.setDetail(&topDetail);
284 vertex.setNormal(SbVec3f(0.0f, 1.0f, 0.0f));
285 shape->beginShape(action, SoShape::TRIANGLE_FAN);
286
287 for (i = 0; i < slices; i++) {
288 vertex.setTextureCoords(SbVec2f(texcoords[i][0] + 0.5f, 1.0f - texcoords[i][1] - 0.5f));
289 const SbVec3f &c = coords[i];
290 vertex.setPoint(SbVec3f(c[0], h2, c[2]));
291 shape->shapeVertex(&vertex);
292 }
293 shape->endShape();
294 if (flags & SOGEN_MATERIAL_PER_PART) matnr++;
295 }
296 if (flags & SOGEN_GENERATE_BOTTOM) {
297 vertex.setMaterialIndex(matnr);
298 vertex.setDetail(&bottomDetail);
299 shape->beginShape(action, SoShape::TRIANGLE_FAN);
300 vertex.setNormal(SbVec3f(0.0f, -1.0f, 0.0f));
301
302 for (i = slices-1; i >= 0; i--) {
303 vertex.setTextureCoords(texcoords[i] + SbVec2f(0.5f, 0.5f));
304 vertex.setPoint(coords[i]);
305 shape->shapeVertex(&vertex);
306 }
307 shape->endShape();
308 }
309 }
310
generate_cube(const float width,const float height,const float depth,const unsigned int flags,SoShape * const shape,SoAction * const action)311 static void generate_cube(const float width,
312 const float height,
313 const float depth,
314 const unsigned int flags,
315 SoShape * const shape,
316 SoAction * const action) {
317 SbVec3f varray[8];
318 sogenerate_generate_cube_vertices(varray,
319 width * 0.5f,
320 height * 0.5f,
321 depth * 0.5f);
322
323
324 SoPrimitiveVertex vertex;
325 SoCubeDetail cubeDetail;
326 vertex.setDetail(&cubeDetail);
327 vertex.setMaterialIndex(0);
328
329 shape->beginShape(action, SoShape::QUADS);
330 int *iptr = sogenerate_cube_vindices;
331 const SbVec3f *nptr = sogenerate_cube_normals;
332 const SbVec2f *tptr = sogenerate_cube_texcoords;
333
334 for (int i = 0; i < 6; i++) { // 6 quads
335 vertex.setNormal(nptr[i]);
336 if (flags & SOGEN_MATERIAL_PER_PART) vertex.setMaterialIndex(i);
337 for (int j = 0; j < 4; j++) {
338 vertex.setTextureCoords(tptr[j]);
339 vertex.setPoint(varray[*iptr++]);
340 shape->shapeVertex(&vertex);
341 }
342 }
343 shape->endShape();
344 }
345
generate_sphere(const float radius,const int numstacks,const int numslices,SoShape * const shape,SoAction * const action)346 static void generate_sphere(const float radius,
347 const int numstacks,
348 const int numslices,
349 SoShape * const shape,
350 SoAction * const action) {
351 int stacks = numstacks;
352 int slices = numslices;
353
354 if (stacks < 3) stacks = 3;
355 if (slices < 4) slices = 4;
356
357 if (slices > 128) slices = 128;
358
359 // used to cache last stack's data
360 SbVec3f coords[129];
361 SbVec3f normals[129];
362 float S[129];
363
364 int i, j;
365 float rho;
366 float drho;
367 float theta;
368 float dtheta;
369 float tc, ts;
370 SbVec3f tmp;
371
372 drho = float(M_PI) / (float) (stacks-1);
373 dtheta = 2.0f * float(M_PI) / (float) slices;
374
375 float currs = 0.0f;
376 float incs = 1.0f / (float)slices;
377 rho = drho;
378 theta = 0.0f;
379 tc = (float) cos(rho);
380 ts = - (float) sin(rho);
381 tmp.setValue(0.0f,
382 tc,
383 ts);
384 normals[0] = tmp;
385 tmp *= radius;
386 coords[0] = tmp;
387 S[0] = currs;
388 float dT = 1.0f / (float) (stacks-1);
389 float T = 1.0f - dT;
390
391 SoPrimitiveVertex vertex;
392 shape->beginShape(action, SoShape::TRIANGLES);
393
394 for (j = 1; j <= slices; j++) {
395 vertex.setNormal(SbVec3f(0.0f, 1.0f, 0.0f));
396 vertex.setTextureCoords(SbVec2f(currs + 0.5f * incs, 1.0f));
397 vertex.setPoint(SbVec3f(0.0f, radius, 0.0f));
398 shape->shapeVertex(&vertex);
399
400 vertex.setNormal(normals[j-1]);
401 vertex.setTextureCoords(SbVec2f(currs, T));
402 vertex.setPoint(coords[j-1]);
403 shape->shapeVertex(&vertex);
404
405 currs += incs;
406 theta += dtheta;
407 S[j] = currs;
408 tmp.setValue(float(sin(theta))*ts,
409 tc,
410 float(cos(theta))*ts);
411
412 normals[j] = tmp;
413 tmp *= radius;
414 coords[j] = tmp;
415
416 vertex.setNormal(normals[j]);
417 vertex.setTextureCoords(SbVec2f(currs, T));
418 vertex.setPoint(coords[j]);
419 shape->shapeVertex(&vertex);
420
421 }
422 shape->endShape();
423
424 rho += drho;
425
426 for (i = 2; i < stacks-1; i++) {
427 tc = (float)cos(rho);
428 ts = - (float) sin(rho);
429 shape->beginShape(action, SoShape::QUAD_STRIP);
430 theta = 0.0f;
431 for (j = 0; j <= slices; j++) {
432 vertex.setTextureCoords(SbVec2f(S[j], T));
433 vertex.setNormal(normals[j]);
434 vertex.setPoint(coords[j]);
435 shape->shapeVertex(&vertex);
436
437 vertex.setTextureCoords(SbVec2f(S[j], T-dT));
438 tmp.setValue(float(sin(theta))*ts,
439 tc,
440 float(cos(theta))*ts);
441 normals[j] = tmp;
442 vertex.setNormal(tmp);
443 tmp *= radius;
444 coords[j] = tmp;
445 theta += dtheta;
446 vertex.setPoint(tmp);
447 shape->shapeVertex(&vertex);
448 }
449 shape->endShape();
450 rho += drho;
451 T -= dT;
452 }
453
454 shape->beginShape(action, SoShape::TRIANGLES);
455 for (j = 0; j < slices; j++) {
456 vertex.setTextureCoords(SbVec2f(S[j], T));
457 vertex.setNormal(normals[j]);
458 vertex.setPoint(coords[j]);
459 shape->shapeVertex(&vertex);
460
461 vertex.setTextureCoords(SbVec2f(S[j]+incs*0.5f, 0.0f));
462 vertex.setNormal(SbVec3f(0.0f, -1.0f, 0.0f));
463 vertex.setPoint(SbVec3f(0.0f, -radius, 0.0f));
464 shape->shapeVertex(&vertex);
465
466 vertex.setTextureCoords(SbVec2f(S[j+1], T));
467 vertex.setNormal(normals[j+1]);
468 vertex.setPoint(coords[j+1]);
469 shape->shapeVertex(&vertex);
470 }
471 shape->endShape();
472 }
473 };
474
475 void
sogen_generate_cone(const float bottomRadius,const float height,const int numslices,const unsigned int flags,SoShape * const shape,SoAction * const action)476 sogen_generate_cone(const float bottomRadius,
477 const float height,
478 const int numslices,
479 const unsigned int flags,
480 SoShape * const shape,
481 SoAction * const action)
482 {
483 so_generate_prim_private::generate_cone(bottomRadius,
484 height,
485 numslices,
486 flags,
487 shape,
488 action);
489 }
490
491 void
sogen_generate_cylinder(const float radius,const float height,const int numslices,const unsigned int flags,SoShape * const shape,SoAction * const action)492 sogen_generate_cylinder(const float radius,
493 const float height,
494 const int numslices,
495 const unsigned int flags,
496 SoShape * const shape,
497 SoAction * const action)
498 {
499 so_generate_prim_private::generate_cylinder(radius,
500 height,
501 numslices,
502 flags,
503 shape,
504 action);
505 }
506
507 void
sogen_generate_sphere(const float radius,const int numstacks,const int numslices,SoShape * const shape,SoAction * const action)508 sogen_generate_sphere(const float radius,
509 const int numstacks,
510 const int numslices,
511 SoShape * const shape,
512 SoAction * const action)
513 {
514 so_generate_prim_private::generate_sphere(radius,
515 numstacks,
516 numslices,
517 shape,
518 action);
519 }
520
521 void
sogen_generate_cube(const float width,const float height,const float depth,const unsigned int flags,SoShape * const shape,SoAction * const action)522 sogen_generate_cube(const float width,
523 const float height,
524 const float depth,
525 const unsigned int flags,
526 SoShape * const shape,
527 SoAction * const action)
528 {
529 so_generate_prim_private::generate_cube(width,
530 height,
531 depth,
532 flags,
533 shape,
534 action);
535 }
536