From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / antspotlight.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  * Copyright 2003 Blair Tennessy
15  */
16
17 #ifdef STANDALONE
18 #define DEFAULTS            "*delay:   20000   \n" \
19                             "*showFPS: False   \n" \
20                             "*useSHM:  True    \n"
21
22 # define refresh_antspotlight 0
23 #include "xlockmore.h"
24 #else
25 #include "xlock.h"
26 #endif
27
28 #ifdef HAVE_COCOA
29 # include "jwxyz.h"
30 #else
31 # include <X11/Xlib.h>
32 # include <GL/gl.h>
33 # include <GL/glu.h>
34 #endif
35
36 #ifdef HAVE_JWZGLES
37 # include "jwzgles.h"
38 #endif /* HAVE_JWZGLES */
39
40 #include "sphere.h"
41 #include "tube.h"
42 #include "rotator.h"
43 #include "gltrackball.h"
44
45 ENTRYPOINT ModeSpecOpt antspotlight_opts = {
46   0, NULL, 0, NULL, NULL
47 };
48
49 #ifdef USE_MODULES
50 ModStruct   antspotlight_description = {
51   "antspotlight", "init_antspotlight", "draw_antspotlight", 
52   "release_antspotlight", "draw_antspotlight", "change_antspotlight", 
53   (char *) NULL, &antspotlight_opts, 1000, 1, 1, 1, 4, 1.0, "",
54   "draws an ant scoping the screen", 0, NULL
55 };
56 #endif
57
58 #define Scale4Window               0.3
59 #define Scale4Iconic               0.4
60
61 #define sqr(A)                     ((A)*(A))
62
63 #ifndef Pi
64 #define Pi M_PI
65 #endif
66
67 #include "ants.h"
68 #include "grab-ximage.h"
69
70 typedef struct {
71   GLXContext *glx_context;
72   rotator    *rot;
73   trackball_state *trackball;
74   Bool        button_down_p;
75
76   GLfloat max_tx, max_ty;
77   int mono, wire, ticks;
78   GLuint screentexture;
79
80   Ant *ant;
81   double boardsize;
82   GLfloat spot_direction[3];
83   int mag;
84
85   Bool mipmap_p;
86   Bool waiting_for_image_p;
87
88 } antspotlightstruct;
89
90 static antspotlightstruct *antspotlight = (antspotlightstruct *) NULL;
91
92 #define NUM_SCENES      2
93
94 /* draw method for ant */
95 static Bool draw_ant(ModeInfo *mi, antspotlightstruct *mp,
96                      const GLfloat *Material, int mono, int shadow, 
97               float ant_step, Bool (*sphere)(float), Bool (*cone)(float))
98 {
99   
100   float cos1 = cos(ant_step);
101   float cos2 = cos(ant_step + 2 * Pi / 3);
102   float cos3 = cos(ant_step + 4 * Pi / 3);
103   float sin1 = sin(ant_step);
104   float sin2 = sin(ant_step + 2 * Pi / 3);
105   float sin3 = sin(ant_step + 4 * Pi / 3);
106   
107 /* Apparently this is a performance killer on many systems...
108    glEnable(GL_POLYGON_SMOOTH);
109  */
110   glEnable(GL_BLEND);
111   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
112
113   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mp->mono ? MaterialGray5 : Material);
114   glEnable(GL_CULL_FACE);
115   glPushMatrix();
116   glScalef(1, 1.3, 1);
117   if(!((*sphere)(0.18)))
118     return False;
119   glScalef(1, 1 / 1.3, 1);
120   glTranslatef(0.00, 0.30, 0.00);
121   if(!((*sphere)(0.2)))
122     return False;
123   
124   glTranslatef(-0.05, 0.17, 0.05);
125   glRotatef(-90, 1, 0, 0);
126   glRotatef(-25, 0, 1, 0);
127   if(!((*cone)(0.05)))
128     return False;
129   glTranslatef(0.00, 0.10, 0.00);
130   if(!((*cone)(0.05)))
131     return False;
132   glRotatef(25, 0, 1, 0);
133   glRotatef(90, 1, 0, 0);
134   
135   glScalef(1, 1.3, 1);
136   glTranslatef(0.15, -0.65, 0.05);
137   if(!((*sphere)(0.25)))
138     return False;
139   glScalef(1, 1 / 1.3, 1);
140   glPopMatrix();
141   glDisable(GL_CULL_FACE);
142   
143   glDisable(GL_LIGHTING);
144   
145   /* ANTENNAS */
146   glEnable(GL_LINE_SMOOTH);
147   glEnable(GL_BLEND);
148   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
149    
150   glBegin(GL_LINES);
151   glColor3fv(mp->mono ? MaterialGray5 : Material);
152   glVertex3f(0.00, 0.30, 0.00);
153   glColor3fv(MaterialGray);
154   glVertex3f(0.40, 0.70, 0.40);
155   mi->polygon_count++;
156   glColor3fv(mp->mono ? MaterialGray5 : Material);
157   glVertex3f(0.00, 0.30, 0.00);
158   glColor3fv(MaterialGray);
159   glVertex3f(0.40, 0.70, -0.40);
160   mi->polygon_count++;
161   glEnd();
162
163   if(!shadow) {
164     glBegin(GL_POINTS);
165     glColor3fv(mp->mono ? MaterialGray6 : MaterialGray5);
166     glVertex3f(0.40, 0.70, 0.40);
167     mi->polygon_count++;
168     glVertex3f(0.40, 0.70, -0.40);
169     mi->polygon_count++;
170     glEnd();
171   }
172
173   /* LEFT-FRONT ARM */
174   glBegin(GL_LINE_STRIP);
175   glColor3fv(mp->mono ? MaterialGray5 : Material);
176   glVertex3f(0.00, 0.05, 0.18);
177   glVertex3f(0.35 + 0.05 * cos1, 0.15, 0.25);
178   mi->polygon_count++;
179   glColor3fv(MaterialGray);
180   glVertex3f(-0.20 + 0.05 * cos1, 0.25 + 0.1 * sin1, 0.45);
181   mi->polygon_count++;
182   glEnd();
183
184   /* LEFT-CENTER ARM */
185   glBegin(GL_LINE_STRIP);
186   glColor3fv(mp->mono ? MaterialGray5 : Material);
187   glVertex3f(0.00, 0.00, 0.18);
188   glVertex3f(0.35 + 0.05 * cos2, 0.00, 0.25);
189   mi->polygon_count++;
190   glColor3fv(MaterialGray);
191   glVertex3f(-0.20 + 0.05 * cos2, 0.00 + 0.1 * sin2, 0.45);
192   mi->polygon_count++;
193   glEnd();
194
195   /* LEFT-BACK ARM */
196   glBegin(GL_LINE_STRIP);
197   glColor3fv(mp->mono ? MaterialGray5 : Material);
198   glVertex3f(0.00, -0.05, 0.18);
199   glVertex3f(0.35 + 0.05 * cos3, -0.15, 0.25);
200   mi->polygon_count++;
201   glColor3fv(MaterialGray);
202   glVertex3f(-0.20 + 0.05 * cos3, -0.25 + 0.1 * sin3, 0.45);
203   mi->polygon_count++;
204   glEnd();
205
206   /* RIGHT-FRONT ARM */
207   glBegin(GL_LINE_STRIP);
208   glColor3fv(mp->mono ? MaterialGray5 : Material);
209   glVertex3f(0.00, 0.05, -0.18);
210   glVertex3f(0.35 - 0.05 * sin1, 0.15, -0.25);
211   mi->polygon_count++;
212   glColor3fv(MaterialGray);
213   glVertex3f(-0.20 - 0.05 * sin1, 0.25 + 0.1 * cos1, -0.45);
214   mi->polygon_count++;
215   glEnd();
216
217   /* RIGHT-CENTER ARM */
218   glBegin(GL_LINE_STRIP);
219   glColor3fv(mp->mono ? MaterialGray5 : Material);
220   glVertex3f(0.00, 0.00, -0.18);
221   glVertex3f(0.35 - 0.05 * sin2, 0.00, -0.25);
222   mi->polygon_count++;
223   glColor3fv(MaterialGray);
224   glVertex3f(-0.20 - 0.05 * sin2, 0.00 + 0.1 * cos2, -0.45);
225   mi->polygon_count++;
226   glEnd();
227
228   /* RIGHT-BACK ARM */
229   glBegin(GL_LINE_STRIP);
230   glColor3fv(mp->mono ? MaterialGray5 : Material);
231   glVertex3f(0.00, -0.05, -0.18);
232   glVertex3f(0.35 - 0.05 * sin3, -0.15, -0.25);
233   mi->polygon_count++;
234   glColor3fv(MaterialGray);
235   glVertex3f(-0.20 - 0.05 * sin3, -0.25 + 0.1 * cos3, -0.45);
236   mi->polygon_count++;
237   glEnd();
238
239   if(!shadow) {
240     glBegin(GL_POINTS);
241     glColor3fv(MaterialGray5);
242     glVertex3f(-0.20 + 0.05 * cos1, 0.25 + 0.1 * sin1, 0.45);
243     glVertex3f(-0.20 + 0.05 * cos2, 0.00 + 0.1 * sin2, 0.45);
244     glVertex3f(-0.20 + 0.05 * cos3, -0.25 + 0.1 * sin3, 0.45);
245     glVertex3f(-0.20 - 0.05 * sin1, 0.25 + 0.1 * cos1, -0.45);
246     glVertex3f(-0.20 - 0.05 * sin2, 0.00 + 0.1 * cos2, -0.45);
247     glVertex3f(-0.20 - 0.05 * sin3, -0.25 + 0.1 * cos3, -0.45);
248     mi->polygon_count += 6;
249     glEnd();
250   }
251
252   glEnable(GL_LIGHTING);
253
254   return True;
255 }
256
257 /* filled sphere */
258 static Bool mySphere(float radius)
259 {
260 #if 0
261   GLUquadricObj *quadObj;
262   
263   if((quadObj = gluNewQuadric()) == 0)
264     return False;
265
266   gluQuadricDrawStyle(quadObj, (GLenum) GLU_FILL);
267   gluSphere(quadObj, radius, 16, 16);
268   gluDeleteQuadric(quadObj);
269 #else
270     glPushMatrix();
271     glScalef (radius, radius, radius);
272     glRotatef (90, 1, 0, 0);
273     unit_sphere (16, 16, False);
274     glPopMatrix();
275 #endif
276   return True;
277 }
278
279 /* silhouette sphere */
280 static Bool mySphere2(float radius)
281 {
282 #if 0
283   GLUquadricObj *quadObj;
284
285   if((quadObj = gluNewQuadric()) == 0)
286         return False;
287   gluQuadricDrawStyle(quadObj, (GLenum) GLU_LINE);
288   gluSphere(quadObj, radius, 16, 8);
289   gluDeleteQuadric(quadObj);
290 #else
291   /* #### no GLU_LINE */
292     glPushMatrix();
293     glScalef (radius, radius, radius);
294     glRotatef (90, 1, 0, 0);
295     unit_sphere (16, 16, True);
296     glPopMatrix();
297 #endif
298   return True;
299 }
300
301 /* no cone */
302 static Bool myCone2(float radius) { return True; }
303
304 static void draw_board(ModeInfo *mi, antspotlightstruct *mp)
305 {
306   int i, j;
307   double cutoff = Pi/3.0;
308   double center[3];
309   double centertex[2];
310
311   glEnable(GL_TEXTURE_2D);
312   glBindTexture(GL_TEXTURE_2D, mp->screentexture);
313   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray6);
314
315   /* draw mesh */
316
317   /* center is roughly spotlight position */
318   center[0] = mp->ant->position[0];/* + cos(ant->direction); */
319   center[1] = 0.0;
320   center[2] = mp->ant->position[2];/* - 0.7*sin(ant->direction);*/
321
322   centertex[0] = (mp->boardsize/2.0+center[0]) * mp->max_tx / mp->boardsize;
323   centertex[1] = mp->max_ty - ((mp->boardsize/2.0+center[2]) * mp->max_ty / mp->boardsize);
324
325 /*   glPolygonMode(GL_FRONT, GL_LINE); */
326 /*   glDisable(GL_TEXTURE_2D); */
327
328   /* 
329      the vertices determined here should correspond to the illuminated
330      board.  ideally the code adapts vertex distribution to the
331      intensity and shape of the light.
332      
333      i should be finding the intersection of the cone of light and
334      the board-plane.
335   */
336   for(i = -12; i < 12; ++i) {
337
338     double theta1, theta2;
339
340     glBegin(GL_TRIANGLE_STRIP);
341     glNormal3f(0.0, 1.0, 0.0);
342
343     glTexCoord2f(centertex[0], centertex[1]);
344     glVertex3f(center[0], 0.01, center[2]);
345
346     /* watch those constants */
347     theta1 = mp->ant->direction + i*(cutoff/8);
348     theta2 = mp->ant->direction + (i+1)*(cutoff/8);
349
350     for(j = 1; j <= 64; ++j) {
351       double point[3], tex[2];
352       /* double fj = pow(1.05, j) - 1.0;*/
353       double fj = j / 6.0;
354       point[0] = center[0] + fj*cos(theta1);
355       point[1] = 0.0;
356       point[2] = center[2] - fj*sin(theta1);
357
358       tex[0] = (mp->boardsize/2.0+point[0]) * mp->max_tx / mp->boardsize;
359       tex[1] = (mp->boardsize/2.0+point[2]) * mp->max_ty / mp->boardsize;
360
361       glTexCoord2f(tex[0], tex[1]);
362       glVertex3f(point[0], point[1], point[2]);
363
364       point[0] = center[0] + fj*cos(theta2);
365       point[1] = 0.0;
366       point[2] = center[2] - fj*sin(theta2);
367
368       tex[0] = (mp->boardsize/2.0+point[0]) * mp->max_tx / mp->boardsize;
369       tex[1] = (mp->boardsize/2.0+point[2]) * mp->max_ty / mp->boardsize;
370
371       glTexCoord2f(tex[0], tex[1]);
372       glVertex3f(point[0], point[1], point[2]);
373       mi->polygon_count++;
374     }
375
376     glEnd();
377   }
378
379   glDisable(GL_TEXTURE_2D);
380 }
381
382 /* return euclidean distance between two points */
383 static double distance(double x[3], double y[3])
384 {
385   double dx = x[0] - y[0];
386   double dz = x[2] - y[2];
387   return sqrt(dx*dx + dz*dz);
388 }
389
390 /* determine a new goal */
391 static void find_goal(antspotlightstruct *mp)
392 {
393   do {
394     mp->ant->goal[0] = random()%((int)(mp->boardsize+0.5)-2) - mp->boardsize/2.0 + 1.0;
395     mp->ant->goal[1] = 0.0;
396     mp->ant->goal[2] = random()%((int)(mp->boardsize+0.5)-2) - mp->boardsize/2.0 + 1.0;
397   }
398   while(distance(mp->ant->position, mp->ant->goal) < 2.0);
399 }
400
401 /* construct our ant */
402 static void build_ant(antspotlightstruct *mp)
403 {
404   mp->ant = (Ant *) malloc(sizeof (Ant));
405   mp->ant->position[0] = 0.0;
406   mp->ant->position[1] = 0.0;
407   mp->ant->position[2] = 0.0;
408   mp->ant->direction = 0.0;
409   mp->ant->velocity = 0.02;
410   mp->ant->material = MaterialGray5;
411   mp->ant->step = 0;
412   find_goal(mp);
413 }
414
415 #define EPSILON 0.01
416
417 static double sign(double d)
418 {
419   return d < 0.0 ? -1.0 : 1.0;
420 }
421
422 static double min(double a, double b)
423 {
424   return a < b ? a : b;
425 }
426
427 /*
428 static double max(double a, double b)
429 {
430   return a > b ? a : b;
431 }
432 */
433
434 /* find a new goal and reset steps */
435 static void reset_ant(antspotlightstruct *mp)
436 {
437   find_goal(mp);
438 }
439
440 /* draw ant composed of skeleton and glass */
441 static void show_ant(ModeInfo *mi, antspotlightstruct *mp)
442 {
443
444   glPushMatrix();
445
446   /* move into position */
447   glTranslatef(mp->ant->position[0], 0.33, mp->ant->position[2]);
448   glRotatef(180.0 + mp->ant->direction*180.0/Pi, 0.0, 1.0, 0.0);
449   glRotatef(90.0, 0.0, 0.0, 1.0);
450
451   /* draw skeleton */
452   draw_ant(mi, mp, mp->ant->material, mp->mono, 0, mp->ant->step, mySphere2, myCone2);
453
454   /* draw glass */
455   if(!mp->wire && !mp->mono) {
456     glEnable(GL_BLEND);
457     glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGrayB);
458     glColor4fv(MaterialGrayB);
459     draw_ant(mi, mp, MaterialGrayB, mp->mono, 0, mp->ant->step, mySphere, myCone2);
460     glDisable(GL_BLEND);
461   }
462
463   glPopMatrix();
464 }
465
466 static void draw_antspotlight_strip(ModeInfo *mi)
467 {
468   antspotlightstruct *mp = &antspotlight[MI_SCREEN(mi)];
469
470   /* compute spotlight position and direction */
471   GLfloat light1_position[4];
472
473   light1_position[0] = mp->ant->position[0] + 0.7*cos(mp->ant->direction);
474   light1_position[1] = 0.5;
475   light1_position[2] = mp->ant->position[2] - 0.7*sin(mp->ant->direction);
476   light1_position[3] = 1.0;
477
478   mp->spot_direction[0] = cos(mp->ant->direction);
479   mp->spot_direction[1] = -0.5;
480   mp->spot_direction[2] = -sin(mp->ant->direction);
481
482   glLightfv(GL_LIGHT2, GL_POSITION, light1_position);
483   glLightfv(GL_LIGHT2, GL_SPOT_DIRECTION, mp->spot_direction);
484   
485   glEnable(GL_LIGHT2);
486   glDisable(GL_LIGHT0);
487   glDisable(GL_LIGHT1);
488
489   /* draw board */
490   if(mp->wire)
491     ;
492   else
493     draw_board(mi, mp);
494
495   glDisable(GL_LIGHT2);
496   glEnable(GL_LIGHT0);
497   glEnable(GL_LIGHT1);
498   
499   /* now modify ant */
500   show_ant(mi, mp);
501
502   /* near goal, bend path towards next step */
503   if(distance(mp->ant->position, mp->ant->goal) < 0.2) {
504     reset_ant(mp);
505   }
506
507   if(random()%100 == 0) {
508     reset_ant(mp);
509   }
510
511
512   /* move toward goal, correct ant direction if required */
513   else {
514     
515     /* difference */
516     double dx = mp->ant->goal[0] - mp->ant->position[0];
517     double dz = -(mp->ant->goal[2] - mp->ant->position[2]);
518     double theta, ideal, dt;
519     
520     if(fabs(dx) > EPSILON) {
521       theta = atan(dz/dx);
522       if(dx < 0.0)
523         theta += Pi;
524     }
525     else
526       theta = dz > 0.0 ? (1.0/2.0)*Pi : (3.0/2.0)*Pi;
527     
528     if(theta < 0.0)
529       theta += 2*Pi;
530     
531     ideal = theta - mp->ant->direction;
532     if(ideal > Pi)
533       ideal -= 2*Pi;
534     
535     /* compute correction */
536     dt = sign(ideal) * min(fabs(ideal), Pi/100.0);
537     mp->ant->direction += dt;
538     while(mp->ant->direction < 0.0)
539       mp->ant->direction += 2*Pi;
540     while(mp->ant->direction > 2*Pi)
541       mp->ant->direction -= 2*Pi;
542   }
543
544   mp->ant->position[0] += mp->ant->velocity * cos(mp->ant->direction);
545   mp->ant->position[2] += mp->ant->velocity * sin(-mp->ant->direction);
546   mp->ant->step += 10*mp->ant->velocity;
547   while(mp->ant->step > 2*Pi)
548     mp->ant->step -= 2*Pi;
549 }
550
551 ENTRYPOINT void reshape_antspotlight(ModeInfo * mi, int width, int height)
552 {
553   double h = (GLfloat) height / (GLfloat) width;  
554   int size = 2;
555   glViewport(0, 0, width, height);
556   glMatrixMode(GL_PROJECTION);
557   glLoadIdentity();
558
559   gluPerspective(45, 1/h, 1.0, 25.0);
560
561   glMatrixMode(GL_MODELVIEW);
562   glLineWidth(size);
563   glPointSize(size);
564 }
565
566 /* lighting variables */
567 static const GLfloat front_shininess[] = {60.0};
568 static const GLfloat front_specular[] = {0.8, 0.8, 0.8, 1.0};
569 static const GLfloat ambient[] = {0.4, 0.4, 0.4, 1.0};
570 /*static const GLfloat ambient2[] = {0.0, 0.0, 0.0, 0.0};*/
571 static const GLfloat diffuse[] = {1.0, 1.0, 1.0, 1.0};
572 static const GLfloat position0[] = {1.0, 5.0, 1.0, 0.0};
573 static const GLfloat position1[] = {-1.0, -5.0, 1.0, 0.0};
574 /*static const GLfloat lmodel_ambient[] = {0.8, 0.8, 0.8, 1.0};*/
575 static const GLfloat lmodel_twoside[] = {GL_TRUE};
576 static const GLfloat spotlight_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
577 static const GLfloat spotlight_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
578
579 static void pinit(void)
580 {
581   glClearDepth(1.0);
582
583   /* setup twoside lighting */
584   glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
585   glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
586   glLightfv(GL_LIGHT0, GL_POSITION, position0);
587   glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
588   glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
589   glLightfv(GL_LIGHT1, GL_POSITION, position1);
590
591   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, spotlight_ambient);
592   glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
593   glEnable(GL_LIGHTING);
594   glEnable(GL_LIGHT0);
595   glEnable(GL_LIGHT1);
596
597   /* setup spotlight */
598   glLightfv(GL_LIGHT2, GL_AMBIENT, spotlight_ambient);
599   glLightfv(GL_LIGHT2, GL_DIFFUSE, spotlight_diffuse);
600   glLightf(GL_LIGHT2, GL_CONSTANT_ATTENUATION, 0.1);
601   glLightf(GL_LIGHT2, GL_LINEAR_ATTENUATION, 0.05);
602   glLightf(GL_LIGHT2, GL_QUADRATIC_ATTENUATION, 0.0);
603   glLightf(GL_LIGHT2, GL_SPOT_CUTOFF, 60.0);
604   glLightf(GL_LIGHT2, GL_SPOT_EXPONENT, 3.0);
605
606   glEnable(GL_NORMALIZE);
607   glFrontFace(GL_CCW);
608   glCullFace(GL_BACK);
609
610   /* setup material properties */
611   glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_shininess);
612   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_specular);
613
614   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
615   glShadeModel(GL_SMOOTH);
616 /*   glShadeModel(GL_FLAT); */
617   glEnable(GL_DEPTH_TEST);
618 }
619
620 /* cleanup routine */
621 ENTRYPOINT void release_antspotlight(ModeInfo * mi)
622 {
623
624   if(antspotlight) {
625     free((void *) antspotlight);
626     antspotlight = (antspotlightstruct *) NULL;
627   }
628
629   FreeAllGL(mi);
630 }
631
632 #define MAX_MAGNIFICATION 10
633 #define max(a, b) a < b ? b : a
634 #define min(a, b) a < b ? a : b
635
636 /* event handling */
637 ENTRYPOINT Bool antspotlight_handle_event(ModeInfo *mi, XEvent *event)
638 {
639   antspotlightstruct *mp = &antspotlight[MI_SCREEN(mi)];
640
641   if (gltrackball_event_handler (event, mp->trackball,
642                                  MI_WIDTH (mi), MI_HEIGHT (mi),
643                                  &mp->button_down_p))
644     return True;
645
646   if (event->xany.type == ButtonPress)
647     {
648       switch(event->xbutton.button) {
649
650       case Button1:
651         mp->button_down_p = True;
652         gltrackball_start(mp->trackball, 
653                           event->xbutton.x, event->xbutton.y,
654                           MI_WIDTH (mi), MI_HEIGHT (mi));
655         return True;
656       
657       case Button4:
658         mp->mag = max(mp->mag-1, 1);
659         return True;
660
661       case Button5:
662         mp->mag = min(mp->mag+1, MAX_MAGNIFICATION);
663         return True;
664       }
665     }
666
667   return False;
668 }
669
670 static void
671 image_loaded_cb (const char *filename, XRectangle *geometry,
672                  int image_width, int image_height, 
673                  int texture_width, int texture_height,
674                  void *closure)
675 {
676   antspotlightstruct *mp = (antspotlightstruct *) closure;
677
678   mp->max_tx = (GLfloat) image_width  / texture_width;
679   mp->max_ty = (GLfloat) image_height / texture_height;
680
681   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
682   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
683                    (mp->mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
684
685   mp->waiting_for_image_p = False;
686 }
687
688
689 /* get screenshot */
690 static void get_snapshot(ModeInfo *modeinfo)
691 {
692   antspotlightstruct *mp = &antspotlight[MI_SCREEN(modeinfo)];
693
694   if (MI_IS_WIREFRAME(modeinfo))
695     return;
696
697   mp->waiting_for_image_p = True;
698   mp->mipmap_p = True;
699   load_texture_async (modeinfo->xgwa.screen, modeinfo->window,
700                       *mp->glx_context, 0, 0, mp->mipmap_p, 
701                       mp->screentexture, image_loaded_cb, mp);
702 }
703
704
705 ENTRYPOINT void init_antspotlight(ModeInfo *mi)
706 {
707   double rot_speed = 0.3;
708
709   antspotlightstruct *mp;
710   
711   if(!antspotlight) {
712     if((antspotlight = (antspotlightstruct *) 
713         calloc(MI_NUM_SCREENS(mi), sizeof (antspotlightstruct))) == NULL)
714       return;
715   }
716   mp = &antspotlight[MI_SCREEN(mi)];
717   mp->rot = make_rotator (rot_speed, rot_speed, rot_speed, 1, 0, True);
718   mp->trackball = gltrackball_init (False);
719
720   if((mp->glx_context = init_GL(mi)) != NULL) {
721     reshape_antspotlight(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
722     glDrawBuffer(GL_BACK);
723     pinit();
724   }
725   else
726     MI_CLEARWINDOW(mi);
727
728   glGenTextures(1, &mp->screentexture);
729   glBindTexture(GL_TEXTURE_2D, mp->screentexture);
730   get_snapshot(mi);
731
732   build_ant(mp);
733   mp->mono = MI_IS_MONO(mi);
734   mp->wire = MI_IS_WIREFRAME(mi);
735   mp->boardsize = 8.0;
736   mp->mag = 1;
737 }
738
739 ENTRYPOINT void draw_antspotlight(ModeInfo * mi)
740 {
741   antspotlightstruct *mp;
742   
743   Display    *display = MI_DISPLAY(mi);
744   Window      window = MI_WINDOW(mi);
745   
746   if(!antspotlight)
747         return;
748   mp = &antspotlight[MI_SCREEN(mi)];
749   
750   MI_IS_DRAWN(mi) = True;
751   
752   if(!mp->glx_context)
753         return;
754   
755   mi->polygon_count = 0;
756
757   /* Just keep running before the texture has come in. */
758   /* if (mp->waiting_for_image_p) return; */
759
760   glXMakeCurrent(display, window, *(mp->glx_context));
761   
762   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
763   
764   glPushMatrix();
765   glRotatef(current_device_rotation(), 0, 0, 1);
766
767   /* position camera */
768
769   /* follow focused ant */
770   glTranslatef(0.0, 0.0, -6.0 - mp->mag);
771   glRotatef(35.0, 1.0, 0.0, 0.0);
772   gltrackball_rotate(mp->trackball);
773   glTranslatef(-mp->ant->position[0], mp->ant->position[1], -mp->ant->position[2]);
774
775   /* stable position */
776 /*   glTranslatef(0.0, 0.0, -10.0 - mag); */
777 /*   gltrackball_rotate(mp->trackball); */
778 /*   glRotatef(40.0, 1.0, 0.0, 0.0); */
779 /*   glRotatef(20.0, 0.0, 1.0, 0.0); */
780
781   draw_antspotlight_strip(mi);
782
783   ++mp->ticks;
784   
785   glPopMatrix();
786   
787   if (MI_IS_FPS(mi)) do_fps (mi);
788   glFlush();
789   
790   glXSwapBuffers(display, window);
791 }
792
793 #ifndef STANDALONE
794 ENTRYPOINT void change_antspotlight(ModeInfo * mi)
795 {
796   antspotlightstruct *mp = &antspotlight[MI_SCREEN(mi)];
797   
798   if (!mp->glx_context)
799         return;
800   
801   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
802   pinit();
803 }
804 #endif /* !STANDALONE */
805
806 XSCREENSAVER_MODULE ("AntSpotlight", antspotlight)