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