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