From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / pulsar.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* pulsar --- pulsar module for xpulsar */
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 pulsar 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 #ifdef STANDALONE
44 #define DEFAULTS                        "*delay:                        10000   \n" \
45                                                                                 "*showFPS:          False   \n" \
46
47 # define refresh_pulsar 0
48 # define release_pulsar 0
49 # define pulsar_handle_event 0
50 # include "xlockmore.h"                         /* from the xpulsar distribution */
51 #else /* !STANDALONE */
52 # include "xlock.h"                                     /* from the xlockmore distribution */
53 #endif /* !STANDALONE */
54
55 #ifdef USE_GL /* whole file */
56
57 #ifdef HAVE_XMU
58 # ifndef VMS
59 #  include <X11/Xmu/Drawing.h>
60 #else  /* VMS */
61 #  include <Xmu/Drawing.h>
62 # endif /* VMS */
63 #endif
64
65 #include "xpm-ximage.h"
66
67 /* Functions for loading and storing textures */
68
69 #define checkImageWidth 64
70 #define checkImageHeight 64
71
72 /* Functions for handling the frames per second timer */
73
74 #undef countof
75 #define countof(x) (sizeof((x))/sizeof((*x)))
76
77 #define WIDTH 800
78 #define HEIGHT 600
79
80 #define NUM_QUADS 5
81 #define DEF_QUADS       "5"
82 #define DEF_LIGHT               "False"
83 #define DEF_WIRE                "False"
84 #define DEF_BLEND       "True"
85 #define DEF_FOG                 "False"
86 #define DEF_ANTIALIAS   "False"
87 #define DEF_TEXTURE     "False"
88 #define DEF_TEXTURE_QUALITY   "False"
89 #define DEF_MIPMAP      "False"
90 #define DEF_DO_DEPTH    "False"
91 #define DEF_IMAGE       "BUILTIN"
92
93 static int num_quads;
94 static int do_light;
95 static int do_wire;
96 static int do_blend;
97 static int do_fog;
98 static int do_antialias;
99 static int do_texture;
100 static int do_texture_quality;
101 static int do_mipmap;
102 static int do_depth;
103 static char *which_image;
104
105
106 static XrmOptionDescRec opts[] = {
107   {"-quads",   ".pulsar.quads",   XrmoptionSepArg, 0 },
108   {"-light",   ".pulsar.light",   XrmoptionNoArg, "true" },
109   {"+light",   ".pulsar.light",   XrmoptionNoArg, "false" },
110   {"-wire",   ".pulsar.wire",   XrmoptionNoArg, "true" },
111   {"+wire",   ".pulsar.wire",   XrmoptionNoArg, "false" },
112   {"-blend",   ".pulsar.blend",   XrmoptionNoArg, "true" },
113   {"+blend",   ".pulsar.blend",   XrmoptionNoArg, "false" },
114   {"-fog",   ".pulsar.fog",   XrmoptionNoArg, "true" },
115   {"+fog",   ".pulsar.fog",   XrmoptionNoArg, "false" },
116   {"-antialias",   ".pulsar.antialias",   XrmoptionNoArg, "true" },
117   {"+antialias",   ".pulsar.antialias",   XrmoptionNoArg, "false" },
118   {"-texture",   ".pulsar.texture",   XrmoptionNoArg, "true" },
119   {"+texture",   ".pulsar.texture",   XrmoptionNoArg, "false" },
120   {"-texture_quality",   ".pulsar.textureQuality",   XrmoptionNoArg, "true" },
121   {"+texture_quality",   ".pulsar.textureQuality",   XrmoptionNoArg, "false" },
122   {"-mipmap",   ".pulsar.mipmap",   XrmoptionNoArg, "true" },
123   {"+mipmap",   ".pulsar.mipmap",   XrmoptionNoArg, "false" },
124   {"-do_depth",   ".pulsar.doDepth",   XrmoptionNoArg, "true" },
125   {"+do_depth",   ".pulsar.doDepth",   XrmoptionNoArg, "false" },
126   {"-image",   ".pulsar.image",  XrmoptionSepArg, 0 },
127 };
128
129
130 static argtype vars[] = {
131   {&num_quads,    "quads",     "Quads",     DEF_QUADS,     t_Int},
132   {&do_light,     "light",     "Light",     DEF_LIGHT,     t_Bool},
133   {&do_wire,      "wire",      "Wire",      DEF_WIRE,      t_Bool},
134   {&do_blend,     "blend",     "Blend",     DEF_BLEND,     t_Bool},
135   {&do_fog,       "fog",       "Fog",       DEF_FOG,       t_Bool},
136   {&do_antialias, "antialias", "Antialias", DEF_ANTIALIAS, t_Bool},
137   {&do_texture,   "texture",   "Texture",   DEF_TEXTURE,   t_Bool},
138   {&do_texture_quality, "textureQuality", "TextureQuality", DEF_TEXTURE_QUALITY,   t_Bool},
139   {&do_mipmap,    "mipmap",    "Mipmap",    DEF_MIPMAP,    t_Bool},
140   {&do_depth,    "doDepth",    "DoDepth",   DEF_DO_DEPTH,  t_Bool},
141   {&which_image, "image",      "Image",     DEF_IMAGE,     t_String},
142 };
143
144
145 static OptionStruct desc[] =
146 {
147         {"-quads num", "how many quads to draw"},
148         {"-/+ light", "whether to do enable lighting (slower)"},
149         {"-/+ wire", "whether to do use wireframe instead of filled (faster)"},
150         {"-/+ blend", "whether to do enable blending (slower)"},
151         {"-/+ fog", "whether to do enable fog (?)"},
152         {"-/+ antialias", "whether to do enable antialiased lines (slower)"},
153         {"-/+ texture", "whether to do enable texturing (much slower)"},
154         {"-/+ texture_quality", "whether to do enable linear/mipmap filtering (much much slower)"},
155         {"-/+ mipmap", "whether to do enable mipmaps (much slower)"},
156         {"-/+ depth", "whether to do enable depth buffer checking (slower)"},
157         {"-image <filename>", "texture image to load"},
158 };
159
160 ENTRYPOINT ModeSpecOpt pulsar_opts = {countof(opts), opts, countof(vars), vars, desc};
161
162 #ifdef USE_MODULES
163 ModStruct   pulsar_description =
164 {"pulsar", "init_pulsar", "draw_pulsar", NULL,
165  "draw_pulsar", "init_pulsar", NULL, &pulsar_opts,
166  1000, 1, 2, 1, 4, 1.0, "",
167  "OpenGL pulsar", 0, NULL};
168 #endif
169
170 struct quad
171 {
172   GLfloat tx, ty, tz;
173   GLfloat rx, ry, rz;
174
175   GLfloat dtx, dty, dtz;
176   GLfloat drx, dry, drz;
177
178 };
179
180
181 /* structure for holding the pulsar data */
182 typedef struct {
183   int screen_width, screen_height;
184   GLXContext *glx_context;
185   Window window;
186   XColor fg, bg;
187
188   GLint quad_list;
189   float scale_x, scale_y, scale_z;
190   int frame;
191
192   struct quad *quads;
193
194 } pulsarstruct;
195
196 static pulsarstruct *Pulsar = NULL;
197
198 static GLubyte *
199 Generate_Image(int *width, int *height, int *format)
200 {
201   GLubyte *result;
202   int i, j, c;
203   int counter=0;
204
205   *width = checkImageWidth;
206   *height = checkImageHeight;
207   result = (GLubyte *)malloc(4 * (*width) * (*height));
208
209   counter = 0;
210   for (i = 0; i < checkImageWidth; i++) {
211     for (j = 0; j < checkImageHeight; j++) {
212       c = (((((i&0x8)==0))^(((j&0x8))==0)))*255;
213       result[counter++] = (GLubyte) c;
214       result[counter++] = (GLubyte) c;
215       result[counter++] = (GLubyte) c;
216       result[counter++] = (GLubyte) 255;
217     }
218   }
219
220   *format = GL_RGBA;
221   return result;
222 }
223
224
225 /* Create a texture in OpenGL.  First an image is loaded 
226    and stored in a raster buffer, then it's  */
227 static void Create_Texture(ModeInfo *mi, const char *filename)
228 {
229   int height, width;
230   GLubyte *image;
231   int format;
232
233   if ( !strncmp(filename, "BUILTIN", 7))
234     {
235     BUILTIN:
236       image = Generate_Image(&width, &height, &format);
237     }
238   else
239     {
240       XImage *ximage = xpm_file_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
241                                            MI_COLORMAP (mi), filename);
242       if (! ximage)
243         goto BUILTIN;
244       image  = (GLubyte *) ximage->data;
245       width  = ximage->width;
246       height = ximage->height;
247       format = GL_RGBA;
248     }
249
250   /* GL_MODULATE or GL_DECAL depending on what you want */
251   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
252   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
253   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
254   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
255   /* perhaps we can edge a bit more speed at the expense of quality */
256   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
257
258   if (do_texture_quality) {
259         /* with texture_quality, the min and mag filters look *much* nice but are *much* slower */
260         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
261         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
262   }
263   else {
264         /* default is to do it quick and dirty */
265         /* if you have mipmaps turned on, but not texture quality, nothing will happen! */
266         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
267         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
268   }
269
270   /* mipmaps make the image look much nicer */
271   if (do_mipmap)
272     {
273       int status;
274       clear_gl_error();
275       status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, format,
276                                  GL_UNSIGNED_BYTE, image);
277       if (status)
278         {
279           const char *s = (char *) gluErrorString (status);
280           fprintf (stderr, "%s: error mipmapping %dx%d texture: %s\n",
281                    progname, width, height,
282                    (s ? s : "(unknown)"));
283           exit (1);
284         }
285       check_gl_error("mipmapping");
286     }
287   else
288     {
289       clear_gl_error();
290       glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0,
291                    format, GL_UNSIGNED_BYTE, image);
292       check_gl_error("texture");
293     }
294 }
295
296 static void resetProjection(void) 
297 {
298   glMatrixMode(GL_PROJECTION);
299   glLoadIdentity();
300   glFrustum(-1, 1, -1, 1, 1, 100); 
301   glMatrixMode(GL_MODELVIEW);
302   glLoadIdentity();
303 }
304
305
306 static void GenerateQuad(pulsarstruct *gp)
307 {
308   int i;
309
310   gp->quad_list = glGenLists(1);
311   glNewList(gp->quad_list,GL_COMPILE);
312 #if 1
313   glBegin(GL_QUADS);
314   glColor4f(1,0,0,.4); glNormal3f(0,0,1);  glTexCoord2f(0,0); glVertex2f(-1, -1);
315   glColor4f(0,1,0,.4); glNormal3f(0,0,1);  glTexCoord2f(0,1); glVertex2f(-1,  1);
316   glColor4f(0,0,1,.4); glNormal3f(0,0,1);  glTexCoord2f(1,1); glVertex2f( 1,  1);
317   glColor4f(1,1,1,1); glNormal3f(0,0,1);  glTexCoord2f(1,0); glVertex2f( 1,  -1);
318 #else
319   glBegin(GL_TRIANGLE_STRIP);
320   glColor4f(0,1,0,.4); glNormal3f(0,0,1);  glTexCoord2f(0,1); glVertex2f(-1,  1);
321   glColor4f(1,0,0,.4); glNormal3f(0,0,1);  glTexCoord2f(0,0); glVertex2f(-1, -1);
322   glColor4f(0,0,1,.4); glNormal3f(0,0,1);  glTexCoord2f(1,1); glVertex2f( 1,  1);
323   glColor4f(1,1,1,.4); glNormal3f(0,0,1);  glTexCoord2f(1,0); glVertex2f( 1,  -1);
324 #endif
325   glEnd();
326   glEndList();
327
328   gp->quads = (struct quad *) malloc(sizeof(struct quad) * num_quads);
329   for (i=0; i < num_quads; i++)
330     {
331       gp->quads[i].rx = 0.;
332       gp->quads[i].ry = 0.;
333       gp->quads[i].rz = 0.;
334       gp->quads[i].tx = 0.;
335       gp->quads[i].ty = 0.;
336       gp->quads[i].tz = -10;
337
338       gp->quads[i].drx = frand(5.0);
339       gp->quads[i].dry = frand(5.0);
340       gp->quads[i].drz = 0;
341     }
342 }
343
344 static void initializeGL(ModeInfo *mi, GLsizei width, GLsizei height) 
345 {
346   pulsarstruct *gp = &Pulsar[MI_SCREEN(mi)];
347   GLfloat fogColor[4] = { 0.1, 0.1, 0.1, 0.1 };
348
349   glViewport( 0, 0, width, height ); 
350   resetProjection();
351
352   if (do_depth)
353         glEnable(GL_DEPTH_TEST);
354
355   if (do_antialias) {
356         do_blend = 1;
357         glEnable(GL_LINE_SMOOTH);
358   }
359
360   if (do_blend) {
361         glEnable(GL_BLEND);
362         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
363   }
364
365
366   if (do_light) {
367         glShadeModel(GL_SMOOTH);
368         glEnable(GL_COLOR_MATERIAL);
369         glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
370   }
371
372 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
373   do_wire = 0;
374 # endif
375
376   if (do_wire)
377         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
378   else 
379         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
380
381   if (do_fog) {
382         glEnable(GL_FOG);
383         glFogi(GL_FOG_MODE, GL_LINEAR);
384         glFogfv(GL_FOG_COLOR, fogColor);
385         glFogf(GL_FOG_DENSITY, 0.35);
386 /*      glHint(GL_FOG_HINT, GL_FASTEST); */
387         glFogf(GL_FOG_START, 50.0);
388         glFogf(GL_FOG_END, 100.0);
389   }
390         
391
392   if (do_texture)
393           Create_Texture(mi, which_image); 
394
395   GenerateQuad(gp);
396 }
397
398 static void drawQuads(pulsarstruct *gp) 
399 {
400   int i;
401   for (i=0; i < num_quads; i++)
402     {
403       glPushMatrix();
404       glTranslatef(gp->quads[i].tx,0,0);
405       glTranslatef(0,gp->quads[i].ty,0);
406       glTranslatef(0,0,gp->quads[i].tz);
407       glRotatef(gp->quads[i].rx, 1,0,0);
408       glRotatef(gp->quads[i].ry, 0,1,0);
409       glRotatef(gp->quads[i].rz, 0,0,1);
410       glCallList(gp->quad_list);
411       glPopMatrix();
412
413       gp->quads[i].rx += gp->quads[i].drx;
414       gp->quads[i].ry += gp->quads[i].dry;
415       gp->quads[i].rz += gp->quads[i].drz;
416
417     }
418 }
419
420 static GLvoid drawScene(ModeInfo * mi) 
421 {
422   pulsarstruct *gp = &Pulsar[MI_SCREEN(mi)];
423 /*  check_gl_error ("drawScene"); */
424   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
425
426   /* we have to do this here because the FPS meter turns these 3 features off!! */
427   {
428         if (do_light) {
429           glEnable(GL_LIGHTING);
430           glEnable(GL_LIGHT0);
431         }
432         
433         if (do_texture) {
434           glEnable(GL_TEXTURE_2D);
435         }
436         
437         if (do_blend) {
438           glEnable(GL_BLEND);
439           glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
440         }
441   }
442
443   resetProjection();
444
445   /* use XYZ scaling factors to change the size of the pulsar */
446   glScalef(gp->scale_x, gp->scale_y, gp->scale_z);
447   drawQuads(gp);
448   mi->polygon_count = num_quads;
449
450   /* update the scaling factors- cyclic */
451   gp->scale_x = cos(gp->frame/360.)*10.;
452   gp->scale_y = sin(gp->frame/360.)*10.;
453   gp->scale_z = 1;
454
455   /* increment frame-counter */
456   gp->frame++;
457
458 /*  check_gl_error ("drawScene"); */
459 }
460
461
462 ENTRYPOINT void draw_pulsar(ModeInfo * mi)
463 {
464   pulsarstruct *gp = &Pulsar[MI_SCREEN(mi)];
465   Display    *display = MI_DISPLAY(mi);
466   Window      window = MI_WINDOW(mi);
467
468   if (!gp->glx_context)
469         return;
470
471   glXMakeCurrent(display, window, *(gp->glx_context));
472   drawScene(mi);
473   if (mi->fps_p) do_fps (mi);
474   glXSwapBuffers(display, window);
475 }
476
477 /* Standard reshape function */
478 ENTRYPOINT void
479 reshape_pulsar(ModeInfo *mi, int width, int height)
480 {
481   glViewport( 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi) );
482   resetProjection();
483 }
484
485 static void free_pulsar(ModeInfo * mi);
486
487 ENTRYPOINT void
488 init_pulsar(ModeInfo * mi)
489 {
490   int screen = MI_SCREEN(mi);
491
492   pulsarstruct *gp;
493
494   MI_INIT (mi, Pulsar, free_pulsar);
495   gp = &Pulsar[screen];
496
497   gp->window = MI_WINDOW(mi);
498
499   gp->scale_x = gp->scale_y = gp->scale_z = 1;
500
501   if ((gp->glx_context = init_GL(mi)) != NULL) {
502         reshape_pulsar(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
503         initializeGL(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
504   } else {
505         MI_CLEARWINDOW(mi);
506   }
507 }
508
509
510 /* all sorts of nice cleanup code should go here! */
511 static void free_pulsar(ModeInfo * mi)
512 {
513   pulsarstruct *gp = &Pulsar[MI_SCREEN(mi)];
514   free(gp->quads);
515 }
516 #endif
517
518 XSCREENSAVER_MODULE ("Pulsar", pulsar)