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