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