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