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