http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[xscreensaver] / hacks / glx / tube.c
1 /* tube, Copyright (c) 2001-2011 Jamie Zawinski <jwz@jwz.org>
2  * Utility functions to create tubes and cones in GL.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #include <math.h>
14
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18
19 #include <stdlib.h>
20
21 #ifdef HAVE_COCOA
22 # include <OpenGL/gl.h>
23 #else
24 # include <GL/gl.h>
25 #endif
26
27 #include "tube.h"
28
29 typedef struct { GLfloat x, y, z; } XYZ;
30
31
32 static int
33 unit_tube (int faces, int smooth, int caps_p, int wire_p)
34 {
35   int i;
36   int polys = 0;
37   GLfloat step = M_PI * 2 / faces;
38   GLfloat s2 = step/2;
39   GLfloat th;
40   GLfloat x, y, x0=0, y0=0;
41   int z = 0;
42
43   int arraysize, out;
44   struct { XYZ p; XYZ n; GLfloat s, t; } *array;
45
46   arraysize = (faces+1) * 6;
47   array = (void *) calloc (arraysize, sizeof(*array));
48   if (! array) abort();
49   out = 0;
50
51
52   /* #### texture coords are currently not being computed */
53
54
55   /* side walls
56    */
57   th = 0;
58   x = 1;
59   y = 0;
60
61   if (!smooth)
62     {
63       x0 = cos (s2);
64       y0 = sin (s2);
65     }
66
67   if (smooth) faces++;
68
69   for (i = 0; i < faces; i++)
70     {
71       array[out].p.x = x;                       /* bottom point A */
72       array[out].p.y = 0;
73       array[out].p.z = y;
74
75       if (smooth)
76         array[out].n = array[out].p;            /* its own normal */
77       else
78         {
79           array[out].n.x = x0;                  /* mid-plane normal */
80           array[out].n.y = 0;
81           array[out].n.z = y0;
82         }
83       out++;
84
85
86       array[out].p.x = x;                       /* top point A */
87       array[out].p.y = 1;
88       array[out].p.z = y;
89       array[out].n = array[out-1].n;            /* same normal */
90       out++;
91
92
93       th += step;
94       x  = cos (th);
95       y  = sin (th);
96
97       if (!smooth)
98         {
99           x0 = cos (th + s2);
100           y0 = sin (th + s2);
101
102           array[out].p.x = x;                   /* top point B */
103           array[out].p.y = 1;
104           array[out].p.z = y;
105           array[out].n = array[out-1].n;        /* same normal */
106           out++;
107
108
109           array[out] = array[out-3];            /* bottom point A */
110           out++;
111
112           array[out] = array[out-2];            /* top point B */
113           out++;
114
115           array[out].p.x = x;                   /* bottom point B */
116           array[out].p.y = 0;
117           array[out].p.z = y;
118           array[out].n = array[out-1].n;        /* same normal */
119           out++;
120
121           polys++;
122         }
123
124       polys++;
125       if (out >= arraysize) abort();
126     }
127
128   glEnableClientState (GL_VERTEX_ARRAY);
129   glEnableClientState (GL_NORMAL_ARRAY);
130   glEnableClientState (GL_TEXTURE_COORD_ARRAY);
131
132   glVertexPointer   (3, GL_FLOAT, sizeof(*array), &array[0].p);
133   glNormalPointer   (   GL_FLOAT, sizeof(*array), &array[0].n);
134   glTexCoordPointer (2, GL_FLOAT, sizeof(*array), &array[0].s);
135
136   glDrawArrays ((wire_p ? GL_LINES :
137                  (smooth ? GL_TRIANGLE_STRIP : GL_TRIANGLES)),
138                 0, out);
139
140
141   /* End caps
142    */
143   if (caps_p)
144     for (z = 0; z <= 1; z++)
145       {
146         out = 0;
147         if (! wire_p)
148           {
149             array[out].p.x = 0;
150             array[out].p.y = z;
151             array[out].p.z = 0;
152
153             array[out].n.x = 0;
154             array[out].n.y = (z == 0 ? -1 : 1);
155             array[out].n.z = 0;
156             out++;
157           }
158
159         th = 0;
160         for (i = (z == 0 ? 0 : faces);
161              (z == 0 ? i <= faces : i >= 0);
162              i += (z == 0 ? 1 : -1)) {
163             GLfloat x = cos (th);
164             GLfloat y = sin (th);
165
166             array[out] = array[0];  /* same normal and texture */
167             array[out].p.x = x;
168             array[out].p.y = z;
169             array[out].p.z = y;
170             out++;
171
172             th += (z == 0 ? step : -step);
173
174             polys++;
175             if (out >= arraysize) abort();
176           }
177
178         glVertexPointer   (3, GL_FLOAT, sizeof(*array), &array[0].p);
179         glNormalPointer   (   GL_FLOAT, sizeof(*array), &array[0].n);
180         glTexCoordPointer (2, GL_FLOAT, sizeof(*array), &array[0].s);
181
182         glDrawArrays ((wire_p ? GL_LINE_LOOP : GL_TRIANGLE_FAN), 0, out);
183       }
184
185   free(array);
186
187   return polys;
188 }
189
190
191 static int
192 unit_cone (int faces, int smooth, int cap_p, int wire_p)
193 {
194   int i;
195   int polys = 0;
196   GLfloat step = M_PI * 2 / faces;
197   GLfloat s2 = step/2;
198   GLfloat th;
199   GLfloat x, y, x0, y0;
200
201   int arraysize, out;
202   struct { XYZ p; XYZ n; GLfloat s, t; } *array;
203
204   arraysize = (faces+1) * 3;
205   array = (void *) calloc (arraysize, sizeof(*array));
206   if (! array) abort();
207   out = 0;
208
209
210   /* #### texture coords are currently not being computed */
211
212
213   /* side walls
214    */
215
216   th = 0;
217   x = 1;
218   y = 0;
219   x0 = cos (s2);
220   y0 = sin (s2);
221
222   for (i = 0; i < faces; i++)
223     {
224       array[out].p.x = x;               /* bottom point A */
225       array[out].p.y = 0;
226       array[out].p.z = y;
227
228       if (smooth)
229         array[out].n = array[out].p;    /* its own normal */
230       else
231         {
232           array[out].n.x = x0;          /* mid-plane normal */
233           array[out].n.y = 0;
234           array[out].n.z = y0;
235         }
236       out++;
237
238
239       array[out].p.x = 0;               /* tip point */
240       array[out].p.y = 1;
241       array[out].p.z = 0;
242
243       array[out].n.x = x0;              /* mid-plane normal */
244       array[out].n.y = 0;
245       array[out].n.z = y0;
246       out++;
247
248
249       th += step;
250       x0 = cos (th + s2);
251       y0 = sin (th + s2);
252       x  = cos (th);
253       y  = sin (th);
254
255       array[out].p.x = x;               /* bottom point B */
256       array[out].p.y = 0;
257       array[out].p.z = y;
258
259       if (smooth)
260         array[out].n = array[out].p;    /* its own normal */
261       else
262         array[out].n = array[out-1].n;  /* same normal as other two */
263       out++;
264
265
266       if (out >= arraysize) abort();
267       polys++;
268     }
269
270   glEnableClientState (GL_VERTEX_ARRAY);
271   glEnableClientState (GL_NORMAL_ARRAY);
272   glEnableClientState (GL_TEXTURE_COORD_ARRAY);
273
274   glVertexPointer   (3, GL_FLOAT, sizeof(*array), &array[0].p);
275   glNormalPointer   (   GL_FLOAT, sizeof(*array), &array[0].n);
276   glTexCoordPointer (2, GL_FLOAT, sizeof(*array), &array[0].s);
277
278   glDrawArrays ((wire_p ? GL_LINES : GL_TRIANGLES), 0, out);
279
280
281   /* End cap
282    */
283   if (cap_p)
284     {
285       out = 0;
286
287       if (! wire_p)
288         {
289           array[out].p.x = 0;
290           array[out].p.y = 0;
291           array[out].p.z = 0;
292
293           array[out].n.x = 0;
294           array[out].n.y = -1;
295           array[out].n.z = 0;
296           out++;
297         }
298
299       for (i = 0, th = 0; i <= faces; i++)
300         {
301           GLfloat x = cos (th);
302           GLfloat y = sin (th);
303
304           array[out] = array[0];  /* same normal and texture */
305           array[out].p.x = x;
306           array[out].p.y = 0;
307           array[out].p.z = y;
308           out++;
309           th += step;
310           polys++;
311           if (out >= arraysize) abort();
312         }
313
314       glVertexPointer   (3, GL_FLOAT, sizeof(*array), &array[0].p);
315       glNormalPointer   (   GL_FLOAT, sizeof(*array), &array[0].n);
316       glTexCoordPointer (2, GL_FLOAT, sizeof(*array), &array[0].s);
317
318       glDrawArrays ((wire_p ? GL_LINE_LOOP : GL_TRIANGLE_FAN), 0, out);
319     }
320
321   free (array);
322
323   return polys;
324 }
325
326
327 static int
328 tube_1 (GLfloat x1, GLfloat y1, GLfloat z1,
329         GLfloat x2, GLfloat y2, GLfloat z2,
330         GLfloat diameter, GLfloat cap_size,
331         int faces, int smooth, int caps_p, int wire_p,
332         int cone_p)
333 {
334   GLfloat length, X, Y, Z;
335   int polys = 0;
336
337   if (diameter <= 0) abort();
338
339   X = (x2 - x1);
340   Y = (y2 - y1);
341   Z = (z2 - z1);
342
343   if (X == 0 && Y == 0 && Z == 0)
344     return 0;
345
346   length = sqrt (X*X + Y*Y + Z*Z);
347
348   glPushMatrix();
349
350   glTranslatef(x1, y1, z1);
351   glRotatef (-atan2 (X, Y)               * (180 / M_PI), 0, 0, 1);
352   glRotatef ( atan2 (Z, sqrt(X*X + Y*Y)) * (180 / M_PI), 1, 0, 0);
353   glScalef (diameter, length, diameter);
354
355   /* extend the endpoints of the tube by the cap size in both directions */
356   if (cap_size != 0)
357     {
358       GLfloat c = cap_size/length;
359       glTranslatef (0, -c, 0);
360       glScalef (1, 1+c+c, 1);
361     }
362
363   if (cone_p)
364     polys = unit_cone (faces, smooth, caps_p, wire_p);
365   else
366     polys = unit_tube (faces, smooth, caps_p, wire_p);
367
368   glPopMatrix();
369   return polys;
370 }
371
372
373 int
374 tube (GLfloat x1, GLfloat y1, GLfloat z1,
375       GLfloat x2, GLfloat y2, GLfloat z2,
376       GLfloat diameter, GLfloat cap_size,
377       int faces, int smooth, int caps_p, int wire_p)
378 {
379   return tube_1 (x1, y1, z1, x2, y2, z2, diameter, cap_size,
380                  faces, smooth, caps_p, wire_p,
381                  0);
382 }
383
384
385 int
386 cone (GLfloat x1, GLfloat y1, GLfloat z1,
387       GLfloat x2, GLfloat y2, GLfloat z2,
388       GLfloat diameter, GLfloat cap_size,
389       int faces, int smooth, int cap_p, int wire_p)
390 {
391   return tube_1 (x1, y1, z1, x2, y2, z2, diameter, cap_size,
392                  faces, smooth, cap_p, wire_p,
393                  1);
394 }