http://nanonyme.dy.fi/mirrors/hvl/distfiles/xscreensaver/xscreensaver-5.03.tar.gz
[xscreensaver] / hacks / glx / glcells.c
1 /* -*- Mode: C; tab-width: 2 -*- */
2 /* glcells --- Cells growing on your screen */
3
4 /*-
5  * Cells growing on your screen
6  *
7  * Copyright (c) 2006 by Matthias Toussaint
8  *
9  * Permission to use, copy, modify, and distribute this software and its
10  * documentation for any purpose and without fee is hereby granted,
11  * provided that the above copyright notice appear in all copies and that
12  * both that copyright notice and this permission notice appear in
13  * supporting documentation.
14  *
15  * This file is provided AS IS with no warranties of any kind.  The author
16  * shall have no liability with respect to the infringement of copyrights,
17  * trade secrets or any patents by this file or any part thereof.  In no
18  * event will the author be liable for any lost revenue or profits or
19  * other special, indirect and consequential damages.
20  *
21  * 2006: Written by Matthias Toussaint
22  */
23  
24 #include <sys/time.h> /* gettimeofday */
25
26 #include "xlockmore.h"
27 #include <math.h>
28
29 /**********************************
30   DEFINES
31  **********************************/
32
33 #define INDEX_OFFSET 100000
34 #define NUM_CELL_SHAPES 10
35
36 #define refresh_glcells 0
37 #define glcells_handle_event 0
38
39 #define DEF_DELAY     "20000"
40 #define DEF_MAXCELLS  "800"
41 #define DEF_RADIUS    "40"
42 #define DEF_SEEDS     "1"
43 #define DEF_QUALITY   "3"
44 #define DEF_KEEPOLD   "False"
45 #define DEF_MINFOOD   "5"
46 #define DEF_MAXFOOD   "20"
47 #define DEF_DIVIDEAGE "20"
48 #define DEF_MINDIST   "1.40"
49 #define DEF_PAUSE     "50"
50
51 #define DEFAULTS        "*delay:        30000            \n" \
52                         "*showFPS:      False            \n" \
53                         "*wireframe:    False            \n" \
54
55 #undef countof
56 #define countof(x) (sizeof((x))/sizeof((*x)))
57
58 #define USE_VERTEX_ARRAY
59
60 #define TEX_SIZE 64
61
62 /**********************************
63   TYPEDEFS
64  **********************************/
65  
66 typedef struct    /* a 3-D vector */
67 {
68   double x, y, z;   /* 3-D coordinates (we don't need w here) */
69 } Vector;
70
71 typedef struct    /* a triangle (indexes of vertexes in some list) */
72 {
73   int i[3];         /* the three indexes for the triangle corners */
74 } Triangle;
75
76 typedef struct
77 {
78   float *vertex;
79   float *normal;
80   unsigned *index;
81   int num_index;
82 } VertexArray;
83
84 typedef struct    /* an 3-D object without normal vectors */
85
86   Vector *vertex;       /* the vertexes */
87   Triangle *triangle;   /* triangle list */
88   int num_vertex;       /* number of vertexes */
89   int num_triangle;     /* number of triangles */
90 } Object;
91
92 typedef struct    /* an 3-D object with smooth normal vectors */
93 {
94   Vector *vertex;       /* the vertexes */
95   Vector *normal;       /* the vertex normal vectors */
96   Triangle *triangle;   /* triangle list */
97   int num_vertex;       /* number of vertexes */
98   int num_triangle;     /* number of triangles */
99 } ObjectSmooth;
100
101 typedef struct    /* Cell */
102 {
103   double x, y;      /* position */
104   double vx, vy;    /* movement vector */
105   int age;          /* cells age */
106   double min_dist;  /* minimum distance to other cells */
107   int energy;       /* health */
108   double rotation;  /* random rot, so they don't look all the same */
109   double radius;    /* current size of cell */
110   double growth;    /* current growth rate. might be <1.0 while dividing,
111                        >1.0 when finished dividing and food is available
112                        and 1.0 when grown up */
113 } Cell;
114
115 typedef struct    /* hacks state */
116 {
117   GLXContext *glx_context;
118   int width, height;    /* current size of viewport */
119   int num_cells;        /* current number of cell in list */
120   Cell *cell;           /* array of cells */
121   int cell_polys;
122   GLfloat color[4];     /* current cell color */
123   int radius;           /* cell radius */
124   int move_dist;        /* min distance from neighbours for forking */
125   int max_cells;        /* maximum number of cells */
126   int num_seeds;        /* number of initial seeds */
127   int keep_old_cells;   /* draw dead cells? */
128   int divide_age;       /* min age for division */
129                         /* display lists for the cell stages */
130   int cell_list[NUM_CELL_SHAPES];    
131   int nucleus_list;
132   int minfood;          /* minimum amount of food per area unit */
133   int maxfood;          /* maximum amount of food per area unit */
134   int pause;            /* pause at end (all cells dead) */
135   int pause_counter;
136   int wire;             /* draw wireframe? */
137   Object *sphere;       /* the raw undisturbed sphere */
138   double *disturbance;  /* disturbance values for the vertexes */
139   int *food;            /* our petri dish (e.g. screen) */
140   GLubyte *texture;     /* texture data for nucleus */
141   GLuint texture_name;  /* texture name for binding */
142 } State;
143
144 /**********************************
145   STATIC STUFF
146  **********************************/
147  
148 static State *sstate = NULL;
149
150 static XrmOptionDescRec opts[] = {
151   { "-maxcells",   ".maxcells",   XrmoptionSepArg, 0 },
152   { "-radius",     ".radius",     XrmoptionSepArg, 0 },
153   { "-seeds",      ".seeds",      XrmoptionSepArg, 0 },
154   { "-quality",    ".quality",    XrmoptionSepArg, 0 },
155   { "-minfood",    ".minfood",    XrmoptionSepArg, 0 },
156   { "-maxfood",    ".maxfood",    XrmoptionSepArg, 0 },
157   { "-divideage",  ".divideage",  XrmoptionSepArg, 0 },
158   { "-mindist",    ".mindist",    XrmoptionSepArg, 0 },
159   { "-pause",      ".pause",      XrmoptionSepArg, 0 },
160   { "-keepold",    ".keepold",    XrmoptionNoArg, "True" }
161 };
162
163 static int  s_maxcells;
164 static int  s_radius;
165 static int  s_seeds;
166 static int  s_quality;
167 static int  s_minfood;
168 static int  s_maxfood;
169 static int  s_divideage;
170 static int  s_pause;
171 static float s_min_dist;
172 static Bool s_keepold;
173
174 static argtype vars[] = {
175   {&s_maxcells,  "maxcells",  "Max Cells",      DEF_MAXCELLS, t_Int},
176   {&s_radius,    "radius",    "Radius",         DEF_RADIUS,   t_Int},
177   {&s_seeds,     "seeds",     "Seeds",          DEF_SEEDS,    t_Int},
178   {&s_quality,   "quality",   "Quality",        DEF_QUALITY,  t_Int},
179   {&s_minfood,   "minfood",   "Min Food",       DEF_MINFOOD,  t_Int},
180   {&s_maxfood,   "maxfood",   "Max Food",       DEF_MAXFOOD,  t_Int},
181   {&s_pause,   "pause",     "Pause at end",   DEF_PAUSE,  t_Int},
182   {&s_divideage, "divideage", "Age for duplication (Ticks)",       DEF_DIVIDEAGE,  t_Int},
183   {&s_min_dist,  "mindist",   "Minimum prefered distance to other cells",       DEF_MINDIST,  t_Float},
184   {&s_keepold,   "keepold",   "Keep old cells", DEF_KEEPOLD,  t_Bool}
185 };
186
187 /**********************************
188   PROTOTYPES
189  **********************************/
190  
191 /* render scene */
192 static int render( State *st );
193 /* create initial cells and fill petri dish with food */
194 static void create_cells( State * );
195 /* do one animation step */
196 static void tick( State *st );
197 /* draw a single cell */
198 static void draw_cell( State *st, int shape );
199 /* draw cells nucleus */
200 static void draw_nucleus( State *st );
201 /* return randum number in the interval min-max */
202 static int random_interval( int min, int max );
203 /* retunr random number in the interval 0-max */
204 static int random_max( int max );
205 /* create display list for given disturbance weighting factor */
206 static int create_list( State *st, double fac );
207 /* return length of vector */
208 static double vector_length( Vector * );
209 /* normalize vector */
210 static void vector_normalize( Vector * );
211 /* a += b */
212 static void vector_add( Vector *a, Vector *b );
213 /* a -= b */
214 static void vector_sub( Vector *a, Vector *b );
215 /* a *= fac */
216 static void vector_mul( Vector *a, double fac );
217 /* a.x = a.y = a.z = 0 */
218 static void vector_clear( Vector *a );
219 /* return crossproduct a*b in out */
220 static void vector_crossprod( Vector *a, Vector *b, Vector *out );
221 /* return 1 if vectors are equal (epsilon compare) otherwise 0 */
222 static int vector_compare( Vector *a, Vector *b );
223 /* compute normal vector of given triangle and return in out */
224 static void triangle_normal( Vector *a, Vector *b, Vector *c, Vector *out );
225 /* take an Object and create an ObjectSmooth out of it */
226 static ObjectSmooth *create_ObjectSmooth( Object * );
227 /* Subdivide the Object once (assuming it's supposed to be a shpere */
228 static Object *subdivide( Object *obj );
229 /* free an Object */
230 static void free_Object( Object * );
231 /* free an ObjectSmooth */
232 static void free_ObjectSmooth( ObjectSmooth * );
233 /* scale an Object. return pointer to the object */
234 /*static Object *scale_Object( Object *obj, double scale );*/
235 /* create a perfect sphere refining with divisions */
236 static Object *create_sphere( State *st, int divisions );
237 /* make a copy of the given Object */
238 static Object *clone_Object( Object * );
239 /* return 1 if cell is capable to divide */
240 static int can_divide( State *st, Cell *cell );
241 static VertexArray *array_from_ObjectSmooth( ObjectSmooth * );
242 static void create_nucleus_texture( State *st );
243
244 ENTRYPOINT ModeSpecOpt glcells_opts = { countof(opts), opts,                                                   countof(vars), vars, 
245                                         NULL };
246                                         
247
248 /**********************************
249   INLINE FUNCTIONS
250  **********************************/
251 /* create random numbers
252 */
253 static inline int random_interval( int min, int max )
254 {
255   return min+(random()%(max-min));
256 }
257
258 static inline int random_max( int max )
259 {
260   return random()%max;
261 }
262
263 /* Vector stuff
264 */
265
266 /* a += b */
267 static inline void vector_add( Vector *a, Vector *b )
268 {
269   a->x += b->x;
270   a->y += b->y;
271   a->z += b->z;
272 }
273
274 /* a -= b */
275 static inline void vector_sub( Vector *a, Vector *b )
276 {
277   a->x -= b->x;
278   a->y -= b->y;
279   a->z -= b->z;
280 }
281
282 /* a *= v */
283 static inline void vector_mul( Vector *a, double v )
284 {
285   a->x *= v;
286   a->y *= v;
287   a->z *= v;
288 }
289
290 /* set to 0 */
291 static inline void vector_clear( Vector *vec )
292 {
293   vec->x = vec->y = vec->z = 0;
294 }
295
296 /* return vector length */
297 static inline double vector_length( Vector *vec )
298 {
299   return sqrt( vec->x*vec->x + vec->y*vec->y + vec->z*vec->z );
300 }
301
302 /* normalize vector */
303 static inline void vector_normalize( Vector *vec )
304 {
305   double len = vector_length( vec );
306   
307   if (len != 0.0) {
308     vector_mul( vec, 1.0 / len );
309   }
310 }
311
312 /* crossproduct */
313 static inline void vector_crossprod( Vector *a, Vector *b, Vector *out )
314 {
315   out->x = a->y*b->z - a->z*b->y;
316   out->y = a->z*b->x - a->x*b->z;
317   out->z = a->x*b->y - a->y*b->x;
318 }
319
320 /* epsilon compare of two vectors */
321 static inline int vector_compare( Vector *a, Vector *b )
322 {
323   const double epsilon = 0.0000001;
324   Vector delta = *a;
325   
326   vector_sub( &delta, b );
327   if (fabs(delta.x) < epsilon && 
328       fabs(delta.y) < epsilon &&
329       fabs(delta.z) < epsilon) {
330     return 1;
331   }
332   
333   return 0;
334 }
335
336 /* check if given cell is capable of dividing 
337    needs space, must be old enough, grown up and healthy
338 */
339 static inline int can_divide( State *st, Cell *cell )
340 {
341   if (cell->min_dist > st->move_dist &&
342       cell->age >= st->divide_age &&
343       cell->radius > 0.99 * st->radius &&
344       cell->energy > 0) {
345     return 1;
346   }
347   
348   return 0;
349 }
350
351 /**********************************
352   FUNCTIONS
353  **********************************/
354
355 /* compute normal vector of given 
356    triangle spanned by the points a, b, c 
357 */
358 static void triangle_normal( Vector *a, Vector *b, Vector *c, Vector *out )
359 {
360   Vector v1 = *a;
361   Vector v2 = *a;
362   
363   vector_sub( &v1, b );
364   vector_sub( &v2, c );
365   vector_crossprod( &v1, &v2, out );
366 }
367
368 /* free */
369 static void free_Object( Object *obj )
370 {
371   free( obj->vertex );
372   free( obj->triangle );
373   free( obj );
374 }
375
376 static void free_ObjectSmooth( ObjectSmooth *obj )
377 {
378   free( obj->vertex );
379   free( obj->triangle );
380   free( obj->normal );
381   free( obj );
382 }
383
384 /* scale the given Object */
385 #if 0
386 static Object *scale_Object( Object *obj, double scale )
387 {
388   int v;
389   
390   for (v=0; v<obj->num_vertex; ++v) {
391     vector_mul( &obj->vertex[v], scale );
392   }
393   
394   return obj;
395 }
396 #endif
397
398 /* create a copy of the given Object */
399 static Object *clone_Object( Object *obj )
400 {
401   /* alloc */
402   Object *ret = (Object *) malloc( sizeof( Object ) );
403   
404   ret->vertex = 
405       (Vector *) malloc( obj->num_vertex*sizeof(Vector) );
406   ret->triangle = 
407       (Triangle *) malloc( obj->num_triangle*sizeof(Triangle) );
408   ret->num_vertex = obj->num_vertex;
409   ret->num_triangle = obj->num_triangle;
410   /* copy */
411   memcpy( ret->vertex, obj->vertex, 
412           obj->num_vertex*sizeof(Vector) );
413   memcpy( ret->triangle, obj->triangle,
414           obj->num_triangle*sizeof(Triangle) );
415   
416   return ret;
417 }
418
419 static VertexArray *array_from_ObjectSmooth( ObjectSmooth *obj )
420 {
421   int i, j;
422   VertexArray *array = (VertexArray *) malloc( sizeof( VertexArray ) );
423   
424   array->vertex = (float *) malloc( 3*sizeof(float)*obj->num_vertex );
425   array->normal = (float *) malloc( 3*sizeof(float)*obj->num_vertex );
426   array->index = (unsigned *) malloc( 3*sizeof(unsigned)*obj->num_triangle );
427   array->num_index = obj->num_triangle*3;
428   
429   for (i=0, j=0; i<obj->num_vertex; ++i) {
430     array->vertex[j]   = obj->vertex[i].x;
431     array->normal[j++] = obj->normal[i].x;
432     array->vertex[j]   = obj->vertex[i].y;
433     array->normal[j++] = obj->normal[i].y;
434     array->vertex[j]   = obj->vertex[i].z;
435     array->normal[j++] = obj->normal[i].z;
436   }
437
438   for (i=0, j=0; i<obj->num_triangle; ++i) {
439     array->index[j++] = obj->triangle[i].i[0];
440     array->index[j++] = obj->triangle[i].i[1];
441     array->index[j++] = obj->triangle[i].i[2];
442   }
443   
444   return array;
445 }
446
447 /* create a smoothed version of the given Object
448    by computing average normal vectors for the vertexes 
449 */
450 static ObjectSmooth *create_ObjectSmooth( Object *obj )
451 {
452   int t, v, i;
453   Vector *t_normal = 
454       (Vector *) malloc( obj->num_triangle*sizeof(Vector) );
455   ObjectSmooth *ret = 
456       (ObjectSmooth *) malloc( sizeof( ObjectSmooth ) );
457   
458   /* fill in vertexes and triangles */
459   ret->num_vertex = obj->num_vertex;
460   ret->num_triangle = obj->num_triangle;
461   ret->vertex = 
462       (Vector *) malloc( obj->num_vertex * sizeof( Vector ) );
463   ret->normal = 
464       (Vector *) malloc( obj->num_vertex * sizeof( Vector ) );
465   ret->triangle = 
466       (Triangle *) malloc( obj->num_triangle * sizeof( Triangle ) );
467   
468   for (v=0; v<obj->num_vertex; ++v) {
469     ret->vertex[v] = obj->vertex[v];
470   }
471   
472   for (t=0; t<obj->num_triangle; ++t) {
473     ret->triangle[t] = obj->triangle[t];
474   }
475   
476   /* create normals (triangles) */
477   for (t=0; t<ret->num_triangle; ++t) {
478     triangle_normal( &ret->vertex[ret->triangle[t].i[0]],
479                      &ret->vertex[ret->triangle[t].i[1]],
480                      &ret->vertex[ret->triangle[t].i[2]],
481                      &t_normal[t] );
482   }
483   
484   /* create normals (vertex) by averaging triangle 
485      normals at vertex
486   */
487   for (v=0; v<ret->num_vertex; ++v) {
488     vector_clear( &ret->normal[v] );
489     for (t=0; t<ret->num_triangle; ++t) {
490       for (i=0; i<3; ++i) {
491         if (ret->triangle[t].i[i] == v) {
492           vector_add( &ret->normal[v], &t_normal[t] );
493         }
494       }
495     }
496     /* as we have only a half sphere we force the
497        normals at the bortder to be perpendicular to z.
498        the simple algorithm above makes an error here.
499     */
500     if (fabs(ret->vertex[v].z) < 0.0001) {
501       ret->normal[v].z = 0.0;
502     }  
503     
504     vector_normalize( &ret->normal[v] );
505   }
506   
507   free( t_normal );
508   
509   return ret;
510 }
511
512 /* subdivide the triangles of the object once
513    The order of this algorithm is probably something like O(n^42) :)
514    but I can't think of something smarter at the moment 
515 */
516 static Object *subdivide( Object *obj )
517 {
518   /* create for worst case (which I dont't know) */
519   int start, t, i, v;
520   int index_list[1000];
521   int index_cnt, index_found;
522   Object *tmp = (Object *)malloc( sizeof(Object) );
523   Object *ret = (Object *)malloc( sizeof(Object) );
524   Object *c_ret;
525   
526   tmp->vertex = 
527       (Vector *)malloc( 100*obj->num_vertex*sizeof( Vector ) );
528   tmp->triangle = 
529       (Triangle *)malloc( 4*obj->num_triangle*sizeof( Triangle ) );
530   tmp->num_vertex = 0;
531   tmp->num_triangle = 0;
532   ret->vertex = 
533       (Vector *)malloc( 100*obj->num_vertex*sizeof( Vector ) );
534   ret->triangle = 
535       (Triangle *)malloc( 4*obj->num_triangle*sizeof( Triangle ) );
536   ret->num_vertex = 0;
537   ret->num_triangle = 0;
538 #ifdef PRINT_STAT
539   fprintf( stderr, "in v=%d t=%d\n", 
540            obj->num_vertex, obj->num_triangle );
541 #endif
542   /* for each triangle create 3 new vertexes and the 4 
543      corresponding triangles 
544   */
545   for (t=0; t<obj->num_triangle; ++t) {
546     /* copy the three original vertexes */
547     for (i=0; i<3; ++i) {
548       tmp->vertex[tmp->num_vertex++] =
549         obj->vertex[obj->triangle[t].i[i]];
550     }
551     
552     /* create 3 new */
553     tmp->vertex[tmp->num_vertex] = 
554         obj->vertex[obj->triangle[t].i[0]];
555     vector_add( &tmp->vertex[tmp->num_vertex],
556                  &obj->vertex[obj->triangle[t].i[1]] );
557     vector_mul( &tmp->vertex[tmp->num_vertex++], 0.5 ); 
558     
559     tmp->vertex[tmp->num_vertex] = 
560         obj->vertex[obj->triangle[t].i[1]];
561     vector_add( &tmp->vertex[tmp->num_vertex],
562                  &obj->vertex[obj->triangle[t].i[2]] );
563     vector_mul( &tmp->vertex[tmp->num_vertex++], 0.5 ); 
564     
565     tmp->vertex[tmp->num_vertex] = 
566         obj->vertex[obj->triangle[t].i[2]];
567     vector_add( &tmp->vertex[tmp->num_vertex],
568                  &obj->vertex[obj->triangle[t].i[0]] );
569     vector_mul( &tmp->vertex[tmp->num_vertex++], 0.5 ); 
570
571     /* create triangles */
572     start = tmp->num_vertex-6;
573     
574     tmp->triangle[tmp->num_triangle].i[0] = start;
575     tmp->triangle[tmp->num_triangle].i[1] = start+3;
576     tmp->triangle[tmp->num_triangle++].i[2] = start+5;
577       
578     tmp->triangle[tmp->num_triangle].i[0] = start+3;
579     tmp->triangle[tmp->num_triangle].i[1] = start+1;
580     tmp->triangle[tmp->num_triangle++].i[2] = start+4;
581       
582     tmp->triangle[tmp->num_triangle].i[0] = start+5;
583     tmp->triangle[tmp->num_triangle].i[1] = start+4;
584     tmp->triangle[tmp->num_triangle++].i[2] = start+2;
585       
586     tmp->triangle[tmp->num_triangle].i[0] = start+3;
587     tmp->triangle[tmp->num_triangle].i[1] = start+4;
588     tmp->triangle[tmp->num_triangle++].i[2] = start+5;
589   }
590   
591   /* compress object eliminating double vertexes 
592      (welcome to the not so smart section)
593   */
594   /* copy original triangle list */
595   for (t=0; t<tmp->num_triangle; ++t) {
596     ret->triangle[t] = tmp->triangle[t];
597   }
598   ret->num_triangle = tmp->num_triangle;
599   
600   /* copy unique vertexes and correct triangle list */
601   for (v=0; v<tmp->num_vertex; ++v) {
602     /* create list of vertexes that are the same */
603     index_cnt = 0;
604     for (i=0; i<tmp->num_vertex; ++i) {
605       /* check if i and v are the same
606          first in the list is the smallest index
607       */
608       if (vector_compare( &tmp->vertex[v], &tmp->vertex[i] )) {
609         index_list[index_cnt++] = i;
610       }
611     }
612     
613     /* check if vertex unknown so far */
614     index_found = 0;
615     for (i=0; i<ret->num_vertex; ++i) {
616       if (vector_compare( &ret->vertex[i],
617           &tmp->vertex[index_list[0]] )) {
618         index_found = 1;
619         break;
620       }
621     }
622     
623     if (!index_found) {
624       ret->vertex[ret->num_vertex] = tmp->vertex[index_list[0]];
625       
626       /* correct triangles 
627          (we add an offset to the index, so we can tell them apart)
628       */
629       for (t=0; t<ret->num_triangle; ++t) {
630         for (i=0; i<index_cnt; ++i) {
631           if (ret->triangle[t].i[0] == index_list[i]) {
632             ret->triangle[t].i[0] = ret->num_vertex+INDEX_OFFSET;
633           }
634           if (ret->triangle[t].i[1] == index_list[i]) {
635             ret->triangle[t].i[1] = ret->num_vertex+INDEX_OFFSET;
636           }
637           if (ret->triangle[t].i[2] == index_list[i]) {
638             ret->triangle[t].i[2] = ret->num_vertex+INDEX_OFFSET;
639           }
640         }
641       }
642       ret->num_vertex++;
643     }
644   }
645   
646   free_Object( tmp );
647   
648   /* correct index offset */
649   for (t=0; t<ret->num_triangle; ++t) {
650     ret->triangle[t].i[0] -= INDEX_OFFSET;
651     ret->triangle[t].i[1] -= INDEX_OFFSET;
652     ret->triangle[t].i[2] -= INDEX_OFFSET;
653   }
654   
655   /* normalize vertexes */
656   for (v=0; v<ret->num_vertex; ++v) {
657     vector_normalize( &ret->vertex[v] );
658   }
659 #ifdef PRINT_STAT
660   fprintf( stderr, "out v=%d t=%d\n", 
661            ret->num_vertex, ret->num_triangle );
662 #endif
663   /* shrink the arrays by cloning */
664   c_ret = clone_Object( ret );
665   free_Object( ret );
666   
667   return c_ret;
668 }
669
670 static int render( State *st )
671 {
672 #ifdef PRINT_STAT
673   struct timeval tv1, tv2;
674   int usec;
675 #endif
676   GLfloat LightAmbient[]= { 0.1f, 0.1f, 0.1f, 1.0f };
677   GLfloat LightPosition[]= { -20.0f, -10.0f, -100.0f, 0.0f };
678   int b;
679   int num_paint = 0;
680   
681   if (0 == st->food) return 0;
682 #ifdef PRINT_STAT
683   gettimeofday( &tv1, NULL );
684 #endif
685   /* life goes on... */
686   tick( st );
687 #ifdef PRINT_STAT
688   gettimeofday( &tv2, NULL );
689   usec = (tv2.tv_sec-tv1.tv_sec)*1000000+(tv2.tv_usec-tv1.tv_usec);
690   fprintf( stderr, "tick %d\n", usec );
691   gettimeofday( &tv1, NULL );
692 #endif
693   glClearColor( 0, 0, 0, 0 );
694   
695   glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
696   glDepthFunc(GL_LESS);
697   glEnable(GL_DEPTH_TEST);
698   glLightfv( GL_LIGHT0, GL_AMBIENT, LightAmbient );
699   glLightfv( GL_LIGHT0, GL_DIFFUSE, st->color );
700   glLightfv( GL_LIGHT0, GL_POSITION, LightPosition );
701   
702   /* prepare lighting vs. wireframe */
703   if (!st->wire) {
704     glEnable( GL_LIGHT0 );
705     glEnable( GL_LIGHTING );
706     glEnable( GL_NORMALIZE );
707     glPolygonMode( GL_FRONT, GL_FILL );
708   } else {
709     glPolygonMode( GL_FRONT, GL_LINE );
710   }
711   
712   /* draw the dead cells if choosen */
713   if (st->keep_old_cells) {
714     for (b=0; b<st->num_cells; ++b) {
715       if (st->cell[b].energy <= 0) {
716         num_paint++;
717         glPushMatrix();
718         glTranslatef( st->cell[b].x, st->cell[b].y, 0.0 );
719         glRotatef( st->cell[b].rotation, 0.0, 0.0, 1.0 );
720         glScalef( st->cell[b].radius, st->cell[b].radius, st->cell[b].radius );
721         draw_cell( st, 9 );
722         glPopMatrix();
723       }
724     }
725   }
726   
727   /* draw the living cells */
728   for (b=0; b<st->num_cells; ++b) {
729      if (st->cell[b].energy >0) { 
730       double fac = (double)st->cell[b].energy / 50.0; 
731       int shape;
732       if (fac < 0.0) fac = 0.0;
733       if (fac > 1.0) fac = 1.0;
734       
735       shape = (int)(9.0*fac);
736       num_paint++;
737       /*glColor3f( fac, fac, fac );*/
738       
739       glPushMatrix();
740       glTranslatef( st->cell[b].x, st->cell[b].y, 0.0 );
741       glRotatef( st->cell[b].rotation, 0.0, 0.0, 1.0 );
742       glScalef( st->cell[b].radius, st->cell[b].radius, st->cell[b].radius );
743       draw_cell( st, 9-shape );
744       glPopMatrix();
745     }
746   }
747   
748   /* draw cell nuclei */
749   if (!st->wire)
750   {
751     glDisable( GL_LIGHT0 );
752     glDisable( GL_LIGHTING );
753     
754     glEnable( GL_BLEND );
755     glDisable( GL_DEPTH_TEST );
756     glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );    
757     glEnable( GL_TEXTURE_2D );
758     glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
759     glBindTexture( GL_TEXTURE_2D, st->texture_name );
760     
761     for (b=0; b<st->num_cells; ++b) {
762       if (st->cell[b].energy>0 || st->keep_old_cells) {
763         glPushMatrix();
764         glTranslatef( st->cell[b].x, st->cell[b].y, 0.0 );
765         glScalef( st->cell[b].radius, st->cell[b].radius, st->cell[b].radius );
766         draw_nucleus( st );
767         glPopMatrix();
768       }
769     }
770     
771     glDisable( GL_TEXTURE_2D );
772     glDisable( GL_BLEND );
773   }
774   
775 #ifdef PRINT_STAT
776   gettimeofday( &tv2, NULL );
777   usec = (tv2.tv_sec-tv1.tv_sec)*1000000+(tv2.tv_usec-tv1.tv_usec);
778   fprintf( stderr, "OpenGL %d\n", usec );
779 #endif
780   return num_paint * st->cell_polys;
781 }
782
783 /* this creates the initial subdivided half-dodecaedron */
784 static Object *create_sphere( State *st, int divisions )
785 {
786   int num_vertex = 9;
787   int num_triangle = 10;  
788   int i, v, t;
789   double a, aStep = (double)M_PI / 3.0;
790   double e;
791   int vi[30] = { 0, 7, 1, 1, 7, 2, 2, 8, 3, 3, 8, 4, 4, 6, 5,
792                  5, 6, 0, 0, 6, 7, 2, 7, 8, 4, 8, 6, 6, 8, 7 };
793   Object *obj = (Object *)malloc( sizeof( Object ) );
794   
795   obj->vertex = (Vector *)malloc( num_vertex*sizeof( Vector ) );
796   obj->triangle = 
797       (Triangle *)malloc( num_triangle*sizeof( Triangle ) );
798   obj->num_vertex = num_vertex;
799   obj->num_triangle = num_triangle;
800                 
801   /* create vertexes for dodecaedron */
802   a = 0.0;
803   for (v=0; v<6; ++v) {
804     obj->vertex[v].x = sin( a );
805     obj->vertex[v].y = -cos( a );
806     obj->vertex[v].z = 0.0;
807   
808     a += aStep;
809   }
810
811   a = -60.0/180.0*(double)M_PI;
812   e = 58.2825/180.0 * (double)M_PI;
813   for (;v<9; ++v) {
814     obj->vertex[v].x = sin( a )*cos( e );
815     obj->vertex[v].y = -cos( a )*cos( e );
816     obj->vertex[v].z = -sin( e );
817   
818     a += 2.0*aStep;
819   }
820
821   /* create triangles */
822   for (t=0; t<obj->num_triangle; ++t) {
823     obj->triangle[t].i[0] = vi[3*t];
824     obj->triangle[t].i[1] = vi[3*t+1];
825     obj->triangle[t].i[2] = vi[3*t+2];
826   }
827
828   /* subdivide as specified */
829   for (i=0; i<divisions; ++i) {
830     Object *newObj = subdivide( obj );
831     free_Object( obj );
832     obj = newObj;
833   }
834   
835   st->cell_polys = obj->num_triangle;
836   
837   return obj;
838 }
839
840 static int create_list( State *st, double fac )
841 {
842   int v;
843   Object *obj = clone_Object( st->sphere );
844   ObjectSmooth *smooth;
845 #ifdef USE_VERTEX_ARRAY
846   VertexArray *vertex_array;
847 #endif
848   int list = glGenLists(1);  
849   
850   /* apply wrinckle factor */
851   for (v=0; v<obj->num_vertex; ++v) {
852     vector_mul( &obj->vertex[v], 1.0+fac*st->disturbance[v] );
853   }
854   
855   /* compute normals */
856   smooth = create_ObjectSmooth( obj );
857   free_Object( obj );
858   
859   /* Create display list */
860   glNewList( list, GL_COMPILE );
861 #ifdef USE_VERTEX_ARRAY
862   vertex_array = array_from_ObjectSmooth( smooth );
863   glEnableClientState( GL_VERTEX_ARRAY );
864   glEnableClientState( GL_NORMAL_ARRAY );
865   glVertexPointer( 3, GL_FLOAT, 0, vertex_array->vertex );
866   glNormalPointer( GL_FLOAT, 0, vertex_array->normal );
867   glDrawElements( GL_TRIANGLES, vertex_array->num_index, 
868                   GL_UNSIGNED_INT, vertex_array->index );
869   free( vertex_array );
870 #else
871   glBegin( GL_TRIANGLES );
872   
873   for (t=0; t<smooth->num_triangle; ++t) {
874     for (i=0; i<3; ++i) {
875       glNormal3f( smooth->normal[smooth->triangle[t].i[i]].x, 
876                   smooth->normal[smooth->triangle[t].i[i]].y, 
877                   smooth->normal[smooth->triangle[t].i[i]].z );
878       glVertex3f( smooth->vertex[smooth->triangle[t].i[i]].x, 
879                   smooth->vertex[smooth->triangle[t].i[i]].y, 
880                   smooth->vertex[smooth->triangle[t].i[i]].z );
881     }
882   }    
883   
884   glEnd();
885 #endif
886   glEndList();
887   
888   free_ObjectSmooth( smooth );
889   
890   return list;
891 }
892
893 static void draw_cell( State *st, int shape )
894 {
895   if (-1 == st->cell_list[shape]) {
896     st->cell_list[shape] = create_list( st, (double)shape/10.0 );
897   }
898   
899   glCallList( st->cell_list[shape] );
900 }
901
902 static void create_nucleus_texture( State *st )
903 {
904   int x, y;
905   int w2 = TEX_SIZE/2;
906   float s = w2*w2/4.0;
907   
908   st->texture = (GLubyte *) malloc( 4*TEX_SIZE*TEX_SIZE );
909   
910   for (y=0; y<TEX_SIZE; ++y) {
911     for (x=0; x<TEX_SIZE; ++x) {
912       float r2 = ((x-w2)*(x-w2)+(y-w2)*(y-w2));
913       float v = 120.0 * expf( -(r2) / s );
914       st->texture[4*(x+y*TEX_SIZE)]   = (GLubyte)0;
915       st->texture[4*(x+y*TEX_SIZE)+1] = (GLubyte)0;
916       st->texture[4*(x+y*TEX_SIZE)+2] = (GLubyte)0;
917       st->texture[4*(x+y*TEX_SIZE)+3] = (GLubyte)v;
918     }
919   }
920   
921   glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
922   glGenTextures( 1, &st->texture_name );
923   glBindTexture( GL_TEXTURE_2D, st->texture_name );
924   
925   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
926   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
927   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
928   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
929   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0,
930                 GL_RGBA, GL_UNSIGNED_BYTE, st->texture );
931 }
932
933 static void draw_nucleus( State *st )
934 {
935   if (-1 == st->nucleus_list) {
936     float z = -1.2f;
937     float r=1.0/2.0f;
938     st->nucleus_list = glGenLists( 1 );
939     glNewList( st->nucleus_list, GL_COMPILE );
940     glBegin( GL_QUADS );
941     glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -r, -r, z );
942     glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -r, r, z );
943     glTexCoord2f( 1.0f, 1.0f ); glVertex3f( r, r, z );
944     glTexCoord2f( 1.0f, 0.0f ); glVertex3f( r, -r, z );    
945     glEnd();
946     glEndList();
947   }
948   
949   glCallList( st->nucleus_list );
950 }
951
952 static void create_cells( State *st )
953 {
954   int i, foodcnt;
955   int w = st->width-400;
956   int h = st->height-400;
957   
958   st->color[0] = 0.5 + random_max( 1000 ) * 0.0005;
959   st->color[1] = 0.5 + random_max( 1000 ) * 0.0005;
960   st->color[2] = 0.5 + random_max( 1000 ) * 0.0005;
961   st->color[3] = 1.0f;
962   
963   /* allocate if startup */
964   if (!st->cell) {
965     st->cell = (Cell *) malloc( st->max_cells * sizeof(Cell));
966   }
967   
968   /* fill the screen with random food for our little critters */
969   foodcnt = (st->width*st->height)/16;  
970   for (i=0; i<foodcnt; ++i) {
971     st->food[i] = random_interval( st->minfood, st->maxfood );
972   }
973     
974   /* create the requested seed-cells */
975   st->num_cells = st->num_seeds;
976
977   for (i=0; i<st->num_cells; ++i) {
978     st->cell[i].x        = 200 + random_max( w );
979     st->cell[i].y        = 200 + random_max( h );
980     st->cell[i].vx       = 0.0;
981     st->cell[i].vy       = 0.0;
982     st->cell[i].age      = random_max( 0x0f );
983     st->cell[i].min_dist = 500.0;
984     st->cell[i].energy   = random_interval( 5, 5+0x3f );
985     st->cell[i].rotation = ((double)random()/(double)RAND_MAX)*360.0;
986     st->cell[i].radius   = st->radius;
987     st->cell[i].growth   = 1.0;
988   }
989 }
990
991 /* all this is rather expensive :( */
992 static void tick( State *st )
993 {
994   int new_num_cells, num_cells=0;
995   int b, j;
996   int x, y, w4=st->width/4, h4=st->height/4, offset;
997   double min_dist;
998   int min_index;
999   int num_living = 0;
1000   const double check_dist = 0.75*st->move_dist;
1001   const double grow_dist = 0.75*st->radius;
1002   
1003   /* find number of cells capable of division 
1004      and count living cells
1005   */
1006   for (b=0; b<st->num_cells; ++b) {
1007     if (st->cell[b].energy > 0) num_living++;
1008     if (can_divide( st, &st->cell[b] )) num_cells++;
1009   }
1010   new_num_cells = st->num_cells + num_cells;
1011   
1012   /* end of simulation ? */
1013   if (0 == num_living || new_num_cells >= st->max_cells) {
1014     if (st->pause_counter > 0) st->pause_counter--;
1015     if (st->pause_counter > 0) return;
1016     create_cells( st );
1017     st->pause_counter = st->pause;
1018   } else if (num_cells) { /* any fertile candidates ? */
1019     for (b=0, j=st->num_cells; b<st->num_cells; ++b) {
1020       if (can_divide( st, &st->cell[b] )) {
1021         st->cell[b].vx      = random_interval( -50, 50 ) * 0.01;
1022         st->cell[b].vy      = random_interval( -50, 50 ) * 0.01;
1023         st->cell[b].age     = random_max( 0x0f );
1024         /* half energy for both plus some bonus for forking */
1025         st->cell[b].energy  = 
1026             st->cell[b].energy/2 + random_max( 0x0f );
1027         /* forking makes me shrink */
1028         st->cell[b].growth  = 0.995;
1029         
1030         /* this one initially goes into the oposite direction */
1031         st->cell[j].vx       = -st->cell[b].vx;
1032         st->cell[j].vy       = -st->cell[b].vy;
1033         /* same center */
1034         st->cell[j].x        = st->cell[b].x;
1035         st->cell[j].y        = st->cell[b].y;
1036         st->cell[j].age      = random_max( 0x0f );
1037         st->cell[j].energy   = (st->cell[b].energy);
1038         st->cell[j].rotation =
1039             ((double)random()/(double)RAND_MAX)*360.0;
1040         st->cell[j].growth   = st->cell[b].growth;
1041         st->cell[j].radius   = st->cell[b].radius;
1042         ++j;
1043       } else {
1044         st->cell[b].vx = 0.0;
1045         st->cell[b].vy = 0.0;
1046       }
1047     }
1048     
1049     st->num_cells = new_num_cells;
1050   }
1051
1052   /* for each find a direction to escape */
1053   if (st->num_cells > 1) {
1054     for (b=0; b<st->num_cells; ++b) {
1055       if (st->cell[b].energy > 0) {
1056         double vx;
1057         double vy;
1058         double len;
1059         
1060         /* grow or shrink */
1061         st->cell[b].radius *= st->cell[b].growth;
1062         /* find closest neighbour */
1063         min_dist = 100000.0;
1064         min_index = 0;
1065         for (j=0; j<st->num_cells; ++j) {
1066           if (j!=b) {
1067             const double dx = st->cell[b].x - st->cell[j].x;
1068             const double dy = st->cell[b].y - st->cell[j].y;
1069             
1070             if (fabs(dx) < check_dist || fabs(dy) < check_dist) {
1071               const double dist = sqrt( dx*dx+dy*dy );
1072               if (dist<min_dist) {
1073                 min_dist = dist;
1074                 min_index = j;
1075               }
1076             }
1077           }
1078         }
1079         /* escape step is away from closest normalized with distance */
1080         vx = st->cell[b].x - st->cell[min_index].x;
1081         vy = st->cell[b].y - st->cell[min_index].y;
1082         len = sqrt( vx*vx + vy*vy );
1083         if (len > 0.0001) {
1084           st->cell[b].vx = vx/len;
1085           st->cell[b].vy = vy/len;
1086         }
1087         st->cell[b].min_dist = len;
1088         /* if not adult (radius too small) */
1089         if (st->cell[b].radius < st->radius) {
1090           /* if too small 60% stop shrinking */
1091           if (st->cell[b].radius < st->radius * 0.6) {
1092             st->cell[b].growth = 1.0;
1093           }
1094           /* at safe distance we start growing again */
1095           if (len > grow_dist) {
1096             if (st->cell[b].energy > 30) {
1097               st->cell[b].growth = 1.005;
1098             } 
1099           }
1100         } else {  /* else keep size */
1101           st->cell[b].growth = 1.0;
1102         }
1103       }
1104     }
1105   } else {
1106     st->cell[0].min_dist = 2*st->move_dist;
1107   }
1108     
1109   /* now move em, snack and burn energy */
1110   for (b=0; b<st->num_cells; ++b) {
1111     /* if still alive */
1112     if (st->cell[b].energy > 0) {
1113       /* agility depends on amount of energy */
1114       double fac = (double)st->cell[b].energy / 50.0;
1115       if (fac < 0.0) fac = 0.0;
1116       if (fac > 1.0) fac = 1.0;
1117       
1118       st->cell[b].x += fac*(2.0 - 
1119           (4.0*(double)random() / (double)RAND_MAX) + 
1120           st->cell[b].vx);
1121       st->cell[b].y += fac*(2.0 - 
1122           (4.0*(double)random() / (double)RAND_MAX) + 
1123           st->cell[b].vy);
1124       
1125       /* get older and burn energy */
1126       if (st->cell[b].energy > 0) {
1127         st->cell[b].age++;
1128         st->cell[b].energy--;
1129       }
1130       
1131       /* have a snack */
1132       x = ((int)st->cell[b].x)/4;
1133       if (x<0) x=0; if (x>=w4) x = w4-1;
1134       y = ((int)st->cell[b].y)/4;
1135       if (y<0) y=0; if (y>=h4) y = h4-1;
1136     
1137       offset = x+y*w4;
1138     
1139       /* don't eat if already satisfied */
1140       if (st->cell[b].energy < 100 &&
1141           st->food[offset] > 0) {
1142         st->food[offset]--;
1143         st->cell[b].energy++;
1144         /* if you are hungry, eat more */
1145         if (st->cell[b].energy < 50 && 
1146             st->food[offset] > 0) {
1147           st->food[offset]--;
1148           st->cell[b].energy++;
1149         }
1150       }
1151     }
1152   }
1153 }
1154
1155 ENTRYPOINT void 
1156 reshape_glcells( ModeInfo *mi, int width, int height )
1157 {
1158   State *st  = &sstate[MI_SCREEN(mi)];
1159   st->height = height;
1160   st->width  = width;
1161   
1162   glViewport (0, 0, (GLint) width, (GLint) height);
1163
1164   glMatrixMode(GL_PROJECTION);
1165   glLoadIdentity();
1166   glOrtho( 0, width, height, 0, 200, 0 );
1167   glMatrixMode(GL_MODELVIEW);
1168   glLoadIdentity();
1169   
1170   if (st->food) free( st->food );
1171   st->food = (int *)malloc( ((width*height)/16)*sizeof(int) );
1172   
1173   create_cells( st );
1174 }
1175
1176 ENTRYPOINT void 
1177 init_glcells( ModeInfo *mi )
1178 {
1179   int i, divisions;
1180   State *st=0;
1181   
1182   if (!sstate) {
1183     sstate = (State *)
1184         calloc( MI_NUM_SCREENS(mi), sizeof(State) );
1185     if (!sstate) {
1186       fprintf( stderr, "%s: out of memory\n", progname );
1187       exit(1);
1188     }
1189   }
1190   st = &sstate[MI_SCREEN(mi)];
1191   
1192   st->glx_context = init_GL(mi);
1193   st->cell = 0;
1194   st->num_cells = 0;
1195   st->wire = MI_IS_WIREFRAME(mi);
1196   
1197   /* get settings */
1198   st->max_cells = s_maxcells;;
1199   if (st->max_cells < 50) st->max_cells = 50;
1200   if (st->max_cells > 10000) st->max_cells = 10000;
1201   
1202   st->pause = s_pause;
1203   if (st->pause < 0) st->pause = 0;
1204   if (st->pause > 400) st->pause = 400;
1205   st->pause_counter = st->pause;
1206   
1207   st->radius = s_radius;
1208   if (st->radius < 5) st->radius = 5;
1209   if (st->radius > 200) st->radius = 200;
1210   
1211   divisions = s_quality;
1212   if (divisions < 0) divisions = 0;
1213   if (divisions > 5) divisions = 5;
1214   
1215   st->num_seeds = s_seeds;
1216   if (st->num_seeds < 1) st->num_seeds = 1;
1217   if (st->num_seeds > 16) st->num_seeds = 16;
1218   
1219   st->minfood = s_minfood;
1220   if (st->minfood < 0) st->minfood = 0;
1221   if (st->minfood > 1000) st->minfood = 1000;
1222   
1223   st->maxfood = s_maxfood;
1224   if (st->maxfood < 0) st->maxfood = 0;
1225   if (st->maxfood > 1000) st->maxfood = 1000;
1226   
1227   if (st->maxfood < st->minfood) st->maxfood = st->minfood+1;
1228   
1229   st->keep_old_cells = s_keepold;
1230   
1231   st->divide_age = s_divideage;
1232   if (st->divide_age < 1) st->divide_age = 1;
1233   if (st->divide_age > 1000) st->divide_age = 1000;
1234      
1235   st->move_dist = s_min_dist;
1236   if (st->move_dist < 1.0) st->move_dist = 1.0;
1237   if (st->move_dist > 3.0) st->move_dist = 3.0;
1238   st->move_dist *= st->radius;
1239   
1240   for (i=0; i<NUM_CELL_SHAPES; ++i) st->cell_list[i] = -1;
1241   st->nucleus_list = -1;
1242   st->food = 0;
1243   
1244   st->sphere = create_sphere( st, divisions );
1245   st->disturbance = 
1246       (double *) malloc( st->sphere->num_vertex*sizeof(double) );
1247   for (i=0; i<st->sphere->num_vertex; ++i) {
1248     st->disturbance[i] = 
1249         0.05-((double)random()/(double)RAND_MAX*0.1);
1250   }
1251   
1252   create_nucleus_texture( st );
1253
1254   reshape_glcells (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1255 }
1256
1257 ENTRYPOINT void
1258 draw_glcells( ModeInfo *mi )
1259 {
1260   State *st = &sstate[MI_SCREEN(mi)];
1261   Display *dpy = MI_DISPLAY(mi);
1262   Window window = MI_WINDOW(mi);
1263   
1264   if (!st->glx_context) return;
1265
1266   glXMakeCurrent( MI_DISPLAY(mi), MI_WINDOW(mi), 
1267                   *(st->glx_context) );
1268   
1269   mi->polygon_count = render( st );
1270   
1271   if (mi->fps_p) do_fps (mi);
1272   
1273   glFinish();
1274   glXSwapBuffers( dpy, window );
1275 }
1276
1277 ENTRYPOINT void 
1278 release_glcells( ModeInfo *mi )
1279 {
1280   int i;
1281   State *st  = &sstate[MI_SCREEN(mi)];
1282   
1283   /* nuke everything before exit */
1284   if (st->sphere) free_Object( st->sphere );
1285   if (st->food)   free( st->food );
1286   for (i=0; i<NUM_CELL_SHAPES; ++i) {
1287     if (st->cell_list[i] != -1) {
1288       glDeleteLists( st->cell_list[i], 1 );
1289     }
1290   }
1291   if (st->cell) free( st->cell );
1292   free( st->disturbance );
1293   glDeleteTextures( 1, &st->texture_name );
1294   free( st->texture );
1295 }
1296
1297 XSCREENSAVER_MODULE( "GLCells", glcells )