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