http://www.jwz.org/xscreensaver/xscreensaver-5.09.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 (event->xany.type == ButtonPress && event->xbutton.button == Button1)
440   {
441     sp->button_down_p = True;
442     gltrackball_start(sp->trackball, event->xbutton.x, event->xbutton.y,
443                       MI_WIDTH (mi), MI_HEIGHT (mi));
444     return True;
445   }
446   else if (event->xany.type == ButtonRelease &&
447            event->xbutton.button == Button1)
448   {
449     sp->button_down_p = False;
450     return True;
451   }
452   else if (event->xany.type == ButtonPress &&
453            (event->xbutton.button == Button4 ||
454             event->xbutton.button == Button5 ||
455             event->xbutton.button == Button6 ||
456             event->xbutton.button == Button7)) {
457     gltrackball_mousewheel(sp->trackball, event->xbutton.button, 10,
458                            !!event->xbutton.state);
459     return True;
460   }
461   else if (event->xany.type == MotionNotify && sp->button_down_p)
462   {
463     gltrackball_track (sp->trackball, event->xmotion.x, event->xmotion.y,
464                        MI_WIDTH (mi), MI_HEIGHT (mi));
465     return True;
466   }
467
468   return False;
469 }
470
471
472 ENTRYPOINT void init_surface(ModeInfo *mi)
473 {
474   int    screen = MI_SCREEN(mi);
475   surfacestruct *sp;
476
477   if (surface == NULL)
478   {
479     if ((surface = (surfacestruct *) calloc(MI_NUM_SCREENS(mi),
480                                             sizeof(surfacestruct))) == NULL)
481       return;
482   }
483   sp = &surface[screen];
484
485   sp->window = MI_WINDOW(mi);
486
487   {
488     double spin_speed    = 1.0;
489     double wander_speed = 0.03;
490     sp->rot = make_rotator(do_spin ? spin_speed : 0,
491                            do_spin ? spin_speed : 0,
492                            do_spin ? spin_speed : 0,
493                            1.0,
494                            do_wander ? wander_speed : 0,
495                            True);
496     sp->trackball = gltrackball_init ();
497   }
498
499   if (!strcasecmp(surface_type,"random"))
500   {
501     sp->random_surface = True;
502     sp->surface = random() % NUM_SURFACES;
503   }
504   else if (!strcasecmp(surface_type,"dini"))
505   {
506     sp->random_surface = False;
507     sp->surface = SURFACE_DINI;
508   }
509   else if (!strcasecmp(surface_type,"enneper"))
510   {
511     sp->random_surface = False;
512     sp->surface = SURFACE_ENNEPER;
513   }
514   else if (!strcasecmp(surface_type,"kuen"))
515   {
516     sp->random_surface = False;
517     sp->surface = SURFACE_KUEN;
518   }
519   else if (!strcasecmp(surface_type,"moebius"))
520   {
521     sp->random_surface = False;
522     sp->surface = SURFACE_MOEBIUS;
523   }
524   else if (!strcasecmp(surface_type,"seashell"))
525   {
526     sp->random_surface = False;
527     sp->surface = SURFACE_SEASHELL;
528   }
529   else if (!strcasecmp(surface_type,"swallowtail"))
530   {
531     sp->random_surface = False;
532     sp->surface = SURFACE_SWALLOWTAIL;
533   }
534   else if (!strcasecmp(surface_type,"bohemian"))
535   {
536     sp->random_surface = False;
537     sp->surface = SURFACE_BOHEMIAN;
538   }
539   else if (!strcasecmp(surface_type,"whitney"))
540   {
541     sp->random_surface = False;
542     sp->surface = SURFACE_WHITNEY;
543   }
544   else if (!strcasecmp(surface_type,"pluecker"))
545   {
546     sp->random_surface = False;
547     sp->surface = SURFACE_PLUECKER;
548   }
549   else if (!strcasecmp(surface_type,"henneberg"))
550   {
551     sp->random_surface = False;
552     sp->surface = SURFACE_HENNEBERG;
553   }
554   else if (!strcasecmp(surface_type,"catalan"))
555   {
556     sp->random_surface = False;
557     sp->surface = SURFACE_CATALAN;
558   }
559   else if (!strcasecmp(surface_type,"corkscrew"))
560   {
561     sp->random_surface = False;
562     sp->surface = SURFACE_CORKSCREW;
563   }
564   else
565   {
566     sp->random_surface = True;
567     sp->surface = random() % NUM_SURFACES;
568   }
569
570   if (!strcasecmp(render_mode,"random"))
571   {
572     sp->random_render = True;
573     render = random() % NUM_RENDER;
574   }
575   else if (!strcasecmp(render_mode,"points"))
576   {
577     sp->random_render = False;
578     render = RENDER_POINTS;
579   }
580   else if (!strcasecmp(render_mode,"lines"))
581   {
582     sp->random_render = False;
583     render = RENDER_LINES;
584   }
585   else if (!strcasecmp(render_mode,"line-loops"))
586   {
587     sp->random_render = False;
588     render = RENDER_LINE_LOOP;
589   }
590   else
591   {
592     sp->random_render = True;
593     render = random() % NUM_RENDER;
594   }
595
596   switch (render)
597   {
598     case RENDER_POINTS:
599       sp->render = GL_POINTS;
600       break;
601     case RENDER_LINES:
602       sp->render = GL_LINES;
603       break;
604     case RENDER_LINE_LOOP:
605       if (sp->surface == SURFACE_BOHEMIAN ||
606           sp->surface == SURFACE_PLUECKER ||
607           sp->surface == SURFACE_HENNEBERG)
608         sp->render = GL_LINE_LOOP;
609       else
610         sp->render = GL_LINE_STRIP;
611       break;
612     default:
613       sp->render = GL_LINE_LOOP;
614       break;
615   }
616
617   sp->frame = 0;
618
619   sp->du = 0.07;
620   sp->dv = 0.07;
621   sp->a = sp->b = 1;
622   sp->c = 0.1;
623
624   if ((sp->glx_context = init_GL(mi)) != NULL)
625   {
626     reshape_surface(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
627   }
628   else
629   {
630     MI_CLEARWINDOW(mi);
631   }
632 }
633
634
635 ENTRYPOINT void draw_surface(ModeInfo * mi)
636 {
637   surfacestruct *sp = &surface[MI_SCREEN(mi)];
638   Display *display = MI_DISPLAY(mi);
639   Window window = MI_WINDOW(mi);
640
641   if (!sp->glx_context)
642     return;
643
644   glDrawBuffer(GL_BACK);
645
646   glXMakeCurrent(display, window, *(sp->glx_context));
647   draw(mi);
648   if (mi->fps_p)
649     do_fps(mi);
650   glFinish();
651   glXSwapBuffers(display, window);
652 }
653
654
655 ENTRYPOINT void release_surface(ModeInfo * mi)
656 {
657   if (surface != NULL)
658   {
659     int  screen;
660
661     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
662     {
663       surfacestruct *sp = &surface[screen];
664
665       if (sp->glx_context)
666       {
667         /* Display lists MUST be freed while their glXContext is current. */
668         glXMakeCurrent(MI_DISPLAY(mi), sp->window, *(sp->glx_context));
669       }
670     }
671     (void) free((void *)surface);
672     surface = NULL;
673   }
674   FreeAllGL(mi);
675 }
676
677
678 XSCREENSAVER_MODULE_2("Surfaces", surfaces, surface)
679
680 #endif