From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / hacks / glx / b_sphere.c
1 #if 0
2 static const char sccsid[] = "@(#)b_sphere.c  4.11 98/06/16 xlockmore";
3 #endif
4
5 /*-
6  * BUBBLE3D (C) 1998 Richard W.M. Jones.
7  * b_sphere.c: Create a list of vertices and triangles in a
8  * normalized sphere, which is then later used as the basic shape
9  * for all bubbles. This code is run once when the program starts
10  * up.
11  */
12
13 #include "bubble3d.h"
14
15 typedef glb_vertex vertex;
16 typedef glb_triangle triangle;
17
18 struct glb_data {
19
20   /* The list of vertices created. */
21   vertex *vertices;
22   int nr_vertices, nr_vertices_allocated;
23
24   /* The list of triangles created. */
25   triangle *triangles;
26   int nr_triangles, nr_triangles_allocated;
27 };
28
29
30 #define EPSILON GLB_VERTICES_EPSILON
31
32 /* Should be taken care of already... but just in case */
33 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
34 #undef inline
35 #define inline                  /* */
36 #endif
37 static inline int
38 close_enough(const GLfloat * v1, const GLfloat * v2)
39 {
40         return fabs((double) (v1[0] - v2[0])) <= EPSILON &&
41                 fabs((double) (v1[1] - v2[1])) <= EPSILON &&
42                 fabs((double) (v1[2] - v2[2])) <= EPSILON;
43 }
44
45 #define INCR(n) ((n == 0) ? (n = 1) : (n *= 2))
46 #define INCR_ALLOCATION(a, n, t) (a = (t *) realloc (a, INCR (n) * sizeof (t)))
47
48 static inline GLuint
49 save_vertex(glb_data *d, const GLfloat * v)
50 {
51         int         i;
52
53         /* Inefficient, but we only do this a few times. Check to see if there's
54          * an existing vertex which is `close enough' to this one.
55          */
56         for (i = 0; i < d->nr_vertices; ++i)
57                 if (close_enough(v, d->vertices[i]))
58                         return i;
59
60         if (d->nr_vertices_allocated <= d->nr_vertices) {
61                 if (d->vertices == 0) {
62                         d->vertices = (vertex *) malloc(INCR(d->nr_vertices_allocated) * sizeof (vertex));
63                 } else {
64                         INCR_ALLOCATION(d->vertices, d->nr_vertices_allocated, vertex);
65                 }
66         }
67         d->vertices[d->nr_vertices][0] = v[0];
68         d->vertices[d->nr_vertices][1] = v[1];
69         d->vertices[d->nr_vertices][2] = v[2];
70         return d->nr_vertices++;
71 }
72
73 static inline GLuint
74 save_triangle(glb_data *d, GLuint v1, GLuint v2, GLuint v3)
75 {
76         if (d->nr_triangles_allocated <= d->nr_triangles) {
77                 if (d->triangles == 0) {
78                         d->triangles = (triangle *) malloc(INCR(d->nr_triangles_allocated) * sizeof (triangle));
79                 } else {
80                         INCR_ALLOCATION(d->triangles, d->nr_triangles_allocated, triangle);
81                 }
82         }
83         d->triangles[d->nr_triangles][0] = v1;
84         d->triangles[d->nr_triangles][1] = v2;
85         d->triangles[d->nr_triangles][2] = v3;
86         return d->nr_triangles++;
87 }
88
89 static inline void
90 normalize(GLfloat v[3])
91 {
92         GLfloat     d = (GLfloat) sqrt((double) (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]));
93
94         if (d != 0) {
95                 v[0] /= d;
96                 v[1] /= d;
97                 v[2] /= d;
98         } else {
99                 v[0] = v[1] = v[2] = 0;
100         }
101 }
102
103 static void
104 subdivide(glb_data *d,
105           const GLfloat * v1, GLuint vi1,
106           const GLfloat * v2, GLuint vi2,
107           const GLfloat * v3, GLuint vi3,
108           int depth)
109 {
110         int         i;
111
112         if (depth == 0) {
113                 save_triangle(d, vi1, vi2, vi3);
114         } else {
115                 GLuint      vi12, vi23, vi31;
116                 GLfloat     v12[3], v23[3], v31[3];
117
118                 for (i = 0; i < 3; ++i) {
119                         v12[i] = v1[i] + v2[i];
120                         v23[i] = v2[i] + v3[i];
121                         v31[i] = v3[i] + v1[i];
122                 }
123                 normalize(v12);
124                 vi12 = save_vertex(d, v12);
125                 normalize(v23);
126                 vi23 = save_vertex(d, v23);
127                 normalize(v31);
128                 vi31 = save_vertex(d, v31);
129                 subdivide(d, v1, vi1, v12, vi12, v31, vi31, depth - 1);
130                 subdivide(d, v2, vi2, v23, vi23, v12, vi12, depth - 1);
131                 subdivide(d, v3, vi3, v31, vi31, v23, vi23, depth - 1);
132                 subdivide(d, v12, vi12, v23, vi23, v31, vi31, depth - 1);
133         }
134 }
135
136 #define ICO_X 0.525731112119133606
137 #define ICO_Z 0.850650808352039932
138
139 static const GLfloat vdata[12][3] =
140 {
141         {-ICO_X, 0, ICO_Z},
142         {ICO_X, 0, ICO_Z},
143         {-ICO_X, 0, -ICO_Z},
144         {ICO_X, 0, -ICO_Z},
145         {0, ICO_Z, ICO_X},
146         {0, ICO_Z, -ICO_X},
147         {0, -ICO_Z, ICO_X},
148         {0, -ICO_Z, -ICO_X},
149         {ICO_Z, ICO_X, 0},
150         {-ICO_Z, ICO_X, 0},
151         {ICO_Z, -ICO_X, 0},
152         {-ICO_Z, -ICO_X, 0}
153 };
154
155 static const GLuint tindices[20][3] =
156 {
157         {0, 4, 1},
158         {0, 9, 4},
159         {9, 5, 4},
160         {4, 5, 8},
161         {4, 8, 1},
162         {8, 10, 1},
163         {8, 3, 10},
164         {5, 3, 8},
165         {5, 2, 3},
166         {2, 7, 3},
167         {7, 10, 3},
168         {7, 6, 10},
169         {7, 11, 6},
170         {11, 0, 6},
171         {0, 1, 6},
172         {6, 1, 10},
173         {9, 0, 11},
174         {9, 11, 2},
175         {9, 2, 5},
176         {7, 2, 11}
177 };
178
179 /* Public interface: Create the sphere. */
180 glb_data *
181 glb_sphere_init(void)
182 {
183         glb_data *d = (glb_data *) calloc (1, sizeof (*d));
184         int         i;
185
186         for (i = 0; i < 20; ++i) {
187                 subdivide(d, vdata[tindices[i][0]], save_vertex(d, vdata[tindices[i][0]]),
188                    vdata[tindices[i][1]], save_vertex(d, vdata[tindices[i][1]]),
189                    vdata[tindices[i][2]], save_vertex(d, vdata[tindices[i][2]]),
190                           glb_config.subdivision_depth);
191         }
192
193         return d;
194 }
195
196 /* Return the vertices list. */
197 glb_vertex *
198 glb_sphere_get_vertices(glb_data *d, int *nr_vertices_ptr)
199 {
200         *nr_vertices_ptr = d->nr_vertices;
201         return d->vertices;
202 }
203
204 /* Return the triangles list. */
205 glb_triangle *
206 glb_sphere_get_triangles(glb_data *d, int *nr_triangles_ptr)
207 {
208         *nr_triangles_ptr = d->nr_triangles;
209         return d->triangles;
210 }
211
212 /* Free up memory. */
213 void
214 glb_sphere_end(glb_data *d)
215 {
216         (void) free((void *) d->vertices);
217         (void) free((void *) d->triangles);
218         free (d);
219 }