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