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