From http://www.jwz.org/xscreensaver/xscreensaver-5.16.tar.gz
[xscreensaver] / hacks / glx / bubble3d.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* bubble3d.c - 3D bubbles  */
3
4 #if 0
5 static const char sccsid[] = "@(#)bubble3d.c  4.11 98/06/16 xlockmore";
6 #endif
7
8 /*-
9  * BUBBLE3D (C) 1998 Richard W.M. Jones.
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * Revision History:
23  * 16-Jun-98: Written.
24  *
25  * bubble.c: This code is responsible for creating and managing
26  * bubbles over their lifetime.
27  * The bubbles may be drawn inside out.
28  */
29
30 #include "bubble3d.h"
31
32 typedef struct bubble {
33         GLfloat    *contributions;      /* List of contributions from each
34                                          * nudge to each vertex. This list has
35                                          * length nr_vertices * nr_nudge_axes.
36                                          */
37         GLfloat     x, y, z;    /* (x,y,z) location of the bubble. */
38         GLfloat     scale;      /* Scaling factor applied to bubble. */
39         GLfloat     y_incr, scale_incr;         /* Change in y and scale each frame. */
40         GLfloat     rotx, roty, rotz;   /* Current rotation. */
41         GLfloat     rotx_incr, roty_incr, rotz_incr;    /* Amount by which we increase
42                                                          * rotation each step.
43                                                          */
44         GLfloat    *nudge_angle;        /* Current angle (radians) of each
45                                          * nudge. This list has length nr_nudge_axes.
46                                          */
47         GLfloat    *nudge_angle_incr;   /* Amount by which we increase each nudge
48                                          * angle in each frame.
49                                          */
50         GLfloat    color[4];
51 } bubble;
52
53 /* Should be taken care of already... but just in case */
54 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
55 #undef inline
56 #define inline                  /* */
57 #endif
58 static inline void
59 normalize(GLfloat v[3])
60 {
61         GLfloat     d = (GLfloat) sqrt((double) (v[0] * v[0] + v[1] * v[1] +
62                                                  v[2] * v[2]));
63
64         if (d != 0) {
65                 v[0] /= d;
66                 v[1] /= d;
67                 v[2] /= d;
68         } else {
69                 v[0] = v[1] = v[2] = 0;
70         }
71 }
72
73 static inline GLfloat
74 dotprod(GLfloat * v1, GLfloat * v2)
75 {
76         return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
77 }
78
79 static inline GLfloat
80 max(GLfloat a, GLfloat b)
81 {
82         return a > b ? a : b;
83 }
84
85 /* Create a new bubble. */
86 void       *
87 glb_bubble_new(glb_data *d, GLfloat x, GLfloat y, GLfloat z, GLfloat scale,
88                GLfloat y_incr, GLfloat scale_incr)
89 {
90         int         i, j;
91
92         /* GLfloat axes [glb_config.nr_nudge_axes][3]; */
93         GLfloat     axes[5][3]; /* HARD CODED for SunCC */
94         int         nr_vertices;
95         glb_vertex *vertices = glb_sphere_get_vertices(d, &nr_vertices);
96
97         bubble     *b = (bubble *) malloc(sizeof *b);
98
99         if (b == 0)
100                 return 0;
101
102         if (glb_config.bubble_colour[0] == -1.0) {
103                 b->color[0] = ((float) (NRAND(100)) / 100.0);
104                 b->color[1] = ((float) (NRAND(100)) / 100.0);
105                 b->color[2] = ((float) (NRAND(100)) / 100.0);
106         } else {
107                 b->color[0] = glb_config.bubble_colour[0];
108                 b->color[1] = glb_config.bubble_colour[1];
109                 b->color[2] = glb_config.bubble_colour[2];
110         }
111         b->color[3] = glb_config.bubble_colour[3];
112         
113
114         b->contributions = (GLfloat *) malloc(sizeof (GLfloat) * nr_vertices *
115                                               glb_config.nr_nudge_axes);
116         if (b->contributions == 0) {
117                 (void) free((void *) b);
118                 return 0;
119         }
120         b->nudge_angle = (GLfloat *) malloc(sizeof (GLfloat) * glb_config.nr_nudge_axes);
121         if (b->nudge_angle == 0) {
122                 (void) free((void *) b->contributions);
123                 (void) free((void *) b);
124                 return 0;
125         }
126         b->nudge_angle_incr = (GLfloat *) malloc(sizeof (GLfloat) * glb_config.nr_nudge_axes);
127         if (b->nudge_angle_incr == 0) {
128                 (void) free((void *) b->nudge_angle);
129                 (void) free((void *) b->contributions);
130                 (void) free((void *) b);
131                 return 0;
132         }
133         /* Initialize primitive elements. */
134         b->x = x;
135         b->y = y;
136         b->z = z;
137         b->scale = scale;
138         b->y_incr = y_incr;
139         b->scale_incr = scale_incr;
140         b->rotx = b->roty = b->rotz = 0;
141         b->rotx_incr = glb_drand() * glb_config.rotation_factor * 2
142                 - glb_config.rotation_factor;
143         b->roty_incr = glb_drand() * glb_config.rotation_factor * 2
144                 - glb_config.rotation_factor;
145         b->rotz_incr = glb_drand() * glb_config.rotation_factor * 2
146                 - glb_config.rotation_factor;
147
148         /* Initialize the nudge angle arrays. */
149         for (i = 0; i < glb_config.nr_nudge_axes; ++i) {
150                 b->nudge_angle[i] = 0;
151                 b->nudge_angle_incr[i] = glb_drand() * glb_config.nudge_angle_factor;
152         }
153
154         /* Choose some random nudge axes. */
155         for (i = 0; i < glb_config.nr_nudge_axes; ++i) {
156                 axes[i][0] = glb_drand() * 2 - 1;
157                 axes[i][1] = glb_drand() * 2 - 1;
158                 axes[i][2] = glb_drand() * 2 - 1;
159                 normalize(axes[i]);
160         }
161
162         /* Calculate the contribution that each nudge axis has on each vertex. */
163         for (i = 0; i < nr_vertices; ++i)
164                 for (j = 0; j < glb_config.nr_nudge_axes; ++j)
165                         b->contributions[i * glb_config.nr_nudge_axes + j]
166                                 = max(0, dotprod(vertices[i], axes[j]));
167
168         return (void *) b;
169 }
170
171 /* Delete a bubble and free up all memory. */
172 void
173 glb_bubble_delete(void *bb)
174 {
175         bubble     *b = (bubble *) bb;
176
177         if (b != NULL) {
178                 if (b->nudge_angle_incr) {
179                         (void) free((void *) b->nudge_angle_incr);
180                         b->nudge_angle_incr = NULL;
181                 }
182                 if (b->nudge_angle) {
183                         (void) free((void *) b->nudge_angle);
184                         b->nudge_angle = NULL;
185                 }
186                 if (b->contributions) {
187                         (void) free((void *) b->contributions);
188                         b->contributions = NULL;
189                 }
190                 (void) free((void *) b);
191                 b = NULL;
192         }
193 }
194
195 /* Rotate and wobble a bubble by a single step. */
196 void
197 glb_bubble_step(void *bb)
198 {
199         int         i;
200         bubble     *b = (bubble *) bb;
201
202         /* Update the rotation. */
203         b->rotx += b->rotx_incr;
204         b->roty += b->roty_incr;
205         b->rotz += b->rotz_incr;
206
207         /* Update the nudge angles. */
208         for (i = 0; i < glb_config.nr_nudge_axes; ++i)
209                 b->nudge_angle[i] += b->nudge_angle_incr[i];
210
211         /* Move it upwards & outwards. */
212         b->y += b->y_incr;
213         b->scale += b->scale_incr;
214 }
215
216 /* Draw a bubble. */
217 void
218 glb_bubble_draw(glb_data *d, void *bb)
219 {
220         int         i, j;
221         bubble     *b = (bubble *) bb;
222         int         nr_vertices;
223         glb_vertex *vertices = glb_sphere_get_vertices(d, &nr_vertices);
224         int         nr_triangles;
225         glb_triangle *triangles = glb_sphere_get_triangles(d, &nr_triangles);
226         glb_vertex *new_vertices;
227
228         new_vertices = (glb_vertex *) malloc(sizeof (glb_vertex) * nr_vertices);
229         /* Calculate the vertices of this bubble, factoring in each nudge axis. */
230         for (i = 0; i < nr_vertices; ++i) {
231                 GLfloat     s = 0;
232
233                 for (j = 0; j < glb_config.nr_nudge_axes; ++j)
234                         s += ((GLfloat) cos((double) (b->nudge_angle[j])) *
235                               glb_config.nudge_factor - glb_config.nudge_factor / 2) *
236                                 b->contributions[i * glb_config.nr_nudge_axes + j];
237
238                 new_vertices[i][0] = vertices[i][0] * (s + 1);
239                 new_vertices[i][1] = vertices[i][1] * (s + 1);
240                 new_vertices[i][2] = vertices[i][2] * (s + 1);
241         }
242
243         glPushMatrix();
244
245         /* Apply translation, rotation and scalings. */
246         glTranslatef(b->x, b->y, b->z);
247
248         glRotatef(b->rotx, 1, 0, 0);
249         glRotatef(b->roty, 0, 1, 0);
250         glRotatef(b->rotz, 0, 0, 1);
251
252         glScalef(b->scale, b->scale, b->scale);
253
254         /* Draw the bubble. */
255     glFrontFace(GL_CW);
256         glBegin(GL_TRIANGLES);
257
258         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, b->color);
259
260         for (i = 0; i < nr_triangles; ++i) {
261                 glNormal3fv(new_vertices[triangles[i][0]]);
262                 glVertex3fv(new_vertices[triangles[i][0]]);
263                 glNormal3fv(new_vertices[triangles[i][1]]);
264                 glVertex3fv(new_vertices[triangles[i][1]]);
265                 glNormal3fv(new_vertices[triangles[i][2]]);
266                 glVertex3fv(new_vertices[triangles[i][2]]);
267         }
268         glEnd();
269         glPopMatrix();
270         (void) free((void *) new_vertices);
271     glb_config.polygon_count += nr_triangles;
272 }
273
274 /* Return y value. */
275 GLfloat
276 glb_bubble_get_y(void *bb)
277 {
278         bubble     *b = (bubble *) bb;
279
280         return b->y;
281 }