http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.20.tar.gz
[xscreensaver] / hacks / glx / extrusion.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* extrusion --- extrusion 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  * Tue Oct 19 22:24:47 PDT 1999    Initial creation by David Konerding
18  *                                 <dek@cgl.ucsf.edu>
19  *                                                                 
20  * Notes:
21  * This screensaver requires the GLE ("OpenGL Tubing and Extrusion Library")
22  * which can be obtained from http://www.linas.org/gle/index.html
23   */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #ifdef STANDALONE
30 # define PROGCLASS                                              "Screensaver"
31 # define HACK_INIT                                              init_screensaver
32 # define HACK_DRAW                                              draw_screensaver
33 # define HACK_RESHAPE                                   reshape_screensaver
34 # define HACK_HANDLE_EVENT                              screensaver_handle_event
35 # define EVENT_MASK                                             PointerMotionMask
36 # define screensaver_opts                               xlockmore_opts
37 #define DEFAULTS                        "*delay:                        20000   \n" \
38                                                                                 "*showFPS:              False   \n" \
39                                                                                 "*light:                        True    \n" \
40                                         "*wire:                         False   \n" \
41                                         "*texture:                      False   \n" \
42                                                                                 "*image:                        BUILTIN \n" \
43                                         "*name:             RANDOM  \n" \
44                                         "*example:          0       \n"
45
46 # include "xlockmore.h"                         /* from the xscreensaver distribution */
47 #else /* !STANDALONE */
48 # include "xlock.h"                                     /* from the xlockmore distribution */
49 #endif /* !STANDALONE */
50
51 #ifdef USE_GL /* whole file */
52
53 #ifdef HAVE_XMU
54 # ifndef VMS
55 #  include <X11/Xmu/Drawing.h>
56 #else  /* VMS */
57 #  include <Xmu/Drawing.h>
58 # endif /* VMS */
59 #endif
60
61 #include <stdlib.h>
62 #include <string.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <GL/gl.h>
66 #include <GL/glu.h>
67 #ifdef HAVE_GLE3
68 #include <GL/gle.h>
69 #else
70 #include <GL/tube.h>
71 #endif
72
73 #undef countof
74 #define countof(x) (sizeof((x))/sizeof((*x)))
75
76 #include "xpm-ximage.h"
77 #include "rotator.h"
78 #include "gltrackball.h"
79
80 #define checkImageWidth 64
81 #define checkImageHeight 64
82
83
84 extern void InitStuff_helix2(void);
85 extern void DrawStuff_helix2(void);
86 extern void InitStuff_helix3(void);
87 extern void DrawStuff_helix3(void);
88 extern void InitStuff_helix4(void);
89 extern void DrawStuff_helix4(void);
90 extern void InitStuff_joinoffset(void);
91 extern void DrawStuff_joinoffset(void);
92 extern void InitStuff_screw(void);
93 extern void DrawStuff_screw(void);
94 extern void InitStuff_taper(void);
95 extern void DrawStuff_taper(void);
96 extern void InitStuff_twistoid(void);
97 extern void DrawStuff_twistoid(void);
98
99
100
101 #define WIDTH 640
102 #define HEIGHT 480
103
104 #define DEF_LIGHT               "True"
105 #define DEF_WIRE                "False"
106 #define DEF_TEXTURE             "False"
107 #define DEF_TEX_QUAL   "False"
108 #define DEF_MIPMAP      "False"
109 #define DEF_NAME        "RANDOM"
110 #define DEF_IMAGE       "BUILTIN"
111
112 static int do_light;
113 static int do_wire;
114 static int do_texture;
115 static int do_tex_qual;
116 static int do_mipmap;
117 static char *which_name;
118 static char *which_image;
119
120 static XrmOptionDescRec opts[] = {
121   {"-light",           ".extrusion.light",   XrmoptionNoArg, "true" },
122   {"+light",           ".extrusion.light",   XrmoptionNoArg, "false" },
123   {"-wire",            ".extrusion.wire",    XrmoptionNoArg, "true" },
124   {"+wire",            ".extrusion.wire",    XrmoptionNoArg, "false" },
125   {"-texture",         ".extrusion.texture", XrmoptionNoArg, "true" },
126   {"+texture",         ".extrusion.texture", XrmoptionNoArg, "false" },
127   {"-texture",         ".extrusion.texture", XrmoptionNoArg, "true" },
128   {"+texture_quality", ".extrusion.texture", XrmoptionNoArg, "false" },
129   {"-texture_quality", ".extrusion.texture", XrmoptionNoArg, "true" },
130   {"+mipmap",          ".extrusion.mipmap",  XrmoptionNoArg, "false" },
131   {"-mipmap",          ".extrusion.mipmap",  XrmoptionNoArg, "true" },
132   {"-name",            ".extrusion.name",    XrmoptionSepArg, 0 },
133   {"-image",           ".extrusion.image",   XrmoptionSepArg, 0 },
134 };
135
136
137 static argtype vars[] = {
138   {&do_light,    "light",           "Light",           DEF_LIGHT,    t_Bool},
139   {&do_wire,     "wire",            "Wire",            DEF_WIRE,     t_Bool},
140   {&do_texture,  "texture",         "Texture",         DEF_TEXTURE,  t_Bool},
141   {&do_tex_qual, "texture_quality", "Texture_Quality", DEF_TEX_QUAL, t_Bool},
142   {&do_mipmap,   "mipmap",          "Mipmap",          DEF_MIPMAP,   t_Bool},
143   {&which_name,  "name",            "Name",            DEF_NAME,     t_String},
144   {&which_image, "image",           "Image",           DEF_IMAGE,    t_String},
145 };
146
147
148 static OptionStruct desc[] =
149 {
150   {"-name num", "example 'name' to draw (helix2, helix3, helix4, joinoffset, screw, taper, twistoid)"},
151   {"-/+ light", "whether to do enable lighting (slower)"},
152   {"-/+ wire", "whether to do use wireframe instead of filled (faster)"},
153   {"-/+ texture", "whether to apply a texture (slower)"},
154   {"-image <filename>", "texture image to load"},
155   {"-/+ texture_quality", "whether to use texture smoothing (slower)"},
156   {"-/+ mipmap", "whether to use texture mipmap (slower)"},
157 };
158
159 ModeSpecOpt screensaver_opts = {countof(opts), opts, countof(vars), vars, desc};
160
161 #ifdef USE_MODULES
162 ModStruct   screensaver_description =
163 {"screensaver", "init_screensaver", "draw_screensaver", "release_screensaver",
164  "draw_screensaver", "init_screensaver", NULL, &screensaver_opts,
165  1000, 1, 2, 1, 4, 1.0, "",
166  "OpenGL screensaver", 0, NULL};
167 #endif
168
169
170 /* structure for holding the screensaver data */
171 typedef struct {
172   int screen_width, screen_height;
173   GLXContext *glx_context;
174   rotator *rot;
175   trackball_state *trackball;
176   Bool button_down_p;
177   Bool button2_down_p;
178   int mouse_start_x, mouse_start_y;
179   int mouse_x, mouse_y;
180   int mouse_dx, mouse_dy;
181   Window window;
182   XColor fg, bg;
183 } screensaverstruct;
184
185 static screensaverstruct *Screensaver = NULL;
186
187
188
189 /* set up a light */
190 static GLfloat lightOnePosition[] = {40.0, 40, 100.0, 0.0};
191 static GLfloat lightOneColor[] = {0.99, 0.99, 0.00, 1.0}; 
192
193 static GLfloat lightTwoPosition[] = {-40.0, 40, 100.0, 0.0};
194 static GLfloat lightTwoColor[] = {0.00, 0.99, 0.99, 1.0}; 
195
196 float rot_x=0, rot_y=0, rot_z=0;
197 float lastx=0, lasty=0;
198
199 static float max_lastx=400,  max_lasty=400;
200 static float min_lastx=-400, min_lasty=-400;
201
202 static int screensaver_number;
203
204 struct functions {
205   void (*InitStuff)(void);
206   void (*DrawStuff)(void);
207   char *name;
208 };
209
210 /* currently joinoffset and twistoid look funny-
211    like we're looking at them from the back or something
212 */
213
214 static struct functions funcs_ptr[] = {
215   {InitStuff_helix2, DrawStuff_helix2, "helix2"},
216   {InitStuff_helix3, DrawStuff_helix3, "helix3"},
217   {InitStuff_helix4, DrawStuff_helix4, "helix4"},
218   {InitStuff_joinoffset, DrawStuff_joinoffset, "joinoffset"},
219   {InitStuff_screw, DrawStuff_screw, "screw"},
220   {InitStuff_taper, DrawStuff_taper, "taper"},
221   {InitStuff_twistoid, DrawStuff_twistoid, "twistoid"},
222 };
223
224 static int num_screensavers = countof(funcs_ptr);
225
226
227 /* BEGINNING OF FUNCTIONS */
228
229
230 GLubyte *
231 Generate_Image(int *width, int *height, int *format)
232 {
233   GLubyte *result;
234   int i, j, c;
235   int counter=0;
236
237   *width = checkImageWidth;
238   *height = checkImageHeight;
239   result = (GLubyte *)malloc(4 * (*width) * (*height));
240
241   counter = 0;
242   for (i = 0; i < checkImageWidth; i++) {
243     for (j = 0; j < checkImageHeight; j++) {
244       c = (((((i&0x8)==0))^(((j&0x8))==0)))*255;
245       result[counter++] = (GLubyte) c;
246       result[counter++] = (GLubyte) c;
247       result[counter++] = (GLubyte) c;
248       result[counter++] = (GLubyte) 255;
249     }
250   }
251
252   *format = GL_RGBA;
253   return result;
254 }
255
256
257 /* Create a texture in OpenGL.  First an image is loaded 
258    and stored in a raster buffer, then it's  */
259 void Create_Texture(ModeInfo *mi, const char *filename)
260 {
261   int height, width;
262   GLubyte *image;
263   int format;
264
265   if ( !strncmp(filename, "BUILTIN", 7))
266     image = Generate_Image(&width, &height, &format);
267   else
268     {
269       XImage *ximage = xpm_file_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
270                                            MI_COLORMAP (mi), filename);
271       image  = (GLubyte *) ximage->data;
272       width  = ximage->width;
273       height = ximage->height;
274       format = GL_RGBA;
275     }
276
277   /* GL_MODULATE or GL_DECAL depending on what you want */
278   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
279   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
280   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
281   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
282   /* perhaps we can edge a bit more speed at the expense of quality */
283   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
284
285   if (do_tex_qual) {
286         /* with texture_quality, the min and mag filters look *much* nice but are *much* slower */
287         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
288         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
289   }
290   else {
291         /* default is to do it quick and dirty */
292         /* if you have mipmaps turned on, but not texture quality, nothing will happen! */
293         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
294         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
295   }
296
297   /* mipmaps make the image look much nicer */
298   if (do_mipmap)
299     {
300       int status;
301       clear_gl_error();
302       status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, format,
303                                  GL_UNSIGNED_BYTE, image);
304       if (status)
305         {
306           const char *s = (char *) gluErrorString (status);
307           fprintf (stderr, "%s: error mipmapping %dx%d texture: %s\n",
308                    progname, width, height,
309                    (s ? s : "(unknown)"));
310           exit (1);
311         }
312       check_gl_error("mipmapping");
313     }
314   else
315     {
316       clear_gl_error();
317       glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0,
318                    format, GL_UNSIGNED_BYTE, image);
319       check_gl_error("texture");
320     }
321 }
322
323
324 static void
325 init_rotation (ModeInfo *mi)
326 {
327   screensaverstruct *gp = &Screensaver[MI_SCREEN(mi)];
328   double spin_speed = 0.5;
329   gp->rot = make_rotator (spin_speed, spin_speed, spin_speed,
330                           0.2,
331                           0.005,
332                           True);
333   gp->trackball = gltrackball_init ();
334
335   lastx = (random() % (int) (max_lastx - min_lastx)) + min_lastx;
336   lasty = (random() % (int) (max_lasty - min_lasty)) + min_lasty;
337 }
338
339
340 /* draw the screensaver once */
341 void
342 draw_screensaver(ModeInfo * mi)
343 {
344   screensaverstruct *gp = &Screensaver[MI_SCREEN(mi)];
345   Display    *display = MI_DISPLAY(mi);
346   Window      window = MI_WINDOW(mi);
347
348   static GLfloat color[4] = {0.6, 0.6, 0.4, 1.0};
349   /* static GLfloat spec[4]  = {0.6, 0.6, 0.6, 1.0}; */
350   /* static GLfloat shiny    = 40.0; */
351
352   double x, y, z;
353
354   if (!gp->glx_context)
355         return;
356
357   glPushMatrix();
358
359   gltrackball_rotate (gp->trackball);
360
361   get_rotation (gp->rot, &x, &y, &z,
362                 !(gp->button_down_p || gp->button2_down_p));
363   glRotatef (x * 360, 1.0, 0.0, 0.0);
364   glRotatef (y * 360, 0.0, 1.0, 0.0);
365   glRotatef (z * 360, 0.0, 0.0, 1.0);
366
367   /* track the mouse only if a button is down. */
368   if (gp->button2_down_p)
369     {
370       gp->mouse_dx += gp->mouse_x - gp->mouse_start_x;
371       gp->mouse_dy += gp->mouse_y - gp->mouse_start_y;
372       gp->mouse_start_x = gp->mouse_x;
373       gp->mouse_start_y = gp->mouse_y;
374     }
375
376   {
377     float scale = (max_lastx - min_lastx);
378     get_position (gp->rot, &x, &y, &z,
379                   !(gp->button_down_p || gp->button2_down_p));
380     lastx = x * scale + min_lastx + gp->mouse_dx;
381     lasty = y * scale + min_lasty + gp->mouse_dy;
382   }
383
384   glScalef(0.5, 0.5, 0.5);
385
386   /* glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec); */
387   /* glMateriali  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny); */
388
389   glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
390   glFrontFace(GL_CCW);
391
392   funcs_ptr[screensaver_number].DrawStuff();
393           
394   glPopMatrix();
395
396   if (mi->fps_p) do_fps (mi);
397   glXSwapBuffers(display, window);
398 }
399
400
401 /* set up lighting conditions */
402 static void
403 SetupLight(void)
404 {
405   glLightfv (GL_LIGHT0, GL_POSITION, lightOnePosition);
406   glLightfv (GL_LIGHT0, GL_DIFFUSE, lightOneColor);
407   glLightfv (GL_LIGHT1, GL_POSITION, lightTwoPosition);
408   glLightfv (GL_LIGHT1, GL_DIFFUSE, lightTwoColor);
409
410   glEnable (GL_LIGHT0);
411   glEnable (GL_LIGHT1);
412   glEnable (GL_LIGHTING);
413
414   glColorMaterial (GL_FRONT, GL_DIFFUSE);
415   glColorMaterial (GL_BACK, GL_DIFFUSE);
416   glEnable (GL_COLOR_MATERIAL);
417 }
418
419 /* Standard reshape function */
420 void
421 reshape_screensaver (ModeInfo *mi, int width, int height)
422 {
423   GLfloat h = (GLfloat) height / (GLfloat) width;
424
425   glViewport (0, 0, (GLint) width, (GLint) height);
426
427   glMatrixMode(GL_PROJECTION);
428   glLoadIdentity();
429   gluPerspective (30.0, 1/h, 1.0, 100.0);
430
431   glMatrixMode(GL_MODELVIEW);
432   glLoadIdentity();
433   gluLookAt( 0.0, 0.0, 30.0,
434              0.0, 0.0, 0.0,
435              0.0, 1.0, 0.0);
436
437   glClear(GL_COLOR_BUFFER_BIT);
438 }
439
440
441 /* decide which screensaver example to run */
442 static void
443 chooseScreensaverExample (ModeInfo *mi)
444 {
445   int i;
446   /* call the extrusion init routine */
447
448   if (!strncmp(which_name, "RANDOM", strlen(which_name))) {
449     screensaver_number = random() % num_screensavers;
450   }
451   else {
452         screensaver_number=-1;
453         for (i=0; i < num_screensavers; i++) {
454           if (!strncmp(which_name, funcs_ptr[i].name, strlen(which_name))) {
455                 screensaver_number = i;
456           }
457         }         
458   }
459         
460   if (screensaver_number < 0 || screensaver_number >= num_screensavers) {
461         fprintf(stderr, "%s: invalid screensaver example number!\n", progname);
462         fprintf(stderr, "%s: known screensavers:\n", progname);
463         for (i=0; i < num_screensavers; i++)
464           fprintf(stderr,"\t%s\n", funcs_ptr[i].name);
465         exit(1);
466   }
467   init_rotation(mi);
468   funcs_ptr[screensaver_number].InitStuff();
469 }
470
471
472 /* main OpenGL initialization routine */
473 static void
474 initializeGL(ModeInfo *mi, GLsizei width, GLsizei height) 
475 {
476   int style;
477   int mode;
478
479   reshape_screensaver(mi, width, height);
480   glViewport( 0, 0, width, height ); 
481
482   glEnable(GL_DEPTH_TEST);
483   glClearColor(0,0,0,0);
484   glDisable (GL_CULL_FACE);
485   glLightModeli (GL_LIGHT_MODEL_TWO_SIDE, True);
486   glShadeModel(GL_SMOOTH);
487
488   if (do_light)
489         SetupLight();
490   if (do_wire) {
491         glPolygonMode(GL_FRONT,GL_LINE);
492         glPolygonMode(GL_BACK,GL_LINE);
493   }
494   if (do_texture) {
495         Create_Texture(mi, which_image);
496         glEnable(GL_TEXTURE_2D);
497
498         /* configure the pipeline */
499         style = TUBE_JN_CAP;
500         style |= TUBE_CONTOUR_CLOSED;
501         style |= TUBE_NORM_FACET;
502         style |= TUBE_JN_ANGLE;
503         gleSetJoinStyle (style);
504
505         if (do_texture) {
506           mode = GLE_TEXTURE_ENABLE | GLE_TEXTURE_VERTEX_MODEL_FLAT;
507           glMatrixMode (GL_TEXTURE); glLoadIdentity ();
508           glScalef (0.25, 0.1, 1); glMatrixMode (GL_MODELVIEW);
509           gleTextureMode (mode);
510         }
511   }
512
513 }
514
515 Bool
516 screensaver_handle_event (ModeInfo *mi, XEvent *event)
517 {
518   screensaverstruct *gp = &Screensaver[MI_SCREEN(mi)];
519
520   if (event->xany.type == ButtonPress &&
521       event->xbutton.button == Button1)
522     {
523       gp->button_down_p = True;
524       gltrackball_start (gp->trackball,
525                          event->xbutton.x, event->xbutton.y,
526                          MI_WIDTH (mi), MI_HEIGHT (mi));
527       return True;
528     }
529   else if (event->xany.type == ButtonRelease &&
530            event->xbutton.button == Button1)
531     {
532       gp->button_down_p = False;
533       return True;
534     }
535   else if (event->xany.type == ButtonPress &&
536            (event->xbutton.button == Button4 ||
537             event->xbutton.button == Button5))
538     {
539       gltrackball_mousewheel (gp->trackball, event->xbutton.button, 10,
540                               !!event->xbutton.state);
541       return True;
542     }
543   else if (event->xany.type == ButtonPress &&
544            event->xbutton.button != Button1)
545     {
546       gp->button2_down_p = True;
547       gp->mouse_start_x = gp->mouse_x = event->xbutton.x;
548       gp->mouse_start_y = gp->mouse_y = event->xbutton.y;
549       return True;
550     }
551   else if (event->xany.type == ButtonRelease &&
552            event->xbutton.button != Button1)
553     {
554       gp->button2_down_p = False;
555       return True;
556     }
557   else if (event->xany.type == MotionNotify)
558     {
559       if (gp->button_down_p)
560         gltrackball_track (gp->trackball,
561                            event->xmotion.x, event->xmotion.y,
562                            MI_WIDTH (mi), MI_HEIGHT (mi));
563       if (gp->button2_down_p)
564         {
565           gp->mouse_x = event->xmotion.x;
566           gp->mouse_y = event->xmotion.y;
567         }
568       return True;
569     }
570
571   return False;
572 }
573
574
575 /* xscreensaver initialization routine */
576 void
577 init_screensaver (ModeInfo * mi)
578 {
579   int screen = MI_SCREEN(mi);
580   screensaverstruct *gp;
581
582   if (do_wire) do_light = 0;
583
584   if (Screensaver == NULL) {
585         if ((Screensaver = (screensaverstruct *)
586          calloc(MI_NUM_SCREENS(mi), sizeof (screensaverstruct))) == NULL)
587           return;
588   }
589   gp = &Screensaver[screen];
590
591   gp->window = MI_WINDOW(mi);
592   if ((gp->glx_context = init_GL(mi)) != NULL) {
593         reshape_screensaver(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
594         initializeGL(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
595         chooseScreensaverExample(mi);
596   } else {
597         MI_CLEARWINDOW(mi);
598   }
599
600 }
601
602 #endif  /* USE_GL */