From http://www.jwz.org/xscreensaver/xscreensaver-5.16.tar.gz
[xscreensaver] / hacks / glx / flipscreen3d.c
1 /*
2  * flipscreen3d - takes snapshots of the screen and flips it around
3  *
4  * version 1.0 - Oct 24, 2001
5  *
6  * Copyright (C) 2001 Ben Buxton (bb@cactii.net)
7  *
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notice appear in all copies and that both that
11  * copyright notice and this permission notice appear in supporting
12  * documentation.  No representations are made about the suitability of this
13  * software for any purpose.  It is provided "as is" without express or
14  * implied warranty.
15  */
16
17 #ifdef STANDALONE
18 #define DEFAULTS "*delay:     20000 \n" \
19                  "*showFPS:   False \n" \
20                  "*wireframe: False \n" \
21                  "*useSHM:    True  \n"
22
23 # define refresh_screenflip 0
24 # include "xlockmore.h"                         /* from the xscreensaver distribution */
25 # include "gltrackball.h"
26 #else  /* !STANDALONE */
27 # include "xlock.h"                                     /* from the xlockmore distribution */
28 #endif /* !STANDALONE */
29
30 /* lifted from lament.c */
31 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
32 #define RANDSIGN() ((random() & 1) ? 1 : -1)
33
34
35 #ifdef USE_GL
36
37 /* Should be in <GL/glext.h> */
38 # ifndef  GL_TEXTURE_MAX_ANISOTROPY_EXT
39 #  define GL_TEXTURE_MAX_ANISOTROPY_EXT     0x84FE
40 # endif
41 # ifndef  GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
42 #  define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
43 # endif
44
45 #define DEF_ROTATE "True"
46 static int rotate;
47
48 #define QW 12
49 #define QH 12
50
51 #undef countof
52 #define countof(x) (sizeof((x))/sizeof((*x)))
53
54
55 static XrmOptionDescRec opts[] = {
56   {"+rotate", ".screenflip.rotate", XrmoptionNoArg, "false" },
57   {"-rotate", ".screenflip.rotate", XrmoptionNoArg, "true" },
58 };
59
60
61 static argtype vars[] = {
62   {&rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
63 };
64
65
66
67 ENTRYPOINT ModeSpecOpt screenflip_opts = {countof(opts), opts, countof(vars), vars, NULL};
68
69
70 #ifdef USE_MODULES
71 ModStruct   screenflip_description =
72 {"screenflip", "init_screenflip", "draw_screenflip", "release_screenflip",
73  "draw_screenflip", "init_screenflip", NULL, &screenflip_opts,
74  1000, 1, 2, 1, 4, 1.0, "",
75  "Screenflips", 0, NULL};
76
77 #endif
78
79
80 typedef struct {
81   GLXContext *glx_context;
82   Window window;
83
84   int winw, winh;
85   int tw, th; /* texture width, height */
86   GLfloat min_tx, min_ty;
87   GLfloat max_tx, max_ty;
88   GLfloat qx, qy, qw, qh; /* the quad we'll draw */
89
90   int regrab;
91   int fadetime; /* fade before regrab */
92
93   trackball_state *trackball;
94   Bool button_down_p;
95
96   GLfloat show_colors[4];
97   GLfloat stretch_val_x, stretch_val_y;
98   GLfloat stretch_val_dx, stretch_val_dy;
99
100   GLfloat curx, cury, curz;
101
102   GLfloat rx, ry, rz;
103   GLfloat rot, drot, odrot, ddrot, orot;
104   float theta, rho, dtheta, drho, gamma, dgamma;
105
106   GLuint texid;
107   Bool mipmap_p;
108   Bool waiting_for_image_p;
109   Bool first_image_p;
110
111   GLfloat anisotropic;
112
113 } Screenflip;
114
115 static Screenflip *screenflip = NULL;
116
117 #include "grab-ximage.h"
118
119 static const GLfloat viewer[] = {0.0, 0.0, 15.0};
120
121
122 ENTRYPOINT Bool
123 screenflip_handle_event (ModeInfo *mi, XEvent *event)
124 {
125   Screenflip *c = &screenflip[MI_SCREEN(mi)];
126
127   if (event->xany.type == ButtonPress &&
128       event->xbutton.button == Button1)
129     {
130       c->button_down_p = True;
131       gltrackball_start (c->trackball,
132                          event->xbutton.x, event->xbutton.y,
133                          MI_WIDTH (mi), MI_HEIGHT (mi));
134       return True;
135     }
136   else if (event->xany.type == ButtonRelease &&
137            event->xbutton.button == Button1)
138     {
139       c->button_down_p = False;
140       return True;
141     }
142   else if (event->xany.type == ButtonPress &&
143            (event->xbutton.button == Button4 ||
144             event->xbutton.button == Button5 ||
145             event->xbutton.button == Button6 ||
146             event->xbutton.button == Button7))
147     {
148       gltrackball_mousewheel (c->trackball, event->xbutton.button, 10,
149                               !!event->xbutton.state);
150       return True;
151     }
152   else if (event->xany.type == MotionNotify &&
153            c->button_down_p)
154     {
155       gltrackball_track (c->trackball,
156                          event->xmotion.x, event->xmotion.y,
157                          MI_WIDTH (mi), MI_HEIGHT (mi));
158       return True;
159     }
160
161   return False;
162 }
163
164
165 /* draw the texture mapped quad (actually two back to back)*/
166 static void showscreen(Screenflip *c, int frozen, int wire)
167 {
168   GLfloat x, y, w, h;
169
170   if (c->fadetime) {
171 /*    r -= 0.02; g -= 0.02; b -= 0.02; */
172     c->show_colors[3] -= 0.02;
173     if (c->show_colors[3] < 0) {
174       c->regrab = 1;
175       c->fadetime = 0;
176     }
177   } else if (c->show_colors[3] < 0) {
178     c->show_colors[0] = c->show_colors[1] = 
179       c->show_colors[2] = c->show_colors[3] = 1;
180     c->stretch_val_x = c->stretch_val_y = 
181       c->stretch_val_dx = c->stretch_val_dy = 0;
182   }
183   if (c->stretch_val_dx == 0 && !frozen && !(random() % 25))
184     c->stretch_val_dx = (float)(random() % 100) / 5000;
185   if (c->stretch_val_dy == 0 && !frozen && !(random() % 25))
186     c->stretch_val_dy = (float)(random() % 100) / 5000;
187     
188   x = c->qx;
189   y = c->qy;
190   w = c->qx+c->qw;
191   h = c->qy-c->qh;
192
193   if (!frozen) {
194      w *= sin (c->stretch_val_x) + 1;
195      x *= sin (c->stretch_val_x) + 1;
196      if (!c->button_down_p) {
197      if (!c->fadetime) c->stretch_val_x += c->stretch_val_dx;
198      if (c->stretch_val_x > 2*M_PI && !(random() % 5))
199        c->stretch_val_dx = (float)(random() % 100) / 5000;
200      else
201        c->stretch_val_x -= 2*M_PI;
202      }
203
204      if (!c->button_down_p && !c->fadetime) c->stretch_val_y += c->stretch_val_dy;
205      h *= sin (c->stretch_val_y) / 2 + 1;
206      y *= sin (c->stretch_val_y) / 2 + 1;
207      if (!c->button_down_p) {
208      if (c->stretch_val_y > 2*M_PI && !(random() % 5))
209        c->stretch_val_dy = (float)(random() % 100) / 5000;
210      else
211        c->stretch_val_y -= 2*M_PI;
212      }
213   }
214
215   glColor4f(c->show_colors[0], c->show_colors[1], 
216             c->show_colors[2], c->show_colors[3]);
217
218   if (!wire)
219     {
220       glEnable(GL_TEXTURE_2D);
221       glEnable(GL_BLEND);
222       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
223       glDepthMask(GL_FALSE);
224     }
225
226   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
227
228   glNormal3f(0, 0, 1);
229   glTexCoord2f(c->max_tx, c->max_ty); glVertex3f(w, h, 0);
230   glTexCoord2f(c->max_tx, c->min_ty); glVertex3f(w, y, 0);
231   glTexCoord2f(c->min_tx, c->min_ty); glVertex3f(x, y, 0);
232   glTexCoord2f(c->min_tx, c->max_ty); glVertex3f(x, h, 0);
233
234   glNormal3f(0, 0, -1);
235   glTexCoord2f(c->min_tx, c->min_ty); glVertex3f(x, y, -0.05);
236   glTexCoord2f(c->max_tx, c->min_ty); glVertex3f(w, y, -0.05);
237   glTexCoord2f(c->max_tx, c->max_ty); glVertex3f(w, h, -0.05);
238   glTexCoord2f(c->min_tx, c->max_ty); glVertex3f(x, h, -0.05);
239   glEnd();
240
241
242   glDisable(GL_TEXTURE_2D);
243   glDepthMask(GL_TRUE);
244
245   glBegin(GL_LINE_LOOP);
246    glVertex3f(x, y, 0);
247    glVertex3f(x, h, 0);
248    glVertex3f(w, h, 0);
249    glVertex3f(w, y, 0);
250  glEnd();
251   glDisable(GL_BLEND);
252
253 }
254
255 /* This function is responsible for 'zooming back' the square after
256  * a new chunk has been grabbed with getSnapshot(), and positioning
257  * it suitably on the screen. Once positioned (where we begin to rotate),
258  * it just does a glTranslatef() and returns 1
259  */
260
261 static int inposition(Screenflip *c)
262 {
263   GLfloat wx;
264   GLfloat wy;
265   wx = 0 - (c->qw/2);
266   wy = (c->qh/2);
267
268   if (c->curx == 0) c->curx = c->qx;
269   if (c->cury == 0) c->cury = c->qy;
270   if (c->regrab) {
271      c->curz = 0;
272      c->curx = c->qx;
273      c->cury = c->qy;
274      c->regrab = 0;
275   }
276   if (c->curz > -10 || c->curx > wx + 0.1 || c->curx < wx - 0.1 ||
277          c->cury > wy + 0.1 || c->cury < wy - 0.1) {
278     if (c->curz > -10)
279       c->curz -= 0.05;
280     if (c->curx > wx) {
281        c->qx -= 0.02;
282        c->curx -= 0.02;
283     }
284     if (c->curx < wx) {
285        c->qx += 0.02;
286        c->curx += 0.02;
287     }
288     if (c->cury > wy) {
289        c->qy -= 0.02;
290        c->cury -= 0.02;
291     }
292     if (c->cury < wy) {
293        c->qy += 0.02;
294        c->cury += 0.02;
295     }
296     glTranslatef(0, 0, c->curz);
297     return 0;
298   }
299   glTranslatef(0, 0, c->curz);
300   return 1;
301
302 }
303
304 #if 0
305 static void drawgrid(void)
306 {
307   int i;
308
309   glColor3f(0, 0.7, 0);
310   glBegin(GL_LINES);
311   for (i = 0 ; i <= 50; i+=2) {
312       glVertex3f( -25, -15, i-70);
313       glVertex3f( 25, -15, i-70);
314       glVertex3f( i-25, -15, -70);
315       glVertex3f( i-25, -15, -20);
316   }
317   glEnd();
318 }
319 #endif
320
321
322 static void display(Screenflip *c, int wire)
323 {
324   int frozen;
325   GLfloat rot = current_device_rotation();
326
327   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
328   glLoadIdentity();
329   gluLookAt(viewer[0], viewer[1], viewer[2], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
330   glPushMatrix();
331
332   glRotatef(rot, 0, 0, 1);
333   if ((rot >  45 && rot <  135) ||
334       (rot < -45 && rot > -135))
335     {
336       GLfloat s = c->winw / (GLfloat) c->winh;
337       glScalef (s, 1/s, 1);
338     }
339
340   if (inposition(c)) {
341     frozen = 0;
342     glTranslatef(5 * sin(c->theta), 5 * sin(c->rho), 10 * cos(c->gamma) - 10);
343 /* randomly change the speed */
344     if (!c->button_down_p && !(random() % 300)) {
345       if (random() % 2)
346         c->drho = 1/60 - (float)(random() % 100)/3000;
347       if (random() % 2)
348         c->dtheta = 1/60 - (float)(random() % 100)/3000;
349       if (random() % 2)
350         c->dgamma = 1/60 - (float)(random() % 100)/3000;
351     }
352     glRotatef(-rot, 0, 0, 1);
353     gltrackball_rotate (c->trackball);
354     glRotatef(rot, 0, 0, 1);
355     if (rotate) glRotatef(c->rot, c->rx, c->ry, c->rz);
356 /* update variables with each frame */
357     if(!c->button_down_p && !c->fadetime) {
358       c->theta += c->dtheta;
359       c->rho += c->drho;
360       c->gamma += c->dgamma;
361       c->rot += c->drot;
362       c->drot += c->ddrot;
363     }
364 /* dont let our rotation speed get too high */
365     if (c->drot > 5 && c->ddrot > 0)
366         c->ddrot = 0 - (GLfloat)(random() % 100) / 1000;
367     else if (c->drot < -5 && c->ddrot < 0)
368         c->ddrot = (GLfloat)(random() % 100) / 1000;
369   } else { /* reset some paramaters */
370     c->ddrot = 0.05 - (GLfloat)(random() % 100) / 1000;
371     c->theta = c->rho = c->gamma = 0;
372     c->rot = 0;
373     frozen = 1;
374   }
375   if (!c->button_down_p && !c->fadetime && (c->rot >= 360 || c->rot <= -360) && !(random() % 7)) { /* rotate  change */
376     c->rx = (GLfloat)(random() % 100) / 100;
377     c->ry = (GLfloat)(random() % 100) / 100;
378     c->rz = (GLfloat)(random() % 100) / 100;
379   }
380   if (c->odrot * c->drot < 0 && c->tw < c->winw && !(random() % 10)) {
381     c->fadetime = 1;                /* randomly fade and get new snapshot */
382   }
383   c->orot = c->rot;
384   c->odrot = c->drot;
385   if (c->rot > 360 || c->rot < -360) /* dont overflow rotation! */
386     c->rot -= c->rot;
387   showscreen(c, frozen, wire);
388   glPopMatrix();
389   glFlush();
390 }
391
392 ENTRYPOINT void reshape_screenflip(ModeInfo *mi, int width, int height)
393 {
394  Screenflip *c = &screenflip[MI_SCREEN(mi)];
395  glViewport(0,0,(GLint)width, (GLint) height);
396  glMatrixMode(GL_PROJECTION);
397  glLoadIdentity();
398  gluPerspective(45, 1, 2.0, 85);
399  glMatrixMode(GL_MODELVIEW);
400  c->winw = width;
401  c->winh = height;
402 }
403
404 static void
405 image_loaded_cb (const char *filename, XRectangle *geometry,
406                  int image_width, int image_height, 
407                  int texture_width, int texture_height,
408                  void *closure)
409 {
410   Screenflip *c = (Screenflip *) closure;
411
412   c->tw = texture_width;
413   c->th = texture_height;
414   c->min_tx = (GLfloat) geometry->x / c->tw;
415   c->min_ty = (GLfloat) geometry->y / c->th;
416   c->max_tx = (GLfloat) (geometry->x + geometry->width)  / c->tw;
417   c->max_ty = (GLfloat) (geometry->y + geometry->height) / c->th;
418
419   c->qx = -QW/2 + ((GLfloat) geometry->x * QW / image_width);
420   c->qy =  QH/2 - ((GLfloat) geometry->y * QH / image_height);
421   c->qw =  QW   * ((GLfloat) geometry->width  / image_width);
422   c->qh =  QH   * ((GLfloat) geometry->height / image_height);
423
424   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
425   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
426                    (c->mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
427
428   if (c->anisotropic >= 1.0)
429     glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 
430                      c->anisotropic);
431
432   c->waiting_for_image_p = False;
433   c->first_image_p = False;
434 }
435
436
437 static void getSnapshot (ModeInfo *modeinfo)
438 {
439   Screenflip *c = &screenflip[MI_SCREEN(modeinfo)];
440
441   if (MI_IS_WIREFRAME(modeinfo))
442     return;
443
444   c->waiting_for_image_p = True;
445   c->mipmap_p = True;
446   load_texture_async (modeinfo->xgwa.screen, modeinfo->window,
447                       *c->glx_context, 0, 0, c->mipmap_p, c->texid,
448                       image_loaded_cb, c);
449 }
450
451 ENTRYPOINT void init_screenflip(ModeInfo *mi)
452 {
453   int screen = MI_SCREEN(mi);
454   Screenflip *c;
455
456  if (screenflip == NULL) {
457    if ((screenflip = (Screenflip *) calloc(MI_NUM_SCREENS(mi),
458                                         sizeof(Screenflip))) == NULL)
459           return;
460  }
461  c = &screenflip[screen];
462  c->window = MI_WINDOW(mi);
463
464  c->trackball = gltrackball_init ();
465
466  if ((c->glx_context = init_GL(mi)) != NULL) {
467       reshape_screenflip(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
468  } else {
469      MI_CLEARWINDOW(mi);
470  }
471  c->winh = MI_WIN_HEIGHT(mi);
472  c->winw = MI_WIN_WIDTH(mi);
473  c->qw = QW;
474  c->qh = QH;
475  c->qx = -6;
476  c->qy = 6;
477
478  c->rx = c->ry = 1;
479  c->odrot = 1;
480
481  c->show_colors[0] = c->show_colors[1] = 
482    c->show_colors[2] = c->show_colors[3] = 1;
483
484  if (! MI_IS_WIREFRAME(mi))
485    {
486      glShadeModel(GL_SMOOTH);
487      glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
488      glEnable(GL_DEPTH_TEST);
489      glEnable(GL_CULL_FACE);
490      glCullFace(GL_BACK);
491      glDisable(GL_LIGHTING);
492    }
493
494  if (strstr ((char *) glGetString(GL_EXTENSIONS),
495              "GL_EXT_texture_filter_anisotropic"))
496    glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &c->anisotropic);
497  else
498    c->anisotropic = 0.0;
499
500  glGenTextures(1, &c->texid);
501
502  c->first_image_p = True;
503  getSnapshot(mi);
504 }
505
506 ENTRYPOINT void draw_screenflip(ModeInfo *mi)
507 {
508   Screenflip *c = &screenflip[MI_SCREEN(mi)];
509   Window w = MI_WINDOW(mi);
510   Display *disp = MI_DISPLAY(mi);
511
512   if (!c->glx_context)
513       return;
514
515   /* Wait for the first image; for subsequent images, load them in the
516      background while animating. */
517   if (c->waiting_for_image_p && c->first_image_p)
518     return;
519
520   glXMakeCurrent(disp, w, *(c->glx_context));
521
522   glBindTexture(GL_TEXTURE_2D, c->texid);
523
524   if (c->regrab)
525     getSnapshot(mi);
526
527   display(c, MI_IS_WIREFRAME(mi));
528
529   if(mi->fps_p) do_fps(mi);
530   glFinish(); 
531   glXSwapBuffers(disp, w);
532 }
533
534 ENTRYPOINT void release_screenflip(ModeInfo *mi)
535 {
536   if (screenflip != NULL) {
537    (void) free((void *) screenflip);
538    screenflip = NULL;
539   }
540   FreeAllGL(mi);
541 }
542
543 XSCREENSAVER_MODULE_2 ("FlipScreen3D", flipscreen3d, screenflip)
544
545 #endif