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