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