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