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