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