7060bcf5d7b1bfe9482de736128c30e08f5a7dfc
[xscreensaver] / hacks / glx / cubicgrid.c
1 /*-
2  * Permission to use, copy, modify, and distribute this software and its
3  * documentation for any purpose and without fee is hereby granted,
4  * provided that the above copyright notice appear in all copies and that
5  * both that copyright notice and this permission notice appear in
6  * supporting documentation.
7  *
8  * This file is provided AS IS with no warranties of any kind.  The author
9  * shall have no liability with respect to the infringement of copyrights,
10  * trade secrets or any patents by this file or any part thereof.  In no
11  * event will the author be liable for any lost revenue or profits or
12  * other special, indirect and consequential damages.
13  *
14  * Cubic Grid - a 3D lattice. The observer is located in the centre of 
15  * a spinning finite lattice. As it rotates, various view-throughs appear and 
16  * evolve. A simple idea with interesting results.
17  * 
18  * Vasek Potocek (Dec-28-2007)
19  * vasek.potocek@post.cz
20  */
21
22 #define DEFAULTS   "*delay:         20000         \n" \
23                    "*showFPS:       False         \n" \
24                    "*wireframe:     False         \n"
25
26 # define refresh_cubicgrid 0
27 #include "xlockmore.h"
28
29 #ifdef USE_GL
30
31 #define DEF_SPEED   "1.0"
32 #define DEF_DIV     "30"
33 #define DEF_ZOOM    "20"
34 #define DEF_BIGDOTS "True"
35
36 #undef countof
37 #define countof(x) (sizeof((x))/sizeof((*x)))
38
39 #include "rotator.h"
40 #include "gltrackball.h"
41
42 /*************************************************************************/
43
44 static int ticks;
45 static float size;
46 static float speed;
47 static Bool bigdots;
48
49 static argtype vars[] = {
50   { &speed,   "speed",   "Speed",   DEF_SPEED,   t_Float },
51   { &size,    "zoom",    "Zoom",    DEF_ZOOM,    t_Float },
52   { &ticks,   "ticks",   "Ticks",   DEF_DIV,     t_Int },
53   { &bigdots, "bigdots", "BigDots", DEF_BIGDOTS, t_Bool },
54 };
55
56 static XrmOptionDescRec opts[] = {
57   { "-speed",   ".speed",   XrmoptionSepArg, 0 },
58   { "-zoom",    ".zoom",    XrmoptionSepArg, 0 },
59   { "-ticks",   ".ticks",   XrmoptionSepArg, 0 },
60   { "-bigdots", ".bigdots", XrmoptionNoArg,  "True" },
61   { "+bigdots", ".bigdots", XrmoptionNoArg,  "False" },
62 };
63
64 ENTRYPOINT ModeSpecOpt cubicgrid_opts = {countof(opts), opts, countof(vars), vars, NULL};
65
66 #ifdef USE_MODULES
67 ModStruct   cubicgrid_description =
68 { "cubicgrid", "init_cubicgrid", "draw_cubicgrid", "release_cubicgrid",
69   "draw_cubicgrid", "change_cubicgrid", NULL, &cubicgrid_opts,
70   25000, 1, 1, 1, 1.0, 4, "",
71   "Shows a rotating 3D lattice from inside", 0, NULL
72 };
73 #endif
74
75 typedef struct {
76   GLXContext    *glx_context;
77   GLfloat       ratio;
78   GLint         list;
79
80   rotator *rot;
81   trackball_state *trackball;
82   Bool button_down_p;
83   int npoints;
84 } cubicgrid_conf;
85
86 static cubicgrid_conf *cubicgrid = NULL;
87
88 static const GLfloat zpos = -18.0;
89
90 /*************************************************************************/
91
92 ENTRYPOINT Bool
93 cubicgrid_handle_event (ModeInfo *mi, XEvent *event)
94 {
95   cubicgrid_conf *cp = &cubicgrid[MI_SCREEN(mi)];
96
97   if (event->xany.type == ButtonPress &&
98       event->xbutton.button == Button1)
99     {
100       cp->button_down_p = True;
101       gltrackball_start (cp->trackball,
102                          event->xbutton.x, event->xbutton.y,
103                          MI_WIDTH (mi), MI_HEIGHT (mi));
104       return True;
105     }
106   else if (event->xany.type == ButtonRelease &&
107            event->xbutton.button == Button1)
108     {
109       cp->button_down_p = False;
110       return True;
111     }
112   else if (event->xany.type == ButtonPress &&
113            (event->xbutton.button == Button4 ||
114             event->xbutton.button == Button5 ||
115             event->xbutton.button == Button6 ||
116             event->xbutton.button == Button7))
117     {
118       gltrackball_mousewheel (cp->trackball, event->xbutton.button, 2,
119                               !!event->xbutton.state);
120       return True;
121     }
122   else if (event->xany.type == MotionNotify &&
123            cp->button_down_p)
124     {
125       gltrackball_track (cp->trackball,
126                          event->xmotion.x, event->xmotion.y,
127                          MI_WIDTH (mi), MI_HEIGHT (mi));
128       return True;
129     }
130
131   return False;
132 }
133
134
135 static Bool draw_main(cubicgrid_conf *cp) 
136 {
137   double x, y, z;
138
139   glClear(GL_COLOR_BUFFER_BIT);
140   glLoadIdentity();
141   glTranslatef(0, 0, zpos);
142
143   glScalef(size/ticks, size/ticks, size/ticks);
144   gltrackball_rotate (cp->trackball);
145   get_rotation (cp->rot, &x, &y, &z, !cp->button_down_p);
146   glRotatef (x * 360, 1.0, 0.0, 0.0);
147   glRotatef (y * 360, 0.0, 1.0, 0.0);
148   glRotatef (z * 360, 0.0, 0.0, 1.0);
149
150   glTranslatef(-ticks/2.0, -ticks/2.0, -ticks/2.0);
151   glCallList(cp->list);
152   return True;
153 }
154
155 static void init_gl(ModeInfo *mi) 
156 {
157   cubicgrid_conf *cp = &cubicgrid[MI_SCREEN(mi)];
158   int x, y, z;
159   float tf = ticks;
160
161   glClearColor(0.0, 0.0, 0.0, 1.0);
162   glDrawBuffer(GL_BACK);
163   if(bigdots) {
164     glPointSize(2.0);
165   }
166   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
167   glShadeModel(GL_FLAT);
168
169   cp->list = glGenLists(1);
170   glNewList(cp->list, GL_COMPILE);
171   if(MI_IS_MONO(mi)) {
172     glColor3f(1.0, 1.0, 1.0);
173     glBegin(GL_POINTS);
174     for(x = 0; x < ticks; x++) {
175       for(y = 0; y < ticks; y++) {
176         for(z = 0; z < ticks; z++) {
177           glVertex3f(x, y, z);
178           cp->npoints++;
179         }
180       }
181     }
182     glEnd();
183   }
184   else
185   {
186     glBegin(GL_POINTS);
187     for(x = 0; x < ticks; x++) {
188       for(y = 0; y < ticks; y++) {
189         for(z = 0; z < ticks; z++) {
190           glColor3f(x/tf, y/tf, z/tf);
191           glVertex3f(x, y, z);
192           cp->npoints++;
193         }
194       }
195     }
196     glEnd();
197   }
198   glEndList();
199 }
200
201 /*************************************************************************/
202
203 ENTRYPOINT void reshape_cubicgrid(ModeInfo *mi, int width, int height) 
204 {
205   cubicgrid_conf *cp = &cubicgrid[MI_SCREEN(mi)];
206   if(!height) height = 1;
207   cp->ratio = (GLfloat)width/(GLfloat)height;
208   glViewport(0, 0, (GLint) width, (GLint) height);
209   glMatrixMode(GL_PROJECTION);
210   glLoadIdentity();
211   gluPerspective(30.0, cp->ratio, 1.0, 100.0);
212   glMatrixMode(GL_MODELVIEW);
213   glClear(GL_COLOR_BUFFER_BIT);
214 }
215
216 ENTRYPOINT void release_cubicgrid(ModeInfo *mi) 
217 {
218   if (cubicgrid != NULL) {
219     int screen;
220     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
221       cubicgrid_conf *cp = &cubicgrid[screen];
222       if (cp->glx_context) {
223         cp->glx_context = NULL;
224       }
225     }
226     free((void *)cubicgrid);
227     cubicgrid = NULL;
228   }
229   FreeAllGL(mi);
230 }
231
232 ENTRYPOINT void init_cubicgrid(ModeInfo *mi) 
233 {
234   cubicgrid_conf *cp;
235   if(!cubicgrid) {
236     cubicgrid = (cubicgrid_conf *)calloc(MI_NUM_SCREENS(mi), sizeof(cubicgrid_conf));
237     if(!cubicgrid) return;
238   }
239   cp = &cubicgrid[MI_SCREEN(mi)];
240
241   if ((cp->glx_context = init_GL(mi)) != NULL) {
242     init_gl(mi);
243     reshape_cubicgrid(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
244   } else {
245     MI_CLEARWINDOW(mi);
246   }
247
248   {
249     double spin_speed = 0.045 * speed;
250     double spin_accel = 0.005 * speed;
251
252     cp->rot = make_rotator (spin_speed, spin_speed, spin_speed,
253                             spin_accel, 0, True);
254     cp->trackball = gltrackball_init ();
255   }
256 }
257
258 ENTRYPOINT void draw_cubicgrid(ModeInfo * mi) 
259 {
260   Display *display = MI_DISPLAY(mi);
261   Window window = MI_WINDOW(mi);
262   cubicgrid_conf *cp;
263   if (!cubicgrid) return;
264   cp = &cubicgrid[MI_SCREEN(mi)];
265   MI_IS_DRAWN(mi) = True;
266   if (!cp->glx_context) return;
267   glXMakeCurrent(display, window, *(cp->glx_context));
268   if (!draw_main(cp)) {
269     release_cubicgrid(mi);
270     return;
271   }
272   mi->polygon_count = cp->npoints;
273   if (MI_IS_FPS(mi)) do_fps (mi);
274   glFlush();
275   glXSwapBuffers(display, window);
276 }
277
278 #ifndef STANDALONE
279 ENTRYPOINT void change_cubicgrid(ModeInfo * mi) 
280 {
281   cubicgrid_conf *cp = &cubicgrid[MI_SCREEN(mi)];
282   if (!cp->glx_context) return;
283   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(cp->glx_context));
284   init_gl(mi);
285 }
286 #endif /* !STANDALONE */
287
288
289 XSCREENSAVER_MODULE ("CubicGrid", cubicgrid)
290
291 #endif