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