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