8d508330ae0674879f7a5bc05cc330e81834ba77
[xscreensaver] / hacks / glx / klein.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* Klein --- Klein Bottle, Moebius and other parametric surfaces
3  * visualization */
4
5 /*
6  * Revision History:
7  * 2000: written by Andrey Mirtchovski <mirtchov@cpsc.ucalgary.ca
8  *       
9  * 01-Mar-2003  mirtchov    modified as a xscreensaver hack
10  *
11  */
12
13 /*-
14  * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
15  * otherwise caddr_t is not defined correctly
16  */
17
18 #include <X11/Intrinsic.h>
19
20 #ifdef STANDALONE
21 # define PROGCLASS                                      "Klein"
22 # define HACK_INIT                                      init_klein
23 # define HACK_DRAW                                      draw_klein
24 # define HACK_RESHAPE                           reshape_klein
25 # define HACK_HANDLE_EVENT                      klein_handle_event
26 # define EVENT_MASK                                     PointerMotionMask
27 # define klein_opts                                     xlockmore_opts
28
29
30 #define DEF_SPIN                                "True"
31 #define DEF_WANDER                              "False"
32 #define DEF_RANDOM                              "False"
33 #define DEF_SPEED                               "150"
34
35 # define DEFAULTS                                       "*delay:                20000   \n"                     \
36                                                                         "*showFPS:      False   \n"                     \
37                                                                         "*wireframe:    False   \n"                     \
38                                                                         "*random:          " DEF_RANDOM     "\n"        \
39                                                                         "*speed:           " DEF_SPEED     "\n" \
40                                                                         "*spin:        " DEF_SPIN      "\n" \
41                                                                         "*wander:      " DEF_WANDER    "\n" \
42
43 # include "xlockmore.h"         /* from the xscreensaver distribution */
44 #else  /* !STANDALONE */
45 # include "xlock.h"                     /* from the xlockmore distribution */
46 #endif /* !STANDALONE */
47
48 #ifdef USE_GL
49
50 #include <GL/glu.h>
51 #include "rotator.h"
52 #include "gltrackball.h"
53
54 #undef countof
55 #define countof(x) (sizeof((x))/sizeof((*x)))
56
57 /* surfaces being drawn */
58 enum { 
59         KLEIN = 0,
60         DINI,
61         ENNEPER,
62         KUEN,
63         MOEBIUS,
64         SEASHELL,
65         SWALLOWTAIL,
66         BOHEM,
67     SURFACE_LAST
68 };
69
70 /* primitives to draw with 
71  * note that we skip the polygons and
72  * triangle fans -- too slow
73  *
74  * also removed triangle_strip and quads -- 
75  * just doesn't look good enough
76  */
77 enum {
78         MY_POINTS = 0,
79         MY_LINES,
80         MY_LINE_LOOP,
81         MY_PRIM_LAST
82 };
83
84
85 static Bool rand;
86 static int render;
87 static int speed;
88 static Bool do_spin;
89 static Bool do_wander;
90
91 static XrmOptionDescRec opts[] = {
92   {"-speed",   ".speed",    XrmoptionSepArg, (caddr_t) 0 },
93   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
94   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
95   { "-wander", ".wander", XrmoptionNoArg, "True" },
96   { "+wander", ".wander", XrmoptionNoArg, "False" },
97   { "-random", ".rand", XrmoptionNoArg, "True" },
98   { "+random", ".rand", XrmoptionNoArg, "False" },
99 };
100
101 static argtype vars[] = {
102   {&rand,      "rand",   "Random", DEF_RANDOM, t_Bool},
103   {&do_spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
104   {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
105   {&speed,     "speed",  "Speed",  DEF_SPEED,  t_Int},
106 };
107
108
109 ModeSpecOpt klein_opts = {countof(opts), opts, countof(vars), vars, NULL};
110
111
112
113 typedef struct{
114   GLfloat x;
115   GLfloat y;
116   GLfloat z;
117 } GL_VECTOR;
118
119 typedef struct {
120         GLXContext *glx_context;
121         Window      window;
122         rotator    *rot;
123         trackball_state *trackball;
124         Bool              button_down_p;
125
126         int render;
127         int surface;
128
129         float du, dv;
130         float a, b, c;
131
132 } kleinstruct;
133
134 static kleinstruct *klein = NULL;
135
136
137 static void
138 draw(ModeInfo *mi)
139 {
140         kleinstruct *kp = &klein[MI_SCREEN(mi)];
141         static float step = 0.0;
142         double u, v;
143         float coord[3];
144         
145         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
146
147         glEnable(GL_DEPTH_TEST);
148         glEnable(GL_NORMALIZE);
149         glEnable(GL_CULL_FACE);
150
151         glPushMatrix();
152
153         {
154                 double x, y, z;
155                 get_position (kp->rot, &x, &y, &z, !kp->button_down_p);
156                 glTranslatef((x - 0.5) * 10,
157                                                                  (y - 0.5) * 10,
158                                                                  (z - 0.5) * 20);
159
160                 gltrackball_rotate (kp->trackball);
161
162                 get_rotation (kp->rot, &x, &y, &z, !kp->button_down_p);
163                 glRotatef (x * 360, 1.0, 0.0, 0.0);
164                 glRotatef (y * 360, 0.0, 1.0, 0.0);
165                 glRotatef (z * 360, 0.0, 0.0, 1.0);
166         }
167
168         glScalef( 4.0, 4.0, 4.0 );
169
170         glBegin(kp->render);
171         switch(kp->surface) {
172         case KLEIN:
173                 for(u = -M_PI; u < M_PI; u+=kp->du){
174                         for(v = -M_PI; v < M_PI; v+=kp->dv){
175                                 coord[0] = cos(u)*(kp->a + sin(v)*cos(u/2) -
176                                                         sin(2*v)*sin(u/2)/2);
177                                 coord[1] = sin(u)*(kp->a + sin(v)*cos(u/2) -
178                                                         sin(2*v)*sin(u/2)/2);
179                                 coord[2] = sin(u/2)*sin(v) + cos(u/2)*sin(2*v)/2;
180                                 glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
181                                 glVertex3fv(coord);
182                         }
183                 }
184                 break;
185                 case DINI:
186                         for(u = -M_PI; u < M_PI; u+=kp->du){
187                                 for(v = -M_PI; v < M_PI; v+=kp->dv){
188                                         coord[0] = kp->a*cos(u)*sin(v);
189                                         coord[1] = kp->a*sin(u)*sin(v);
190                                         coord[2] = kp->a*(cos(v) + sin(tan((v/2))))+0.2*u;
191                                         glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
192                                         glVertex3fv(coord);
193                                 }
194                         }
195                         break;
196                 case ENNEPER:
197                         for(u = -M_PI; u < M_PI; u+=kp->du){
198                                 for(v = -M_PI; v < M_PI; v+=kp->dv){
199                                         coord[0] = kp->a*(u-(u*u*u/3)+u*v*v);
200                                         coord[1] = kp->b*(v-(v*v*v/3)+u*u*v);
201                                         coord[2] = u*u-v*v;
202                                         glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
203                                         glVertex3fv(coord);
204                                 }
205                         }
206                         break;
207                 case KUEN:
208                         for(u = -M_PI; u < M_PI; u+=kp->du){
209                                 for(v = -M_PI; v < M_PI; v+=kp->dv){
210                                         coord[0] = 2*(cos(u)+u*sin(u))*sin(v)/(1+u*u*sin(v)*sin(v));
211                                         coord[1] = 2*(sin(u)-u*cos(u))*sin(v)/(1+u*u*sin(v)*sin(v));
212                                         coord[2] = sin(tan(v/2))+2*cos(v)/(1+u*u*sin(v)*sin(v));
213
214                                         glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
215                                         glVertex3fv(coord);
216                                 }
217                         }
218                         break;
219                 case MOEBIUS:
220                         for(u = -M_PI; u < M_PI; u+=kp->du){
221                                 for(v = -M_PI; v < M_PI; v+=kp->dv){
222                                         coord[0] = cos(u)+v*cos(u/2)*cos(u);
223                                         coord[1] = sin(u)+v*cos(u/2)*sin(u);
224                                         coord[2] = v*sin(u/2);
225                                         glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
226                                         glVertex3fv(coord);
227                                 }
228                         }
229                         break;
230                 case SEASHELL:
231                         for(u = 0; u < 2*M_PI; u+=kp->du){
232                                 for(v = 0; v < 2*M_PI; v+=kp->dv){
233                                         coord[0] = kp->a*(1-v/(2*M_PI))*cos(2*v)*(1+cos(u))+sin(kp->c+=0.00001)*cos(2*v);
234                                         coord[1] = kp->a*(1-v/(2*M_PI))*sin(2*v)*(1+cos(u))+cos(kp->c+=0.00001)*sin(2*v);
235                                         coord[2] = sin(kp->b+=0.00001)*v/(2*M_PI)+kp->a*(1-v/(2*M_PI))*sin(u);
236                                         glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
237                                         glVertex3fv(coord);
238                                 }
239                         }
240                         break;
241                 case SWALLOWTAIL:
242                         for(u = -M_PI; u < M_PI; u+=kp->du){
243                                 for(v = -M_PI; v < M_PI; v+=kp->dv){
244                                         coord[0] = u*pow(v,2) + 3*pow(v,4);
245                                         coord[1] = -2*u*v - 4*pow(v,3);
246                                         coord[2] = u;
247                                         glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
248                                         glVertex3fv(coord);
249                                 }
250                         }
251                         break;
252                 case BOHEM:
253                         for(u = -M_PI; u < M_PI; u+=kp->du){
254                                 for(v = -M_PI; v < M_PI; v+=kp->dv){
255                                         coord[0] = kp->a*cos(u);
256                                         coord[1] = 1.5*cos(v) + kp->a*sin(u);
257                                         coord[2] = sin(v);
258                                         glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
259                                         glVertex3fv(coord);
260                                 }
261                         }
262                         break;
263                 default:
264                         for(u = -M_PI; u < M_PI; u+=kp->du){
265                                 for(v = -M_PI; v < M_PI; v+=kp->dv){
266                                         coord[0] = sin(u)*kp->a;        
267                                         coord[1] = cos(u)*kp->a;
268                                         coord[2] = sin(u/2)*cos(v) + cos(u/2)*sin(v);
269                                         glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
270                                         glVertex3fv(coord);
271                                 }
272                         }
273                         break;
274         }
275         glEnd();
276         glPopMatrix();
277
278
279         kp->a = sin(step+=0.01);
280         kp->b = cos(step+=0.01);
281 }
282
283
284 /* new window size or exposure */
285 void
286 reshape_klein(ModeInfo *mi, int width, int height)
287 {
288         GLfloat h = (GLfloat) height / (GLfloat) width;
289
290         glViewport(0, 0, (GLint) width, (GLint) height);
291         glMatrixMode(GL_PROJECTION);
292         glLoadIdentity();
293         gluPerspective (30.0, 1/h, 1.0, 100.0);
294
295         glMatrixMode(GL_MODELVIEW);
296         glLoadIdentity();
297         gluLookAt( 0.0, 0.0, 30.0,
298                          0.0, 0.0, 0.0,
299                          0.0, 1.0, 0.0);
300         
301         glClear(GL_COLOR_BUFFER_BIT);
302 }
303
304
305 Bool
306 klein_handle_event (ModeInfo *mi, XEvent *event)
307 {
308         kleinstruct *kp = &klein[MI_SCREEN(mi)];
309
310         if (event->xany.type == ButtonPress && event->xbutton.button & Button1) {
311                         kp->button_down_p = True;
312                         gltrackball_start (kp->trackball, event->xbutton.x, event->xbutton.y, MI_WIDTH (mi), MI_HEIGHT (mi));
313                         return True;
314         } else if (event->xany.type == ButtonRelease && event->xbutton.button & Button1) {
315                         kp->button_down_p = False;
316                         return True;
317         } else if (event->xany.type == MotionNotify && kp->button_down_p) {
318                         gltrackball_track (kp->trackball, event->xmotion.x, event->xmotion.y, MI_WIDTH (mi), MI_HEIGHT (mi));
319                         return True;
320         }
321
322         return False;
323 }
324
325
326 void
327 init_klein(ModeInfo *mi)
328 {
329         int      screen = MI_SCREEN(mi);
330         kleinstruct *kp;
331
332         if (klein == NULL) {
333                 if ((klein = (kleinstruct *) calloc(MI_NUM_SCREENS(mi), sizeof (kleinstruct))) == NULL)
334                         return;
335         }
336         kp = &klein[screen];
337
338         kp->window = MI_WINDOW(mi);
339
340         {
341                 double spin_speed        = 1.0;
342                 double wander_speed = 0.03;
343                 kp->rot = make_rotator (do_spin ? spin_speed : 0,
344                                                 do_spin ? spin_speed : 0,
345                                                 do_spin ? spin_speed : 0,
346                                                 1.0,
347                                                 do_wander ? wander_speed : 0,
348                                                 True);
349                 kp->trackball = gltrackball_init ();
350         }
351
352         if(rand) {
353                 render = random() % MY_PRIM_LAST;
354                 kp->surface = random() % SURFACE_LAST;
355         } else {
356                 render = MY_LINE_LOOP;
357                 kp->surface = KLEIN;
358         }
359
360         switch (render) {
361         case MY_POINTS: kp->render = GL_POINTS; break;
362         case MY_LINES: kp->render = GL_LINES; break;
363         case MY_LINE_LOOP: kp->render = GL_LINE_LOOP; break;
364         default:
365                         kp->render = GL_LINE_LOOP;
366         }
367 /*kp->render=GL_TRIANGLE_FAN;*/
368 /*kp->render=GL_POLYGON;*/
369
370         kp->du = 0.07;
371         kp->dv = 0.07;
372         kp->a = kp->b = 1;
373         kp->c = 0.1;
374
375
376         if ((kp->glx_context = init_GL(mi)) != NULL) {
377                 reshape_klein(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
378         } else {
379                 MI_CLEARWINDOW(mi);
380         }
381 }
382
383 void
384 draw_klein(ModeInfo * mi)
385 {
386         kleinstruct *kp = &klein[MI_SCREEN(mi)];
387         Display *display = MI_DISPLAY(mi);
388         Window  window = MI_WINDOW(mi);
389
390         if (!kp->glx_context) return;
391
392         glDrawBuffer(GL_BACK);
393
394         glXMakeCurrent(display, window, *(kp->glx_context));
395         draw(mi);
396         if (mi->fps_p) do_fps (mi);
397         glFinish();
398         glXSwapBuffers(display, window);
399 }
400
401 void
402 release_klein(ModeInfo * mi)
403 {
404         if (klein != NULL) {
405                 int      screen;
406
407                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
408                         kleinstruct *kp = &klein[screen];
409
410                         if (kp->glx_context) {
411                                 /* Display lists MUST be freed while their glXContext is current. */
412                                 glXMakeCurrent(MI_DISPLAY(mi), kp->window, *(kp->glx_context));
413                         }
414                 }
415                 (void) free((void *) klein);
416                 klein = NULL;
417         }
418         FreeAllGL(mi);
419 }
420
421
422 /*********************************************************/
423
424 #endif