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