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