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