http://ftp.x.org/contrib/applications/xscreensaver-3.10.tar.gz
[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  *
19  */
20
21 #include <math.h> 
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 /*-
26  * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
27  * otherwise caddr_t is not defined correctly
28  */
29
30 #include <X11/Intrinsic.h>
31
32 #ifdef STANDALONE
33 # define PROGCLASS                                              "Screensaver"
34 # define HACK_INIT                                              init_screensaver
35 # define HACK_DRAW                                              draw_screensaver
36 # define screensaver_opts                               xlockmore_opts
37 #define DEFAULTS                        "*light:                        False   \n" \
38                                         "*wire:                         False   \n" \
39                                         "*blend:                        True    \n" \
40                                         "*fog:                          False   \n" \
41                                         "*antialias:            False   \n" \
42                                         "*texture:                      False   \n" \
43                                         "*texture_quality:      False   \n" \
44                                         "*mipmap:                       False   \n" \
45                                         "*fps:                          False   \n" \
46                                         "*doDepth:                      False   \n" \
47
48 # include "xlockmore.h"                         /* from the xscreensaver distribution */
49 #else /* !STANDALONE */
50 # include "xlock.h"                                     /* from the xlockmore distribution */
51 #endif /* !STANDALONE */
52
53 #ifdef USE_GL /* whole file */
54
55 #ifdef HAVE_XPM
56 # include <X11/xpm.h>
57 # ifndef PIXEL_ALREADY_TYPEDEFED
58 # define PIXEL_ALREADY_TYPEDEFED /* Sigh, Xmu/Drawing.h needs this... */
59 # endif
60 #endif
61
62 #ifdef HAVE_XMU
63 # ifndef VMS
64 #  include <X11/Xmu/Drawing.h>
65 #else  /* VMS */
66 #  include <Xmu/Drawing.h>
67 # endif /* VMS */
68 #endif
69
70
71 #include <GL/gl.h>
72 #include <GL/glu.h>
73
74 #include <string.h>
75 #include <malloc.h>
76 #include <stdio.h>
77
78 /* Functions for loading and storing textures */
79
80 #define checkImageWidth 64
81 #define checkImageHeight 64
82
83 char *Textures[] = {
84   "./test.ppm4",
85 };
86
87 typedef struct {
88     int sizeX, sizeY;
89     GLubyte *data;
90 } PPMImage;
91
92 /* Functions for handling the frames per second timer */
93 #include "GL/glx.h"
94
95 #ifndef SAMPLE_FRAMES
96 #define SAMPLE_FRAMES 10
97 #endif
98
99 static GLint base;
100 static int FrameCounter = 0;
101 static double oldtime=-1., newtime=-1.;
102 static char FPSstring[1024]="FPS: NONE"; 
103
104 static float x_pos=0, y_pos=0;
105
106 #define FONT "-*-courier-medium-r-normal-*-240-*"
107
108
109
110 #define NUM_TRIANGLES 5
111 static float scale_x=1, scale_y=1, scale_z=1;
112 static int frame = 0;
113 struct triangle
114 {
115   GLfloat tx, ty, tz;
116   GLfloat rx, ry, rz;
117
118   GLfloat dtx, dty, dtz;
119   GLfloat drx, dry, drz;
120
121 };
122 struct triangle triangles[NUM_TRIANGLES];
123 GLint tri_list;
124
125
126 #undef countof
127 #define countof(x) (sizeof((x))/sizeof((*x)))
128
129 #define WIDTH 800
130 #define HEIGHT 600
131
132 int global_width=WIDTH, global_height=HEIGHT;
133
134
135 static XrmOptionDescRec opts[] = {
136   {"-light",   ".pulsar.light",   XrmoptionNoArg, (caddr_t) "true" },
137   {"+light",   ".pulsar.light",   XrmoptionNoArg, (caddr_t) "false" },
138   {"-wire",   ".pulsar.wire",   XrmoptionNoArg, (caddr_t) "true" },
139   {"+wire",   ".pulsar.wire",   XrmoptionNoArg, (caddr_t) "false" },
140   {"-blend",   ".pulsar.blend",   XrmoptionNoArg, (caddr_t) "true" },
141   {"+blend",   ".pulsar.blend",   XrmoptionNoArg, (caddr_t) "false" },
142   {"-fog",   ".pulsar.fog",   XrmoptionNoArg, (caddr_t) "true" },
143   {"+fog",   ".pulsar.fog",   XrmoptionNoArg, (caddr_t) "false" },
144   {"-antialias",   ".pulsar.antialias",   XrmoptionNoArg, (caddr_t) "true" },
145   {"+antialias",   ".pulsar.antialias",   XrmoptionNoArg, (caddr_t) "false" },
146   {"-texture",   ".pulsar.texture",   XrmoptionNoArg, (caddr_t) "true" },
147   {"+texture",   ".pulsar.texture",   XrmoptionNoArg, (caddr_t) "false" },
148   {"-texture_quality",   ".pulsar.texture_quality",   XrmoptionNoArg, (caddr_t) "true" },
149   {"+texture_quality",   ".pulsar.texture_quality",   XrmoptionNoArg, (caddr_t) "false" },
150   {"-mipmap",   ".pulsar.mipmap",   XrmoptionNoArg, (caddr_t) "true" },
151   {"+mipmap",   ".pulsar.mipmap",   XrmoptionNoArg, (caddr_t) "false" },
152   {"-fps",   ".pulsar.fps",   XrmoptionNoArg, (caddr_t) "true" },
153   {"+fps",   ".pulsar.fps",   XrmoptionNoArg, (caddr_t) "false" },
154   {"-do_depth",   ".pulsar.doDepth",   XrmoptionNoArg, (caddr_t) "true" },
155   {"+do_depth",   ".pulsar.doDepth",   XrmoptionNoArg, (caddr_t) "false" },
156 };
157
158 #define DEF_LIGHT  "False"
159 #define DEF_WIRE   "False"
160 #define DEF_BLEND   "True"
161 #define DEF_FOG   "False"
162 #define DEF_ANTIALIAS   "False"
163 #define DEF_TEXTURE   "False"
164 #define DEF_TEXTURE_QUALITY   "False"
165 #define DEF_MIPMAP   "False"
166 #define DEF_FPS   "False"
167 #define DEF_DO_DEPTH   "False"
168
169 static int do_light;
170 static int do_wire;
171 static int do_blend;
172 static int do_fog;
173 static int do_antialias;
174 static int do_texture;
175 static int do_texture_quality;
176 static int do_mipmap;
177 static int do_fps;
178 static int do_depth;
179
180 static argtype vars[] = {
181   {(caddr_t *) &do_light,    "light",   "Light",   DEF_LIGHT,   t_Bool},
182   {(caddr_t *) &do_wire,    "wire",   "Wire",   DEF_WIRE,   t_Bool},
183   {(caddr_t *) &do_blend,    "blend",   "Blend",   DEF_BLEND,   t_Bool},
184   {(caddr_t *) &do_fog,    "fog",   "Fog",   DEF_FOG,   t_Bool},
185   {(caddr_t *) &do_antialias,    "antialias",   "Antialias",   DEF_ANTIALIAS,   t_Bool},
186   {(caddr_t *) &do_texture,    "texture",   "Texture",   DEF_TEXTURE,   t_Bool},
187   {(caddr_t *) &do_texture_quality,    "texture_quality",   "Texture_quality",   DEF_TEXTURE_QUALITY,   t_Bool},
188   {(caddr_t *) &do_mipmap,    "mipmap",   "Mipmap",   DEF_MIPMAP,   t_Bool},
189   {(caddr_t *) &do_fps,    "fps",   "fps",   DEF_FPS,   t_Bool},
190   {(caddr_t *) &do_depth,    "doDepth",   "DoDepth",   DEF_DO_DEPTH,   t_Bool},
191 };
192
193
194 ModeSpecOpt screensaver_opts = {countof(opts), opts, countof(vars), vars, NULL};
195
196 #ifdef USE_MODULES
197 ModStruct   screensaver_description =
198 {"screensaver", "init_screensaver", "draw_screensaver", "release_screensaver",
199  "draw_screensaver", "init_screensaver", NULL, &screensaver_opts,
200  1000, 1, 2, 1, 4, 1.0, "",
201  "OpenGL screensaver", 0, NULL};
202 #endif
203
204
205 /* structure for holding the screensaver data */
206 typedef struct {
207   int screen_width, screen_height;
208   GLXContext *glx_context;
209   Window window;
210   XColor fg, bg;
211 } screensaverstruct;
212
213 static screensaverstruct *Screensaver = NULL;
214
215
216 void FPS_Setup(void)
217 {
218   Display *Dpy;
219   XFontStruct *fontInfo;
220   Font id;
221   int first=0, last=255;
222
223   Dpy = XOpenDisplay(NULL);
224   fontInfo = XLoadQueryFont(Dpy, FONT);
225   if (fontInfo == NULL)
226     {
227       fprintf(stderr, "Failed to load font %s\n", FONT);
228       exit(1);
229     }
230
231   id = fontInfo->fid;
232   first = fontInfo->min_char_or_byte2;
233   last = fontInfo->max_char_or_byte2;
234   
235   base = glGenLists((GLuint) last+1);
236   if (base == 0) {
237     fprintf (stderr, "out of display lists\n");
238     exit(1);
239   }
240   glXUseXFont(id, first, last-first+1, base+first);
241
242 }
243
244 void PrintString(float x, float y, char *string)
245 {
246   int len, i;
247
248   /* save the current state */
249   /* note: could be expensive! */
250   glPushAttrib(GL_ALL_ATTRIB_BITS);
251   glMatrixMode(GL_PROJECTION);
252   glLoadIdentity();
253   gluOrtho2D(0, global_width, 0, global_height);
254   glMatrixMode(GL_MODELVIEW);
255   glLoadIdentity();
256
257   /* disable lighting and texturing when drawing bitmaps! */
258   glDisable(GL_TEXTURE_2D);
259   glDisable(GL_LIGHTING);
260   glDisable(GL_BLEND);
261
262   /* draw a black background */
263
264   /* draw the text */
265   glColor3f(1,1,1);
266   glRasterPos2f( x, y);
267   len = (int) strlen(string);
268   for (i = 0; i < len; i++) {
269     glCallList(base+string[i]);
270   }
271
272   /* clean up after our state changes */
273   glPopAttrib();
274 }
275
276 double gettime(void)
277 {
278   struct timeval now;
279 #ifdef GETTIMEOFDAY_TWO_ARGS
280   struct timezone tzp;
281   gettimeofday(&now, &tzp);
282 #else
283   gettimeofday(&now);
284 #endif
285   return (double) (now.tv_sec + (((double) now.tv_usec) * 0.000001));
286 }
287
288 void DoFPS(void)
289 {
290   /* every SAMPLE_FRAMES frames, get the time and use it to get the 
291      frames per second */
292   if (!(FrameCounter % SAMPLE_FRAMES)) {
293     oldtime = newtime;
294     newtime = gettime();
295     sprintf(FPSstring, "FPS: %.02f", SAMPLE_FRAMES/(newtime-oldtime));
296   }
297
298   PrintString(x_pos,y_pos,FPSstring);
299
300   FrameCounter++;
301 }
302
303 GLubyte *Generate_Image(int *width, int *height, int *format)
304 {
305   GLubyte *result;
306   int i, j, c;
307   int counter=0;
308
309   *width = checkImageWidth;
310   *height = checkImageHeight;
311   result = (GLubyte *)malloc(4 * *width * *height);
312
313   counter = 0;
314   for (i = 0; i < checkImageWidth; i++) {
315     for (j = 0; j < checkImageHeight; j++) {
316       c = (((((i&0x8)==0))^(((j&0x8))==0)))*255;
317       result[counter++] = (GLubyte) c;
318       result[counter++] = (GLubyte) c;
319       result[counter++] = (GLubyte) c;
320       result[counter++] = (GLubyte) 255;
321     }
322   }
323
324   *format = GL_RGBA;
325   return result;
326 }
327
328
329
330 #ifdef TIFF
331 #include <tiffio>
332 /* Load a TIFF texture: requires libtiff */
333 uint32 *LoadTIFF(char *filename, int *width, int *height, int *format)
334 {
335   TIFF *tif;
336   char emsg[1024];
337   uint32 *raster;
338   TIFFRGBAImage img;
339   tsize_t npixels;
340
341   tif = TIFFOpen(filename, "r");
342   if (tif == NULL) {
343     fprintf(stderr, "Problem showing %s\n", filename);
344     return Generate_Image(&width, &height, &format);
345   }
346   if (TIFFRGBAImageBegin(&img, tif, 0, emsg)) {
347     npixels = (tsize_t) (img.width * img.height);
348     raster = (uint32 *) _TIFFmalloc(npixels * (tsize_t) sizeof(uint32));
349     if (raster != NULL) {
350       if (TIFFRGBAImageGet(&img, raster, img.width, img.height) == 0) {
351         TIFFError(filename, emsg);
352         return Generate_Image(&width, &height, &format);
353       }
354     }
355     TIFFRGBAImageEnd(&img);
356   } else {
357     TIFFError(filename, emsg);
358     return Generate_Image(&width, &height, &format);
359   }
360
361   *width = img.width;
362   *height = img.height;
363   *format = GL_RGBA;
364
365   TIFFClose(tif);
366   return raster;
367 }
368 #endif
369
370
371 /* Load a modified version of PPM format with an extra byte for alpha */
372 GLubyte *LoadPPM4(const char *filename, int *width, int *height, int *format)
373 {
374   char buff[1024];
375   PPMImage *result;
376   FILE *fp;
377   int maxval;
378
379   fp = fopen(filename, "rb");
380   if (!fp)
381     {
382       fprintf(stderr, "Unable to open file '%s'\n", filename);
383       return  Generate_Image(width, height, format);
384     }
385
386   if (!fgets(buff, sizeof(buff), fp))
387     {
388       perror(filename);
389       return  Generate_Image(width, height, format);
390     }
391
392   if (buff[0] != '6' || buff[1] != 'P')
393     {
394       fprintf(stderr, "Invalid image format (must be `6P')\n");
395       return  Generate_Image(width, height, format);
396     }
397
398   result = malloc(sizeof(PPMImage));
399   if (!result)
400     {
401       fprintf(stderr, "Unable to allocate memory\n");
402       return  Generate_Image(width, height, format);
403     }
404
405   do
406     {
407       fgets(buff, sizeof(buff), fp);
408     }
409   while (buff[0] == '#');
410     
411   if (sscanf(buff, "%d %d", &result->sizeX, &result->sizeY) != 2)
412     {
413       fprintf(stderr, "Error loading image `%s'\n", filename);
414       return  Generate_Image(width, height, format);
415     }
416
417   if (fscanf(fp, "%d", &maxval) != 1)
418     {
419       fprintf(stderr, "Error loading image `%s'\n", filename);
420       return  Generate_Image(width, height, format);
421     }
422
423   while (fgetc(fp) != '\n')
424     ;
425
426   result->data = (GLubyte *)malloc(4 * result->sizeX * result->sizeY);
427   if (!result)
428     {
429       fprintf(stderr, "Unable to allocate memory\n");
430       exit(1);
431     }
432
433   if (fread(result->data, 4 * result->sizeX, result->sizeY, fp) != result->sizeY)
434     {
435       fprintf(stderr, "Error loading image `%s'\n", filename);
436       return  Generate_Image(width, height, format);
437     }
438
439   fclose(fp);
440
441   *width = result->sizeX;
442   *height = result->sizeY;
443   *format = GL_RGBA;
444   return result->data;
445 }
446
447 /* Load a plain PPM image */
448 GLubyte *LoadPPM(const char *filename, int *width, int *height, int *format)
449 {
450   char buff[1024];
451   PPMImage *result;
452   FILE *fp;
453   int maxval;
454
455   fp = fopen(filename, "rb");
456   if (!fp)
457     {
458       fprintf(stderr, "Unable to open file '%s'\n", filename);
459       return  Generate_Image(width, height, format);
460       exit(1);
461     }
462   if (!fgets(buff, sizeof(buff), fp))
463     {
464       perror(filename);
465       return  Generate_Image(width, height, format);
466     }
467
468   if (buff[0] != 'P' || buff[1] != '6')
469     {
470       fprintf(stderr, "Invalid image format (must be `P6')\n");
471       return  Generate_Image(width, height, format);
472     }
473
474   result = malloc(sizeof(PPMImage));
475   if (!result)
476     {
477       fprintf(stderr, "Unable to allocate memory\n");
478       return  Generate_Image(width, height, format);
479     }
480
481   do
482     {
483       fgets(buff, sizeof(buff), fp);
484     }
485   while (buff[0] == '#');
486     
487   if (sscanf(buff, "%d %d", &result->sizeX, &result->sizeY) != 2)
488     {
489       fprintf(stderr, "Error loading image `%s'\n", filename);
490       return  Generate_Image(width, height, format);
491     }
492
493   if (fscanf(fp, "%d", &maxval) != 1)
494     {
495       fprintf(stderr, "Error loading image `%s'\n", filename);
496       return  Generate_Image(width, height, format);
497     }
498
499   while (fgetc(fp) != '\n')
500     ;
501
502   result->data = (GLubyte *)malloc(3 * result->sizeX * result->sizeY);
503   if (!result)
504     {
505       fprintf(stderr, "Unable to allocate memory\n");
506       return  Generate_Image(width, height, format);
507     }
508
509   if (fread(result->data, 3 * result->sizeX, result->sizeY, fp) != result->sizeY)
510     {
511       fprintf(stderr, "Error loading image `%s'\n", filename);
512       return  Generate_Image(width, height, format);
513     }
514
515   fclose(fp);
516
517   *width = result->sizeX;
518   *height = result->sizeY;
519   *format = GL_RGB;
520   return result->data;
521 }
522
523 /* Create a texture in OpenGL.  First an image is loaded 
524    and stored in a raster buffer, then it's  */
525 void Create_Texture(char *filename)
526 {
527   int height, width;
528   GLubyte *image;
529   GLint a;
530   int format;
531
532   fprintf(stdout, "Loading texture '%s'\n", filename);
533
534   if ( !strncmp((filename+strlen(filename)-3), "ppm", 3))
535     image = LoadPPM(filename, &width, &height, &format);
536   else if ( !strncmp((filename+strlen(filename)-4), "ppm4", 4))
537     image = LoadPPM4(filename, &width, &height, &format);
538 #ifdef TIFF
539   else if ( !strncmp((filename+strlen(filename)-4), "tiff", 4))
540     image = (GLubyte *)LoadTIFF(filename, &width, &height, &format);
541 #endif
542   else {
543     fprintf(stderr, "Unknown file format extension: '%s'\n", filename);
544     image = Generate_Image(&width, &height, &format);
545   }
546
547   /* GL_MODULATE or GL_DECAL depending on what you want */
548   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
549   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
550   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
551   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
552
553   if (do_texture_quality) {
554         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
555         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
556         glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
557   }
558   else {
559         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
560         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
561         glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
562   }
563
564   if (do_mipmap)
565         a=gluBuild2DMipmaps(GL_TEXTURE_2D, format, width, height, format, GL_UNSIGNED_BYTE, image);
566   else 
567         glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0,
568                                  format, GL_UNSIGNED_BYTE, image);
569
570   free(image);
571 }
572
573 void resetProjection(void) {
574   glMatrixMode(GL_PROJECTION);
575   glLoadIdentity();
576   glFrustum(-1, 1, -1, 1, 1, 100); 
577   glMatrixMode(GL_MODELVIEW);
578   glLoadIdentity();
579 }
580
581
582 void GenerateTriangle(void)
583 {
584   int i;
585
586   tri_list = glGenLists(1);
587   glNewList(tri_list,GL_COMPILE);
588 /*    glBegin(GL_TRIANGLES); */
589   glBegin(GL_QUADS);
590   glColor4f(1,0,0,.4); glNormal3f(0,0,1);  glTexCoord2f(0,0); glVertex2f(-1, -1);
591   glColor4f(0,1,0,.4); glNormal3f(0,0,1);  glTexCoord2f(0,1); glVertex2f(-1,  1);
592   glColor4f(0,0,1,.4); glNormal3f(0,0,1);  glTexCoord2f(1,1); glVertex2f( 1,  1);
593   glColor4f(1,1,1,1); glNormal3f(0,0,1);  glTexCoord2f(1,0); glVertex2f( 1,  -1);
594   glEnd();
595   glEndList();
596
597   for (i=0; i < NUM_TRIANGLES; i++)
598     {
599       triangles[i].rx = 0.;
600       triangles[i].ry = 0.;
601       triangles[i].rz = 0.;
602       triangles[i].tx = 0.;
603       triangles[i].ty = 0.;
604       triangles[i].tz = -10;
605
606       triangles[i].drx = drand48() * 5.;
607       triangles[i].dry = drand48() * 5.;
608       triangles[i].drz = 0;
609     }
610 }
611
612 void initializeGL(GLsizei width, GLsizei height) 
613 {
614   GLfloat fogColor[4] = { 0.1, 0.1, 0.1, 0.1 };
615
616   global_width=width;
617   global_height=height;
618
619   glViewport( 0, 0, width, height ); 
620   resetProjection();
621
622   if (do_depth)
623         glEnable(GL_DEPTH_TEST);
624
625   if (do_fps)
626         FPS_Setup();
627
628   if (do_antialias) {
629         do_blend = 1;
630         glEnable(GL_LINE_SMOOTH);
631   }
632
633   if (do_blend) {
634         glEnable(GL_BLEND);
635         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
636   }
637
638
639   if (do_light) {
640         glShadeModel(GL_SMOOTH);
641         glEnable(GL_COLOR_MATERIAL);
642         glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
643   }
644
645   if (do_wire)
646         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
647   else 
648         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
649
650   if (do_fog) {
651         glEnable(GL_FOG);
652         glFogi(GL_FOG_MODE, GL_LINEAR);
653         glFogfv(GL_FOG_COLOR, fogColor);
654         glFogf(GL_FOG_DENSITY, 0.35);
655 /*      glHint(GL_FOG_HINT, GL_FASTEST); */
656         glFogf(GL_FOG_START, 50.0);
657         glFogf(GL_FOG_END, 100.0);
658   }
659         
660
661   if (do_texture)
662         {
663           Create_Texture(Textures[0]); 
664         }
665
666   GenerateTriangle();
667 }
668 void drawTriangles(void) {
669   int i;
670   for (i=0; i < NUM_TRIANGLES; i++)
671     {
672       glPushMatrix();
673       glTranslatef(triangles[i].tx,0,0);
674       glTranslatef(0,triangles[i].ty,0);
675       glTranslatef(0,0,triangles[i].tz);
676       glRotatef(triangles[i].rx, 1,0,0);
677       glRotatef(triangles[i].ry, 0,1,0);
678       glRotatef(triangles[i].rz, 0,0,1);
679       glCallList(tri_list);
680       glPopMatrix();
681
682       triangles[i].rx += triangles[i].drx;
683       triangles[i].ry += triangles[i].dry;
684       triangles[i].rz += triangles[i].drz;
685
686     }
687 }
688
689 GLvoid drawScene(GLvoid) 
690 {
691
692   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
693
694   /* we have to do this here because the FPS meter turns these 3 features off!! */
695   {
696         if (do_light) {
697           glEnable(GL_LIGHTING);
698           glEnable(GL_LIGHT0);
699         }
700         
701         if (do_texture) {
702           glEnable(GL_TEXTURE_2D);
703         }
704         
705         if (do_blend) {
706           glEnable(GL_BLEND);
707           glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
708         }
709   }
710
711   resetProjection();
712   glScalef(scale_x, scale_y, scale_z);
713   drawTriangles();
714   
715   scale_x = cos(frame/360.)*10.;
716   scale_y = sin(frame/360.)*10.;
717   scale_z = 1;
718
719   frame++;
720   DoFPS();
721 }
722
723
724 void draw_screensaver(ModeInfo * mi)
725 {
726   screensaverstruct *gp = &Screensaver[MI_SCREEN(mi)];
727   Display    *display = MI_DISPLAY(mi);
728   Window      window = MI_WINDOW(mi);
729
730   if (!gp->glx_context)
731         return;
732
733   glXMakeCurrent(display, window, *(gp->glx_context));
734   drawScene();
735   glXSwapBuffers(display, window);
736 }
737
738 /* Standard reshape function */
739 static void
740 reshape(int width, int height)
741 {
742   glViewport( 0, 0, global_width, global_height );
743   resetProjection();
744 }
745
746 void
747 init_screensaver(ModeInfo * mi)
748 {
749   int screen = MI_SCREEN(mi);
750
751   screensaverstruct *gp;
752
753   if (Screensaver == NULL) {
754         if ((Screensaver = (screensaverstruct *) calloc(MI_NUM_SCREENS(mi), sizeof (screensaverstruct))) == NULL)
755           return;
756   }
757   gp = &Screensaver[screen];
758
759   gp->window = MI_WINDOW(mi);
760   if ((gp->glx_context = init_GL(mi)) != NULL) {
761         reshape(MI_WIDTH(mi), MI_HEIGHT(mi));
762         initializeGL(MI_WIDTH(mi), MI_HEIGHT(mi));
763   } else {
764         MI_CLEARWINDOW(mi);
765   }
766 }
767
768
769 /* all sorts of nice cleanup code should go here! */
770 void release_screensaver(ModeInfo * mi)
771 {
772   int screen;
773   if (Screensaver != NULL) {
774         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
775 /*        screensaverstruct *gp = &Screensaver[screen];*/
776         }
777         (void) free((void *) Screensaver);
778         Screensaver = NULL;
779   }
780   FreeAllGL(mi);
781 }
782 #endif
783