b3f0cf41fd79d1b9fc64c27ba9c78154ae85a848
[xscreensaver] / hacks / glx / pulsar.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* pulsar --- pulsar module for xscreensaver */
3 /*-
4  * Permission to use, copy, modify, and distribute this software and its
5  * documentation for any purpose and without fee is hereby granted,
6  * provided that the above copyright notice appear in all copies and that
7  * both that copyright notice and this permission notice appear in
8  * supporting documentation.
9  *
10  * This file is provided AS IS with no warranties of any kind.  The author
11  * shall have no liability with respect to the infringement of copyrights,
12  * trade secrets or any patents by this file or any part thereof.  In no
13  * event will the author be liable for any lost revenue or profits or
14  * other special, indirect and consequential damages.
15
16  * Revision History:
17  * 4-Apr-1999:  dek@cgl.ucsf.edu  Created module "pulsar"
18  * 27-Apr-1999:  dek@cgl.ucsf.edu  Submitted module "pulsar"
19  * 4-May-1999:  jwz@jwz.org  Added module "pulsar"
20  * 4-May-1999:  dek@cgl.ucsf.edu  Submitted module "pulsar" updates
21  *
22  * Notes:
23  * The pulsar screensaver draws a number of rotating, pulsing rectangles
24  * on your screen.  Depending on the options you choose, you can set a number
25  * of interesting OpenGL parameters, including alpha blending, depth testing, fog,
26  * lighting, texturing, mipmapping, bilinear filtering, and line antialiasing.  
27  * Additionally, there is a "frames per second" meter which gives an estimate of
28  * the speed of your graphics card.  
29  *
30  * Example command-line switches:
31  * Only draw a single quad, and don't fill it (boring but fast)
32  * pulsar -wire -quads 1 -fps
33  *
34  * Only try this with hardware graphics acceleration (PPro 200 w/ a Voodoo 2 runs great)
35  * pulsar -quads 10 -texture -mipmap -texture_quality -light -fog -fps
36  *                                                                 
37  */
38
39 #include <math.h> 
40 #include <stdio.h>
41 #include <stdlib.h>
42
43 /*-
44  * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
45  * otherwise caddr_t is not defined correctly
46  */
47
48 #include <X11/Intrinsic.h>
49
50 #ifdef STANDALONE
51 # define PROGCLASS                                              "Screensaver"
52 # define HACK_INIT                                              init_screensaver
53 # define HACK_DRAW                                              draw_screensaver
54 # define HACK_RESHAPE                                   reshape_screensaver
55 # define screensaver_opts                               xlockmore_opts
56 #define DEFAULTS                        "*delay:                        10000   \n" \
57                                                                                 "*showFPS:          False   \n" \
58                                                                                 "*light:                        False   \n" \
59                                         "*wire:                         False   \n" \
60                                         "*quads:                        5       \n" \
61                                         "*blend:                        True    \n" \
62                                         "*fog:                          False   \n" \
63                                         "*antialias:            False   \n" \
64                                         "*texture:                      False   \n" \
65                                         "*texture_quality:      False   \n" \
66                                         "*mipmap:                       False   \n" \
67                                         "*doDepth:                      False   \n" \
68                                                                                 "*image:                        BUILTIN \n"
69
70 # include "xlockmore.h"                         /* from the xscreensaver distribution */
71 #else /* !STANDALONE */
72 # include "xlock.h"                                     /* from the xlockmore distribution */
73 #endif /* !STANDALONE */
74
75 #ifdef USE_GL /* whole file */
76
77 #ifdef HAVE_XMU
78 # ifndef VMS
79 #  include <X11/Xmu/Drawing.h>
80 #else  /* VMS */
81 #  include <Xmu/Drawing.h>
82 # endif /* VMS */
83 #endif
84
85
86 #include <GL/gl.h>
87 #include <GL/glu.h>
88
89 #include <stdlib.h>
90 #include <stdio.h>
91 #include <string.h>
92
93 #include "xpm-ximage.h"
94
95 /* Functions for loading and storing textures */
96
97 #define checkImageWidth 64
98 #define checkImageHeight 64
99
100 /* Functions for handling the frames per second timer */
101 #include "GL/glx.h"
102
103 #undef countof
104 #define countof(x) (sizeof((x))/sizeof((*x)))
105
106 #define WIDTH 800
107 #define HEIGHT 600
108
109 #define NUM_QUADS 5
110 #define DEF_NUM_QUADS   "5"
111 #define DEF_LIGHT               "False"
112 #define DEF_WIRE                "False"
113 #define DEF_BLEND       "True"
114 #define DEF_FOG                 "False"
115 #define DEF_ANTIALIAS   "False"
116 #define DEF_TEXTURE     "False"
117 #define DEF_TEXTURE_QUALITY   "False"
118 #define DEF_MIPMAP      "False"
119 #define DEF_DO_DEPTH    "False"
120 #define DEF_IMAGE       "BUILTIN"
121
122 static int num_quads;
123 static int do_light;
124 static int do_wire;
125 static int do_blend;
126 static int do_fog;
127 static int do_antialias;
128 static int do_texture;
129 static int do_texture_quality;
130 static int do_mipmap;
131 static int do_depth;
132 static char *which_image;
133
134
135 static XrmOptionDescRec opts[] = {
136   {"-quads",   ".pulsar.quads",   XrmoptionSepArg, (caddr_t) NULL },
137   {"-light",   ".pulsar.light",   XrmoptionNoArg, (caddr_t) "true" },
138   {"+light",   ".pulsar.light",   XrmoptionNoArg, (caddr_t) "false" },
139   {"-wire",   ".pulsar.wire",   XrmoptionNoArg, (caddr_t) "true" },
140   {"+wire",   ".pulsar.wire",   XrmoptionNoArg, (caddr_t) "false" },
141   {"-blend",   ".pulsar.blend",   XrmoptionNoArg, (caddr_t) "true" },
142   {"+blend",   ".pulsar.blend",   XrmoptionNoArg, (caddr_t) "false" },
143   {"-fog",   ".pulsar.fog",   XrmoptionNoArg, (caddr_t) "true" },
144   {"+fog",   ".pulsar.fog",   XrmoptionNoArg, (caddr_t) "false" },
145   {"-antialias",   ".pulsar.antialias",   XrmoptionNoArg, (caddr_t) "true" },
146   {"+antialias",   ".pulsar.antialias",   XrmoptionNoArg, (caddr_t) "false" },
147   {"-texture",   ".pulsar.texture",   XrmoptionNoArg, (caddr_t) "true" },
148   {"+texture",   ".pulsar.texture",   XrmoptionNoArg, (caddr_t) "false" },
149   {"-texture_quality",   ".pulsar.texture_quality",   XrmoptionNoArg, (caddr_t) "true" },
150   {"+texture_quality",   ".pulsar.texture_quality",   XrmoptionNoArg, (caddr_t) "false" },
151   {"-mipmap",   ".pulsar.mipmap",   XrmoptionNoArg, (caddr_t) "true" },
152   {"+mipmap",   ".pulsar.mipmap",   XrmoptionNoArg, (caddr_t) "false" },
153   {"-do_depth",   ".pulsar.doDepth",   XrmoptionNoArg, (caddr_t) "true" },
154   {"+do_depth",   ".pulsar.doDepth",   XrmoptionNoArg, (caddr_t) "false" },
155   {"-image",   ".pulsar.image",  XrmoptionSepArg, (caddr_t) NULL },
156 };
157
158
159 static argtype vars[] = {
160   {&num_quads,    "quads",     "Quads",     DEF_NUM_QUADS, t_Int},
161   {&do_light,     "light",     "Light",     DEF_LIGHT,     t_Bool},
162   {&do_wire,      "wire",      "Wire",      DEF_WIRE,      t_Bool},
163   {&do_blend,     "blend",     "Blend",     DEF_BLEND,     t_Bool},
164   {&do_fog,       "fog",       "Fog",       DEF_FOG,       t_Bool},
165   {&do_antialias, "antialias", "Antialias", DEF_ANTIALIAS, t_Bool},
166   {&do_texture,   "texture",   "Texture",   DEF_TEXTURE,   t_Bool},
167   {&do_texture_quality, "texture_quality", "Texture_quality", DEF_TEXTURE_QUALITY,   t_Bool},
168   {&do_mipmap,    "mipmap",    "Mipmap",    DEF_MIPMAP,    t_Bool},
169   {&do_depth,    "doDepth",    "DoDepth",   DEF_DO_DEPTH,  t_Bool},
170   {&which_image, "image",      "Image",     DEF_IMAGE,     t_String},
171 };
172
173
174 static OptionStruct desc[] =
175 {
176         {"-quads num", "how many quads to draw"},
177         {"-/+ light", "whether to do enable lighting (slower)"},
178         {"-/+ wire", "whether to do use wireframe instead of filled (faster)"},
179         {"-/+ blend", "whether to do enable blending (slower)"},
180         {"-/+ fog", "whether to do enable fog (?)"},
181         {"-/+ antialias", "whether to do enable antialiased lines (slower)"},
182         {"-/+ texture", "whether to do enable texturing (much slower)"},
183         {"-/+ texture_quality", "whether to do enable linear/mipmap filtering (much much slower)"},
184         {"-/+ mipmap", "whether to do enable mipmaps (much slower)"},
185         {"-/+ depth", "whether to do enable depth buffer checking (slower)"},
186         {"-image <filename>", "texture image to load"},
187 };
188
189 ModeSpecOpt screensaver_opts = {countof(opts), opts, countof(vars), vars, desc};
190
191 #ifdef USE_MODULES
192 ModStruct   screensaver_description =
193 {"screensaver", "init_screensaver", "draw_screensaver", "release_screensaver",
194  "draw_screensaver", "init_screensaver", NULL, &screensaver_opts,
195  1000, 1, 2, 1, 4, 1.0, "",
196  "OpenGL screensaver", 0, NULL};
197 #endif
198
199
200 /* structure for holding the screensaver data */
201 typedef struct {
202   int screen_width, screen_height;
203   GLXContext *glx_context;
204   Window window;
205   XColor fg, bg;
206 } screensaverstruct;
207
208 static screensaverstruct *Screensaver = NULL;
209
210 struct quad
211 {
212   GLfloat tx, ty, tz;
213   GLfloat rx, ry, rz;
214
215   GLfloat dtx, dty, dtz;
216   GLfloat drx, dry, drz;
217
218 };
219
220
221 GLint quad_list;
222
223 static float scale_x=1, scale_y=1, scale_z=1;
224 static int frame = 0;
225
226 struct quad *quads;
227
228 GLubyte *
229 Generate_Image(int *width, int *height, int *format)
230 {
231   GLubyte *result;
232   int i, j, c;
233   int counter=0;
234
235   *width = checkImageWidth;
236   *height = checkImageHeight;
237   result = (GLubyte *)malloc(4 * (*width) * (*height));
238
239   counter = 0;
240   for (i = 0; i < checkImageWidth; i++) {
241     for (j = 0; j < checkImageHeight; j++) {
242       c = (((((i&0x8)==0))^(((j&0x8))==0)))*255;
243       result[counter++] = (GLubyte) c;
244       result[counter++] = (GLubyte) c;
245       result[counter++] = (GLubyte) c;
246       result[counter++] = (GLubyte) 255;
247     }
248   }
249
250   *format = GL_RGBA;
251   return result;
252 }
253
254
255 /* Create a texture in OpenGL.  First an image is loaded 
256    and stored in a raster buffer, then it's  */
257 void Create_Texture(ModeInfo *mi, const char *filename)
258 {
259   int height, width;
260   GLubyte *image;
261   int format;
262
263   if ( !strncmp(filename, "BUILTIN", 7))
264     image = Generate_Image(&width, &height, &format);
265   else
266     {
267       XImage *ximage = xpm_file_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
268                                            MI_COLORMAP (mi), filename);
269       image  = (GLubyte *) ximage->data;
270       width  = ximage->width;
271       height = ximage->height;
272       format = GL_RGBA;
273     }
274
275   /* GL_MODULATE or GL_DECAL depending on what you want */
276   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
277   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
278   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
279   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
280   /* perhaps we can edge a bit more speed at the expense of quality */
281   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
282
283   if (do_texture_quality) {
284         /* with texture_quality, the min and mag filters look *much* nice but are *much* slower */
285         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
286         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
287   }
288   else {
289         /* default is to do it quick and dirty */
290         /* if you have mipmaps turned on, but not texture quality, nothing will happen! */
291         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
292         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
293   }
294
295   /* mipmaps make the image look much nicer */
296   if (do_mipmap)
297     {
298       int status;
299       clear_gl_error();
300       status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, format,
301                                  GL_UNSIGNED_BYTE, image);
302       if (status)
303         {
304           const char *s = (char *) gluErrorString (status);
305           fprintf (stderr, "%s: error mipmapping %dx%d texture: %s\n",
306                    progname, width, height,
307                    (s ? s : "(unknown)"));
308           exit (1);
309         }
310       check_gl_error("mipmapping");
311     }
312   else
313     {
314       clear_gl_error();
315       glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0,
316                    format, GL_UNSIGNED_BYTE, image);
317       check_gl_error("texture");
318     }
319 }
320
321 void resetProjection(void) {
322   glMatrixMode(GL_PROJECTION);
323   glLoadIdentity();
324   glFrustum(-1, 1, -1, 1, 1, 100); 
325   glMatrixMode(GL_MODELVIEW);
326   glLoadIdentity();
327 }
328
329
330 void GenerateQuad(void)
331 {
332   int i;
333
334   quad_list = glGenLists(1);
335   glNewList(quad_list,GL_COMPILE);
336 #if 1
337   glBegin(GL_QUADS);
338   glColor4f(1,0,0,.4); glNormal3f(0,0,1);  glTexCoord2f(0,0); glVertex2f(-1, -1);
339   glColor4f(0,1,0,.4); glNormal3f(0,0,1);  glTexCoord2f(0,1); glVertex2f(-1,  1);
340   glColor4f(0,0,1,.4); glNormal3f(0,0,1);  glTexCoord2f(1,1); glVertex2f( 1,  1);
341   glColor4f(1,1,1,1); glNormal3f(0,0,1);  glTexCoord2f(1,0); glVertex2f( 1,  -1);
342 #else
343   glBegin(GL_TRIANGLE_STRIP);
344   glColor4f(0,1,0,.4); glNormal3f(0,0,1);  glTexCoord2f(0,1); glVertex2f(-1,  1);
345   glColor4f(1,0,0,.4); glNormal3f(0,0,1);  glTexCoord2f(0,0); glVertex2f(-1, -1);
346   glColor4f(0,0,1,.4); glNormal3f(0,0,1);  glTexCoord2f(1,1); glVertex2f( 1,  1);
347   glColor4f(1,1,1,.4); glNormal3f(0,0,1);  glTexCoord2f(1,0); glVertex2f( 1,  -1);
348 #endif
349   glEnd();
350   glEndList();
351
352   quads = (struct quad *) malloc(sizeof(struct quad) * num_quads);
353   for (i=0; i < num_quads; i++)
354     {
355       quads[i].rx = 0.;
356       quads[i].ry = 0.;
357       quads[i].rz = 0.;
358       quads[i].tx = 0.;
359       quads[i].ty = 0.;
360       quads[i].tz = -10;
361
362       quads[i].drx = frand(5.0);
363       quads[i].dry = frand(5.0);
364       quads[i].drz = 0;
365     }
366 }
367
368 void initializeGL(ModeInfo *mi, GLsizei width, GLsizei height) 
369 {
370   GLfloat fogColor[4] = { 0.1, 0.1, 0.1, 0.1 };
371
372   glViewport( 0, 0, width, height ); 
373   resetProjection();
374
375   if (do_depth)
376         glEnable(GL_DEPTH_TEST);
377
378   if (do_antialias) {
379         do_blend = 1;
380         glEnable(GL_LINE_SMOOTH);
381   }
382
383   if (do_blend) {
384         glEnable(GL_BLEND);
385         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
386   }
387
388
389   if (do_light) {
390         glShadeModel(GL_SMOOTH);
391         glEnable(GL_COLOR_MATERIAL);
392         glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
393   }
394
395   if (do_wire)
396         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
397   else 
398         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
399
400   if (do_fog) {
401         glEnable(GL_FOG);
402         glFogi(GL_FOG_MODE, GL_LINEAR);
403         glFogfv(GL_FOG_COLOR, fogColor);
404         glFogf(GL_FOG_DENSITY, 0.35);
405 /*      glHint(GL_FOG_HINT, GL_FASTEST); */
406         glFogf(GL_FOG_START, 50.0);
407         glFogf(GL_FOG_END, 100.0);
408   }
409         
410
411   if (do_texture)
412           Create_Texture(mi, which_image); 
413
414   GenerateQuad();
415 }
416 void drawQuads(void) {
417   int i;
418   for (i=0; i < num_quads; i++)
419     {
420       glPushMatrix();
421       glTranslatef(quads[i].tx,0,0);
422       glTranslatef(0,quads[i].ty,0);
423       glTranslatef(0,0,quads[i].tz);
424       glRotatef(quads[i].rx, 1,0,0);
425       glRotatef(quads[i].ry, 0,1,0);
426       glRotatef(quads[i].rz, 0,0,1);
427       glCallList(quad_list);
428       glPopMatrix();
429
430       quads[i].rx += quads[i].drx;
431       quads[i].ry += quads[i].dry;
432       quads[i].rz += quads[i].drz;
433
434     }
435 }
436
437 GLvoid drawScene(ModeInfo * mi) 
438 {
439 /*  check_gl_error ("drawScene"); */
440   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
441
442   /* we have to do this here because the FPS meter turns these 3 features off!! */
443   {
444         if (do_light) {
445           glEnable(GL_LIGHTING);
446           glEnable(GL_LIGHT0);
447         }
448         
449         if (do_texture) {
450           glEnable(GL_TEXTURE_2D);
451         }
452         
453         if (do_blend) {
454           glEnable(GL_BLEND);
455           glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
456         }
457   }
458
459   resetProjection();
460
461   /* use XYZ scaling factors to change the size of the pulsar */
462   glScalef(scale_x, scale_y, scale_z);
463   drawQuads();
464
465   /* update the scaling factors- cyclic */
466   scale_x = cos(frame/360.)*10.;
467   scale_y = sin(frame/360.)*10.;
468   scale_z = 1;
469
470   /* increment frame-counter */
471   frame++;
472
473 /*  check_gl_error ("drawScene"); */
474 }
475
476
477 void draw_screensaver(ModeInfo * mi)
478 {
479   screensaverstruct *gp = &Screensaver[MI_SCREEN(mi)];
480   Display    *display = MI_DISPLAY(mi);
481   Window      window = MI_WINDOW(mi);
482
483   if (!gp->glx_context)
484         return;
485
486   glXMakeCurrent(display, window, *(gp->glx_context));
487   drawScene(mi);
488   if (mi->fps_p) do_fps (mi);
489   glXSwapBuffers(display, window);
490 }
491
492 /* Standard reshape function */
493 void
494 reshape_screensaver(ModeInfo *mi, int width, int height)
495 {
496   glViewport( 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi) );
497   resetProjection();
498 }
499
500 void
501 init_screensaver(ModeInfo * mi)
502 {
503   int screen = MI_SCREEN(mi);
504
505   screensaverstruct *gp;
506
507   if (Screensaver == NULL) {
508         if ((Screensaver = (screensaverstruct *) calloc(MI_NUM_SCREENS(mi), sizeof (screensaverstruct))) == NULL)
509           return;
510   }
511   gp = &Screensaver[screen];
512
513   gp->window = MI_WINDOW(mi);
514   if ((gp->glx_context = init_GL(mi)) != NULL) {
515         reshape_screensaver(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
516         initializeGL(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
517   } else {
518         MI_CLEARWINDOW(mi);
519   }
520 }
521
522
523 /* all sorts of nice cleanup code should go here! */
524 void release_screensaver(ModeInfo * mi)
525 {
526   int screen;
527   if (Screensaver != NULL) {
528         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
529 /*        screensaverstruct *gp = &Screensaver[screen];*/
530         }
531         (void) free((void *) Screensaver);
532         Screensaver = NULL;
533   }
534   free(quads);
535   FreeAllGL(mi);
536 }
537 #endif