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