From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / glx / surfaces.c
1 /* Surface --- Parametric 3d surfaces visualization */
2
3 /*
4  * Revision History:
5  * 2000: written by Andrey Mirtchovski <mirtchov@cpsc.ucalgary.ca>
6  *       
7  * 01-Mar-2003  mirtchov    Modified as a xscreensaver hack.
8  * 01-jan-2009  steger      Renamed from klein.c to surfaces.c.
9  *                          Removed the Klein bottle.
10  *                          Added many new surfaces.
11  *                          Added many command line options.
12  *
13  */
14
15 /* surfaces to draw */
16 #define SURFACE_RANDOM            -1
17 #define SURFACE_DINI              0
18 #define SURFACE_ENNEPER           1
19 #define SURFACE_KUEN              2
20 #define SURFACE_MOEBIUS           3
21 #define SURFACE_SEASHELL          4
22 #define SURFACE_SWALLOWTAIL       5
23 #define SURFACE_BOHEMIAN          6
24 #define SURFACE_WHITNEY           7
25 #define SURFACE_PLUECKER          8
26 #define SURFACE_HENNEBERG         9
27 #define SURFACE_CATALAN           10
28 #define SURFACE_CORKSCREW         11
29 #define NUM_SURFACES              12
30
31 /* primitives to draw with 
32  * note that we skip the polygons and
33  * triangle fans -- too slow
34  *
35  * also removed triangle_strip and quads -- 
36  * just doesn't look good enough
37  */
38 #define RENDER_RANDOM             -1
39 #define RENDER_POINTS             0
40 #define RENDER_LINES              1
41 #define RENDER_LINE_LOOP          2
42 #define NUM_RENDER                3
43
44 #ifdef STANDALONE
45 # define DEFAULTS                   "*delay:        20000   \n" \
46                                     "*showFPS:      False   \n" \
47                                    "*suppressRotationAnimation: True\n" \
48
49 # define refresh_surface 0
50 # include "xlockmore.h"     /* from the xscreensaver distribution */
51 #else  /* !STANDALONE */
52 # include "xlock.h"         /* from the xlockmore distribution */
53 #endif /* !STANDALONE */
54
55 #ifdef USE_GL
56
57 #define DEF_SURFACE      "random"
58 #define DEF_MODE         "random"
59 #define DEF_SPIN         "True"
60 #define DEF_WANDER       "False"
61 #define DEF_SPEED        "300"
62
63 #include "rotator.h"
64 #include "gltrackball.h"
65
66 #undef countof
67 #define countof(x) (sizeof((x))/sizeof((*x)))
68
69
70 static char *surface_type;
71 static char *render_mode;
72 static int render;
73 static int speed;
74 static Bool do_spin;
75 static Bool do_wander;
76
77 static XrmOptionDescRec opts[] = {
78   { "-surface",        ".surface", XrmoptionSepArg, 0 },
79   { "-random-surface", ".surface", XrmoptionNoArg,  "random" },
80   { "-dini",           ".surface", XrmoptionNoArg,  "dini" },
81   { "-enneper",        ".surface", XrmoptionNoArg,  "enneper" },
82   { "-kuen",           ".surface", XrmoptionNoArg,  "kuen" },
83   { "-moebius",        ".surface", XrmoptionNoArg,  "moebius" },
84   { "-seashell",       ".surface", XrmoptionNoArg,  "seashell" },
85   { "-swallowtail",    ".surface", XrmoptionNoArg,  "swallowtail" },
86   { "-bohemian",       ".surface", XrmoptionNoArg,  "bohemian" },
87   { "-whitney",        ".surface", XrmoptionNoArg,  "whitney" },
88   { "-pluecker",       ".surface", XrmoptionNoArg,  "pluecker" },
89   { "-henneberg",      ".surface", XrmoptionNoArg,  "henneberg" },
90   { "-catalan",        ".surface", XrmoptionNoArg,  "catalan" },
91   { "-corkscrew",      ".surface", XrmoptionNoArg,  "corkscrew" },
92   { "-mode",           ".mode",    XrmoptionSepArg, 0 },
93   { "-random-mode",    ".mode",    XrmoptionNoArg,  "random" },
94   { "-points",         ".mode",    XrmoptionNoArg,  "points" },
95   { "-lines",          ".mode",    XrmoptionNoArg,  "lines" },
96   { "-line-loops",     ".mode",    XrmoptionNoArg,  "line-loops" },
97   { "-speed",          ".speed",   XrmoptionSepArg, 0 },
98   { "-spin",           ".spin",    XrmoptionNoArg, "True" },
99   { "+spin",           ".spin",    XrmoptionNoArg, "False" },
100   { "-wander",         ".wander",  XrmoptionNoArg, "True" },
101   { "+wander",         ".wander",  XrmoptionNoArg, "False" },
102 };
103
104 static argtype vars[] = {
105   {&surface_type, "surface", "Surface", DEF_SURFACE, t_String },
106   {&render_mode,  "mode",    "Mode",    DEF_MODE,    t_String },
107   {&do_spin,      "spin",    "Spin",    DEF_SPIN,    t_Bool },
108   {&do_wander,    "wander",  "Wander",  DEF_WANDER,  t_Bool },
109   {&speed,        "speed",   "Speed",   DEF_SPEED,   t_Int },
110 };
111
112
113 ENTRYPOINT ModeSpecOpt surface_opts =
114 {countof(opts), opts, countof(vars), vars, NULL};
115
116
117
118 typedef struct {
119   GLfloat x;
120   GLfloat y;
121   GLfloat z;
122 } GL_VECTOR;
123
124 typedef struct {
125   GLXContext *glx_context;
126   Window      window;
127   rotator    *rot;
128   trackball_state *trackball;
129   Bool        button_down_p;
130
131   int  render;
132   Bool random_render;
133   int  surface;
134   Bool random_surface;
135   int  frame;
136
137   float du, dv;
138   float a, b, c;
139
140   float draw_step;
141 } surfacestruct;
142
143 static surfacestruct *surface = NULL;
144
145
146 static void draw(ModeInfo *mi)
147 {
148   surfacestruct *sp = &surface[MI_SCREEN(mi)];
149   double u, v;
150   float coord[3];
151   int render;
152
153   mi->polygon_count = 0;
154   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
155
156   glEnable(GL_DEPTH_TEST);
157   glEnable(GL_NORMALIZE);
158   glEnable(GL_CULL_FACE);
159
160   glPushMatrix();
161
162   {
163     double x, y, z;
164     get_position(sp->rot, &x, &y, &z, !sp->button_down_p);
165     glTranslatef((x-0.5)*10, (y-0.5)*10, (z-0.5)*20);
166
167     gltrackball_rotate(sp->trackball);
168
169     get_rotation(sp->rot, &x, &y, &z, !sp->button_down_p);
170     glRotatef(x*360, 1.0, 0.0, 0.0);
171     glRotatef(y*360, 0.0, 1.0, 0.0);
172     glRotatef(z*360, 0.0, 0.0, 1.0);
173   }
174
175   glScalef(4.0, 4.0, 4.0);
176
177   switch(sp->surface)
178   {
179     case SURFACE_DINI:
180       for (v=0.11; v<=2.0; v+=sp->dv)
181       {
182         glBegin(sp->render);
183         for (u=0; u<=6.0*M_PI; u+=sp->du)
184         {
185           coord[0] = sp->a*cos(u)*sin(v);
186           coord[1] = sp->a*sin(u)*sin(v);
187           coord[2] = sp->a*(cos(v)+log(tan(0.5*v)))+0.2*sp->b*u;
188           glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
189           glVertex3fv(coord);
190           mi->polygon_count++;
191         }
192         glEnd();
193       }
194       break;
195     case SURFACE_ENNEPER:
196       for (u=-M_PI; u<=M_PI; u+=sp->du)
197       {
198         glBegin(sp->render);
199         for (v=-M_PI; v<M_PI; v+=sp->dv)
200         {
201           coord[0] = sp->a*(u-(1.0/3.0*u*u*u)+u*v*v);
202           coord[1] = sp->b*(v-(1.0/3.0*v*v*v)+u*u*v);
203           coord[2] = u*u-v*v;
204           glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
205           glVertex3fv(coord);
206           mi->polygon_count++;
207         }
208         glEnd();
209       }
210       break;
211     case SURFACE_KUEN:
212       for (u=-4.48; u<=4.48; u+=sp->du)
213       {
214         glBegin(sp->render);
215         for (v=M_PI/51; v<M_PI; v+=sp->dv)
216         {
217           coord[0] = 2*(cos(u)+u*sin(u))*sin(v)/(1+u*u*sin(v)*sin(v));
218           coord[1] = 2*(sin(u)-u*cos(u))*sin(v)/(1+u*u*sin(v)*sin(v));
219           coord[2] = log(tan(0.5*v))+2*cos(v)/(1+u*u*sin(v)*sin(v));
220           glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
221           glVertex3fv(coord);
222           mi->polygon_count++;
223         }
224         glEnd();
225       }
226       break;
227     case SURFACE_MOEBIUS:
228       for (u=-M_PI; u<M_PI; u+=sp->du)
229       {
230         glBegin(sp->render);
231         for (v=-0.735; v<0.74; v+=sp->dv)
232         {
233           coord[0] = cos(u)+v*cos(u/2)*cos(u);
234           coord[1] = sin(u)+v*cos(u/2)*sin(u);
235           coord[2] = v*sin(u/2);
236           glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
237           glVertex3fv(coord);
238           mi->polygon_count++;
239         }
240         glEnd();
241       }
242       break;
243     case SURFACE_SEASHELL:
244       for (u=0; u<2*M_PI; u+=sp->du)
245       {
246         glBegin(sp->render);
247         for (v=0; v<2*M_PI; v+=sp->dv)
248         {
249           coord[0] = sp->a*(1-v/(2*M_PI))*cos(2*v)*(1+cos(u))+sp->c*cos(2*v);
250           coord[1] = sp->a*(1-v/(2*M_PI))*sin(2*v)*(1+cos(u))+sp->c*sin(2*v);
251           coord[2] = 2*sp->b*v/(2*M_PI)+sp->a*(1-v/(2*M_PI))*sin(u);
252           glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
253           glVertex3fv(coord);
254           mi->polygon_count++;
255         }
256         glEnd();
257       }
258       break;
259     case SURFACE_SWALLOWTAIL:
260       for (u=-2.5; u<2.0; u+=sp->du)
261       {
262         glBegin(sp->render);
263         for (v=-1.085; v<1.09; v+=sp->dv)
264         {
265           coord[0] = u*v*v+3*v*v*v*v;
266           coord[1] = -2*u*v-4*v*v*v;
267           coord[2] = u;
268           glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
269           glVertex3fv(coord);
270           mi->polygon_count++;
271         }
272         glEnd();
273       }
274       break;
275     case SURFACE_BOHEMIAN:
276       for (u=-M_PI; u<M_PI; u+=sp->du)
277       {
278         glBegin(sp->render);
279         for (v=-M_PI; v<M_PI; v+=sp->dv)
280         {
281           coord[0] = sp->a*cos(u);
282           coord[1] = sp->b*cos(v)+sp->a*sin(u);
283           coord[2] = sin(v);
284           glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
285           glVertex3fv(coord);
286           mi->polygon_count++;
287         }
288         glEnd();
289       }
290       break;
291     case SURFACE_WHITNEY:
292       for (v=-1.995; v<2.0; v+=sp->dv)
293       {
294         glBegin(sp->render);
295         for (u=-1.995; u<2.0; u+=sp->du)
296         {
297           coord[0] = u*v;
298           coord[1] = u;
299           coord[2] = v*v-2;
300           glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
301           glVertex3fv(coord);
302           mi->polygon_count++;
303         }
304         glEnd();
305       }
306       break;
307     case SURFACE_PLUECKER:
308       for (u=0; u<2.5; u+=sp->dv)
309       {
310         glBegin(sp->render);
311         for (v=-M_PI; v<M_PI; v+=sp->du)
312         {
313           coord[0] = u*cos(v);
314           coord[1] = u*sin(v);
315           coord[2] = 2*cos(v)*sin(v);
316           glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
317           glVertex3fv(coord);
318           mi->polygon_count++;
319         }
320         glEnd();
321       }
322       break;
323     case SURFACE_HENNEBERG:
324       for (u=0.9; u<2.55; u+=sp->dv)
325       {
326         glBegin(sp->render);
327         for (v=-M_PI; v<M_PI; v+=sp->du)
328         {
329           coord[0] = sinh(1.0/3.0*u)*cos(v)-1.0/3.0*sinh(u)*cos(3.0*v);
330           coord[1] = sinh(1.0/3.0*u)*sin(v)+1.0/3.0*sinh(u)*sin(3.0*v);
331           coord[2] = cosh(2.0/3.0*u)*cos(2.0*v);
332           glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
333           glVertex3fv(coord);
334           mi->polygon_count++;
335         }
336         glEnd();
337       }
338       break;
339     case SURFACE_CATALAN:
340       for (v=-2; v<2; v+=sp->du)
341       {
342         glBegin(sp->render);
343         for (u=-2*M_PI; u<2*M_PI+0.05; u+=sp->dv)
344         {
345           coord[0] = 0.33*(u-sin(u)*cosh(v));
346           coord[1] = 0.33*(1.0-cos(u)*cosh(v));
347           coord[2] = 0.33*4.0*sin(0.5*u)*sinh(0.5*v);
348           glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
349           glVertex3fv(coord);
350           mi->polygon_count++;
351         }
352         glEnd();
353       }
354       break;
355     case SURFACE_CORKSCREW:
356       for (v=-M_PI; v<M_PI; v+=sp->du)
357       {
358         glBegin(sp->render);
359         for (u=-M_PI; u<M_PI; u+=sp->dv)
360         {
361           coord[0] = 0.5*(sp->a+2.0)*cos(u)*cos(v);
362           coord[1] = 0.5*(sp->a+2.0)*sin(u)*cos(v);
363           coord[2] = 0.5*(sp->a+2.0)*sin(v)+u;
364           glColor3f(coord[0]+0.7, coord[1]+0.7, coord[2]+0.7);
365           glVertex3fv(coord);
366           mi->polygon_count++;
367         }
368         glEnd();
369       }
370       break;
371   }
372   glPopMatrix();
373
374   if (sp->render == GL_LINES)
375     mi->polygon_count /= 2;
376
377   sp->a = sin(sp->draw_step+=0.01);
378   sp->b = cos(sp->draw_step+=0.01);
379   sp->c = sin(sp->draw_step+0.25*M_PI);
380
381   if (sp->random_surface || sp->random_render)
382   {
383     sp->frame++;
384     if (sp->frame >= speed)
385     {
386       sp->frame = 0;
387       if (sp->random_surface)
388         sp->surface = random() % NUM_SURFACES;
389       if (sp->random_render)
390       {
391         render = random() % NUM_RENDER;
392         switch (render)
393         {
394           case RENDER_POINTS:
395             sp->render = GL_POINTS;
396             break;
397           case RENDER_LINES:
398             sp->render = GL_LINES;
399             break;
400           case RENDER_LINE_LOOP:
401             if (sp->surface == SURFACE_BOHEMIAN ||
402                 sp->surface == SURFACE_PLUECKER ||
403                 sp->surface == SURFACE_HENNEBERG)
404               sp->render = GL_LINE_LOOP;
405             else
406               sp->render = GL_LINE_STRIP;
407             break;
408           default:
409             sp->render = GL_LINE_LOOP;
410             break;
411         }
412       }
413     }
414   }
415 }
416
417
418 /* new window size or exposure */
419 ENTRYPOINT void reshape_surface(ModeInfo *mi, int width, int height)
420 {
421   GLfloat h = (GLfloat) height / (GLfloat) width;
422
423   glViewport(0, 0, (GLint) width, (GLint) height);
424   glMatrixMode(GL_PROJECTION);
425   glLoadIdentity();
426   gluPerspective (30.0, 1/h, 1.0, 100.0);
427
428   glMatrixMode(GL_MODELVIEW);
429   glLoadIdentity();
430   gluLookAt(0.0, 0.0, 30.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
431     
432 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
433   {
434     int o = (int) current_device_rotation();
435     if (o != 0 && o != 180 && o != -180)
436       glScalef (1/h, 1/h, 1/h);
437   }
438 # endif
439
440   glClear(GL_COLOR_BUFFER_BIT);
441 }
442
443
444 ENTRYPOINT Bool surface_handle_event(ModeInfo *mi, XEvent *event)
445 {
446   surfacestruct *sp = &surface[MI_SCREEN(mi)];
447
448   if (gltrackball_event_handler (event, sp->trackball,
449                                  MI_WIDTH (mi), MI_HEIGHT (mi),
450                                  &sp->button_down_p))
451     return True;
452
453   return False;
454 }
455
456
457 ENTRYPOINT void init_surface(ModeInfo *mi)
458 {
459   int    screen = MI_SCREEN(mi);
460   surfacestruct *sp;
461
462   if (surface == NULL)
463   {
464     if ((surface = (surfacestruct *) calloc(MI_NUM_SCREENS(mi),
465                                             sizeof(surfacestruct))) == NULL)
466       return;
467   }
468   sp = &surface[screen];
469
470   sp->window = MI_WINDOW(mi);
471
472   {
473     double spin_speed    = 1.0;
474     double wander_speed = 0.03;
475     sp->rot = make_rotator(do_spin ? spin_speed : 0,
476                            do_spin ? spin_speed : 0,
477                            do_spin ? spin_speed : 0,
478                            1.0,
479                            do_wander ? wander_speed : 0,
480                            True);
481     sp->trackball = gltrackball_init (True);
482   }
483
484   if (!strcasecmp(surface_type,"random"))
485   {
486     sp->random_surface = True;
487     sp->surface = random() % NUM_SURFACES;
488   }
489   else if (!strcasecmp(surface_type,"dini"))
490   {
491     sp->random_surface = False;
492     sp->surface = SURFACE_DINI;
493   }
494   else if (!strcasecmp(surface_type,"enneper"))
495   {
496     sp->random_surface = False;
497     sp->surface = SURFACE_ENNEPER;
498   }
499   else if (!strcasecmp(surface_type,"kuen"))
500   {
501     sp->random_surface = False;
502     sp->surface = SURFACE_KUEN;
503   }
504   else if (!strcasecmp(surface_type,"moebius"))
505   {
506     sp->random_surface = False;
507     sp->surface = SURFACE_MOEBIUS;
508   }
509   else if (!strcasecmp(surface_type,"seashell"))
510   {
511     sp->random_surface = False;
512     sp->surface = SURFACE_SEASHELL;
513   }
514   else if (!strcasecmp(surface_type,"swallowtail"))
515   {
516     sp->random_surface = False;
517     sp->surface = SURFACE_SWALLOWTAIL;
518   }
519   else if (!strcasecmp(surface_type,"bohemian"))
520   {
521     sp->random_surface = False;
522     sp->surface = SURFACE_BOHEMIAN;
523   }
524   else if (!strcasecmp(surface_type,"whitney"))
525   {
526     sp->random_surface = False;
527     sp->surface = SURFACE_WHITNEY;
528   }
529   else if (!strcasecmp(surface_type,"pluecker"))
530   {
531     sp->random_surface = False;
532     sp->surface = SURFACE_PLUECKER;
533   }
534   else if (!strcasecmp(surface_type,"henneberg"))
535   {
536     sp->random_surface = False;
537     sp->surface = SURFACE_HENNEBERG;
538   }
539   else if (!strcasecmp(surface_type,"catalan"))
540   {
541     sp->random_surface = False;
542     sp->surface = SURFACE_CATALAN;
543   }
544   else if (!strcasecmp(surface_type,"corkscrew"))
545   {
546     sp->random_surface = False;
547     sp->surface = SURFACE_CORKSCREW;
548   }
549   else
550   {
551     sp->random_surface = True;
552     sp->surface = random() % NUM_SURFACES;
553   }
554
555   if (!strcasecmp(render_mode,"random"))
556   {
557     sp->random_render = True;
558     render = random() % NUM_RENDER;
559   }
560   else if (!strcasecmp(render_mode,"points"))
561   {
562     sp->random_render = False;
563     render = RENDER_POINTS;
564   }
565   else if (!strcasecmp(render_mode,"lines"))
566   {
567     sp->random_render = False;
568     render = RENDER_LINES;
569   }
570   else if (!strcasecmp(render_mode,"line-loops"))
571   {
572     sp->random_render = False;
573     render = RENDER_LINE_LOOP;
574   }
575   else
576   {
577     sp->random_render = True;
578     render = random() % NUM_RENDER;
579   }
580
581   switch (render)
582   {
583     case RENDER_POINTS:
584       sp->render = GL_POINTS;
585       break;
586     case RENDER_LINES:
587       sp->render = GL_LINES;
588       break;
589     case RENDER_LINE_LOOP:
590       if (sp->surface == SURFACE_BOHEMIAN ||
591           sp->surface == SURFACE_PLUECKER ||
592           sp->surface == SURFACE_HENNEBERG)
593         sp->render = GL_LINE_LOOP;
594       else
595         sp->render = GL_LINE_STRIP;
596       break;
597     default:
598       sp->render = GL_LINE_LOOP;
599       break;
600   }
601
602   sp->frame = 0;
603
604   sp->du = 0.07;
605   sp->dv = 0.07;
606   sp->a = sp->b = 1;
607   sp->c = 0.1;
608
609   if ((sp->glx_context = init_GL(mi)) != NULL)
610   {
611     reshape_surface(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
612   }
613   else
614   {
615     MI_CLEARWINDOW(mi);
616   }
617 }
618
619
620 ENTRYPOINT void draw_surface(ModeInfo * mi)
621 {
622   surfacestruct *sp = &surface[MI_SCREEN(mi)];
623   Display *display = MI_DISPLAY(mi);
624   Window window = MI_WINDOW(mi);
625
626   if (!sp->glx_context)
627     return;
628
629   glDrawBuffer(GL_BACK);
630
631   glXMakeCurrent(display, window, *(sp->glx_context));
632   draw(mi);
633   if (mi->fps_p)
634     do_fps(mi);
635   glFinish();
636   glXSwapBuffers(display, window);
637 }
638
639
640 ENTRYPOINT void release_surface(ModeInfo * mi)
641 {
642   if (surface != NULL)
643   {
644     int  screen;
645
646     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
647     {
648       surfacestruct *sp = &surface[screen];
649
650       if (sp->glx_context)
651       {
652         /* Display lists MUST be freed while their glXContext is current. */
653         glXMakeCurrent(MI_DISPLAY(mi), sp->window, *(sp->glx_context));
654       }
655     }
656     (void) free((void *)surface);
657     surface = NULL;
658   }
659   FreeAllGL(mi);
660 }
661
662
663 XSCREENSAVER_MODULE_2("Surfaces", surfaces, surface)
664
665 #endif