1 /* -*- Mode: C; tab-width: 2 -*- */
2 /* glcells --- Cells growing on your screen */
5 * Cells growing on your screen
7 * Copyright (c) 2007 by Matthias Toussaint
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.
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.
21 * 2007: Written by Matthias Toussaint
23 * 0.2 Bugfixes (threading) and code cleanup by Jamie Zawinski
24 * Window scaling bug + performance bug in tick()
27 #include <sys/time.h> /* gettimeofday */
29 #include "xlockmore.h"
32 /**********************************
34 **********************************/
36 #define INDEX_OFFSET 100000
37 #define NUM_CELL_SHAPES 10
39 #define refresh_glcells 0
40 #define release_glcells 0
41 #define glcells_handle_event 0
43 #define DEF_DELAY "20000"
44 #define DEF_MAXCELLS "800"
45 #define DEF_RADIUS "40"
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"
55 #define DEFAULTS "*delay: 30000 \n" \
56 "*showFPS: False \n" \
57 "*wireframe: False \n" \
58 "*suppressRotationAnimation: True\n" \
61 #define countof(x) (sizeof((x))/sizeof((*x)))
63 #ifndef HAVE_JWZGLES /* glDrawElements unimplemented... */
64 # define USE_VERTEX_ARRAY
69 /**********************************
71 **********************************/
73 typedef struct /* a 3-D vector */
75 double x, y, z; /* 3-D coordinates (we don't need w here) */
78 typedef struct /* a triangle (indexes of vertexes in some list) */
80 int i[3]; /* the three indexes for the triangle corners */
91 typedef struct /* an 3-D object without normal vectors */
93 Vector *vertex; /* the vertexes */
94 Triangle *triangle; /* triangle list */
95 int num_vertex; /* number of vertexes */
96 int num_triangle; /* number of triangles */
99 typedef struct /* an 3-D object with smooth normal vectors */
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 */
108 typedef struct /* Cell */
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 */
122 typedef struct /* hacks state */
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 */
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];
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) */
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 */
152 /**********************************
154 **********************************/
156 static State *sstate = NULL;
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" }
171 static int s_maxcells;
174 static int s_quality;
175 static int s_minfood;
176 static int s_maxfood;
177 static int s_divideage;
179 static float s_min_dist;
180 static Bool s_keepold;
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}
195 /**********************************
197 **********************************/
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 * );
220 static void vector_add( Vector *a, Vector *b );
222 static void vector_sub( Vector *a, Vector *b );
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 );
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 * );
252 static void create_nucleus_texture( State *st );
254 ENTRYPOINT ModeSpecOpt glcells_opts = { countof(opts), opts, countof(vars), vars,
258 /**********************************
260 **********************************/
261 /* create random numbers
263 static inline int random_interval( int min, int max )
267 return min+(random()%n);
270 static inline int random_max( int max )
279 static inline void vector_add( Vector *a, Vector *b )
287 static inline void vector_sub( Vector *a, Vector *b )
295 static inline void vector_mul( Vector *a, double v )
303 static inline void vector_clear( Vector *vec )
305 vec->x = vec->y = vec->z = 0;
308 /* return vector length */
309 static inline double vector_length( Vector *vec )
311 return sqrt( vec->x*vec->x + vec->y*vec->y + vec->z*vec->z );
314 /* normalize vector */
315 static inline void vector_normalize( Vector *vec )
317 double len = vector_length( vec );
320 vector_mul( vec, 1.0 / len );
325 static inline void vector_crossprod( Vector *a, Vector *b, Vector *out )
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;
332 /* epsilon compare of two vectors */
333 static inline int vector_compare( Vector *a, Vector *b )
335 const double epsilon = 0.0000001;
338 vector_sub( &delta, b );
339 if (fabs(delta.x) < epsilon &&
340 fabs(delta.y) < epsilon &&
341 fabs(delta.z) < epsilon) {
348 /* check if given cell is capable of dividing
349 needs space, must be old enough, grown up and healthy
351 static inline int can_divide( State *st, Cell *cell )
353 if (cell->min_dist > st->move_dist &&
354 cell->age >= st->divide_age &&
355 cell->radius > 0.99 * st->radius &&
363 /**********************************
365 **********************************/
367 /* compute normal vector of given
368 triangle spanned by the points a, b, c
370 static void triangle_normal( Vector *a, Vector *b, Vector *c, Vector *out )
375 vector_sub( &v1, b );
376 vector_sub( &v2, c );
377 vector_crossprod( &v1, &v2, out );
381 static void free_Object( Object *obj )
384 free( obj->triangle );
388 static void free_ObjectSmooth( ObjectSmooth *obj )
391 free( obj->triangle );
396 /* scale the given Object */
398 static Object *scale_Object( Object *obj, double scale )
402 for (v=0; v<obj->num_vertex; ++v) {
403 vector_mul( &obj->vertex[v], scale );
410 /* create a copy of the given Object */
411 static Object *clone_Object( Object *obj )
414 Object *ret = (Object *) malloc( sizeof( Object ) );
417 (Vector *) malloc( obj->num_vertex*sizeof(Vector) );
419 (Triangle *) malloc( obj->num_triangle*sizeof(Triangle) );
420 ret->num_vertex = obj->num_vertex;
421 ret->num_triangle = obj->num_triangle;
423 memcpy( ret->vertex, obj->vertex,
424 obj->num_vertex*sizeof(Vector) );
425 memcpy( ret->triangle, obj->triangle,
426 obj->num_triangle*sizeof(Triangle) );
431 #ifdef USE_VERTEX_ARRAY
432 static VertexArray *array_from_ObjectSmooth( ObjectSmooth *obj )
435 VertexArray *array = (VertexArray *) malloc( sizeof( VertexArray ) );
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;
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;
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];
459 #endif /* USE_VERTEX_ARRAY */
462 /* create a smoothed version of the given Object
463 by computing average normal vectors for the vertexes
465 static ObjectSmooth *create_ObjectSmooth( Object *obj )
469 (Vector *) malloc( obj->num_triangle*sizeof(Vector) );
471 (ObjectSmooth *) malloc( sizeof( ObjectSmooth ) );
473 /* fill in vertexes and triangles */
474 ret->num_vertex = obj->num_vertex;
475 ret->num_triangle = obj->num_triangle;
477 (Vector *) malloc( obj->num_vertex * sizeof( Vector ) );
479 (Vector *) malloc( obj->num_vertex * sizeof( Vector ) );
481 (Triangle *) malloc( obj->num_triangle * sizeof( Triangle ) );
483 for (v=0; v<obj->num_vertex; ++v) {
484 ret->vertex[v] = obj->vertex[v];
487 for (t=0; t<obj->num_triangle; ++t) {
488 ret->triangle[t] = obj->triangle[t];
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]],
499 /* create normals (vertex) by averaging triangle
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] );
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.
515 if (fabs(ret->vertex[v].z) < 0.0001) {
516 ret->normal[v].z = 0.0;
519 vector_normalize( &ret->normal[v] );
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
531 static Object *subdivide( Object *obj )
533 /* create for worst case (which I dont't know) */
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) );
542 (Vector *)malloc( 100*obj->num_vertex*sizeof( Vector ) );
544 (Triangle *)malloc( 4*obj->num_triangle*sizeof( Triangle ) );
546 tmp->num_triangle = 0;
548 (Vector *)malloc( 100*obj->num_vertex*sizeof( Vector ) );
550 (Triangle *)malloc( 4*obj->num_triangle*sizeof( Triangle ) );
552 ret->num_triangle = 0;
554 fprintf( stderr, "in v=%d t=%d\n",
555 obj->num_vertex, obj->num_triangle );
557 /* for each triangle create 3 new vertexes and the 4
558 corresponding triangles
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]];
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 );
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 );
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 );
586 /* create triangles */
587 start = tmp->num_vertex-6;
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;
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;
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;
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;
606 /* compress object eliminating double vertexes
607 (welcome to the not so smart section)
609 /* copy original triangle list */
610 for (t=0; t<tmp->num_triangle; ++t) {
611 ret->triangle[t] = tmp->triangle[t];
613 ret->num_triangle = tmp->num_triangle;
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 */
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
623 if (vector_compare( &tmp->vertex[v], &tmp->vertex[i] )) {
624 index_list[index_cnt++] = i;
628 /* check if vertex unknown so far */
630 for (i=0; i<ret->num_vertex; ++i) {
631 if (vector_compare( &ret->vertex[i],
632 &tmp->vertex[index_list[0]] )) {
639 ret->vertex[ret->num_vertex] = tmp->vertex[index_list[0]];
642 (we add an offset to the index, so we can tell them apart)
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;
649 if (ret->triangle[t].i[1] == index_list[i]) {
650 ret->triangle[t].i[1] = ret->num_vertex+INDEX_OFFSET;
652 if (ret->triangle[t].i[2] == index_list[i]) {
653 ret->triangle[t].i[2] = ret->num_vertex+INDEX_OFFSET;
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;
670 /* normalize vertexes */
671 for (v=0; v<ret->num_vertex; ++v) {
672 vector_normalize( &ret->vertex[v] );
675 fprintf( stderr, "out v=%d t=%d\n",
676 ret->num_vertex, ret->num_triangle );
678 /* shrink the arrays by cloning */
679 c_ret = clone_Object( ret );
685 static int render( State *st )
688 struct timeval tv1, tv2;
691 GLfloat LightAmbient[]= { 0.1f, 0.1f, 0.1f, 1.0f };
692 GLfloat LightPosition[]= { -20.0f, -10.0f, -100.0f, 0.0f };
696 if (0 == st->food) return 0;
698 gettimeofday( &tv1, NULL );
700 /* life goes on... */
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 );
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 );
716 /* prepare lighting vs. wireframe */
718 glEnable( GL_LIGHT0 );
719 glEnable( GL_LIGHTING );
720 glEnable( GL_NORMALIZE );
721 glPolygonMode( GL_FRONT, GL_FILL );
723 # ifndef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
724 glPolygonMode( GL_FRONT, GL_LINE );
730 glDisable(GL_DEPTH_TEST);
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);
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) {
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 );
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;
761 if (fac < 0.0) fac = 0.0;
762 if (fac > 1.0) fac = 1.0;
764 shape = (int)(9.0*fac);
766 /*glColor3f( fac, fac, fac );*/
772 glVertex3f(st->cell[b].x, st->cell[b].y, 0);
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 );
786 /* draw cell nuclei */
789 glDisable( GL_LIGHT0 );
790 glDisable( GL_LIGHTING );
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 );
799 for (b=0; b<st->num_cells; ++b) {
800 if (st->cell[b].energy>0 || st->keep_old_cells) {
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 );
809 glDisable( GL_TEXTURE_2D );
810 glDisable( GL_BLEND );
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 );
818 return num_paint * st->cell_polys;
821 /* this creates the initial subdivided half-dodecaedron */
822 static Object *create_sphere( State *st, int divisions )
825 int num_triangle = 10;
827 double a, aStep = (double)M_PI / 3.0;
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 ) );
833 obj->vertex = (Vector *)malloc( num_vertex*sizeof( Vector ) );
835 (Triangle *)malloc( num_triangle*sizeof( Triangle ) );
836 obj->num_vertex = num_vertex;
837 obj->num_triangle = num_triangle;
839 /* create vertexes for dodecaedron */
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;
849 a = -60.0/180.0*(double)M_PI;
850 e = 58.2825/180.0 * (double)M_PI;
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 );
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];
866 /* subdivide as specified */
867 for (i=0; i<divisions; ++i) {
868 Object *newObj = subdivide( obj );
873 st->cell_polys = obj->num_triangle;
878 static int create_list( State *st, double fac )
881 Object *obj = clone_Object( st->sphere );
882 ObjectSmooth *smooth;
883 #ifdef USE_VERTEX_ARRAY
884 VertexArray *vertex_array;
888 int list = glGenLists(1);
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] );
895 /* compute normals */
896 smooth = create_ObjectSmooth( obj );
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 );
911 glBegin( GL_TRIANGLES );
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 );
928 free_ObjectSmooth( smooth );
933 static void draw_cell( State *st, int shape )
935 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
937 glDisable(GL_DEPTH_TEST);
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);
947 glVertex3f (-1, -1, 0); glVertex3f (1, 1, 0);
948 glVertex3f (-1, 1, 0); glVertex3f (1, -1, 0);
956 if (-1 == st->cell_list[shape]) {
957 st->cell_list[shape] = create_list( st, (double)shape/10.0 );
960 glCallList( st->cell_list[shape] );
963 static void create_nucleus_texture( State *st )
969 st->texture = (GLubyte *) malloc( 4*TEX_SIZE*TEX_SIZE );
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;
982 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
983 glGenTextures( 1, &st->texture_name );
984 glBindTexture( GL_TEXTURE_2D, st->texture_name );
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 );
994 static void draw_nucleus( State *st )
996 if (-1 == st->nucleus_list) {
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 );
1010 glCallList( st->nucleus_list );
1013 static void create_cells( State *st )
1015 int border = (int)(200.0 * st->screen_scale);
1017 int w = st->width-2*border;
1018 int h = st->height-2*border;
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;
1025 /* allocate if startup */
1027 st->cell = (Cell *) malloc( st->max_cells * sizeof(Cell));
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 );
1036 /* create the requested seed-cells */
1037 st->num_cells = st->num_seeds;
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;
1053 /* all this is rather expensive :( */
1054 static void tick( State *st )
1056 int new_num_cells, num_cells=0;
1058 int x, y, w4=st->width/4, h4=st->height/4, offset;
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;
1066 /* find number of cells capable of division
1067 and count living cells
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++;
1073 new_num_cells = st->num_cells + num_cells;
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;
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;
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;
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;
1107 st->cell[b].vx = 0.0;
1108 st->cell[b].vy = 0.0;
1112 st->num_cells = new_num_cells;
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) {
1123 /* grow or shrink */
1124 st->cell[b].radius *= st->cell[b].growth;
1125 /* find closest neighbour */
1126 min_dist = 100000.0;
1128 for (j=0; j<st->num_cells; ++j) {
1130 const double dx = st->cell[b].x - st->cell[j].x;
1131 const double dy = st->cell[b].y - st->cell[j].y;
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) {
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 );
1148 st->cell[b].vx = vx/len;
1149 st->cell[b].vy = vy/len;
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;
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;
1164 } else { /* else keep size */
1165 st->cell[b].growth = 1.0;
1170 st->cell[0].min_dist = 2*st->move_dist;
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;
1182 st->cell[b].x += fac*(2.0 -
1183 (4.0*(double)random() / (double)RAND_MAX) +
1185 st->cell[b].y += fac*(2.0 -
1186 (4.0*(double)random() / (double)RAND_MAX) +
1189 /* get older and burn energy */
1190 if (st->cell[b].energy > 0) {
1192 st->cell[b].energy--;
1196 x = ((int)st->cell[b].x)/4;
1198 if (x>=w4) x = w4-1;
1199 y = ((int)st->cell[b].y)/4;
1201 if (y>=h4) y = h4-1;
1205 /* don't eat if already satisfied */
1206 if (st->cell[b].energy < 100 &&
1207 st->food[offset] > 0) {
1209 st->cell[b].energy++;
1210 /* if you are hungry, eat more */
1211 if (st->cell[b].energy < 50 &&
1212 st->food[offset] > 0) {
1214 st->cell[b].energy++;
1222 reshape_glcells( ModeInfo *mi, int width, int height )
1224 State *st = &sstate[MI_SCREEN(mi)];
1226 int rot = current_device_rotation();
1228 st->height = height;
1231 st->screen_scale = (double)(width < height ? width : height) / 1600.0;
1233 st->screen_scale = (double)width / 1600.0;
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;
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;
1246 glViewport (0, 0, (GLint) width, (GLint) height);
1248 glMatrixMode(GL_PROJECTION);
1250 glOrtho( 0, width, height, 0, 200, 0 );
1252 glRotatef (rot, 0, 0, 1);
1254 glMatrixMode(GL_MODELVIEW);
1256 if (st->food) free( st->food );
1257 st->food = (int *)malloc( ((width*height)/16)*sizeof(int) );
1258 /* create_cells( st );*/
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);
1270 static void free_glcells( ModeInfo *mi );
1273 init_glcells( ModeInfo *mi )
1278 MI_INIT(mi, sstate, free_glcells);
1279 st = &sstate[MI_SCREEN(mi)];
1281 st->glx_context = init_GL(mi);
1284 st->wire = MI_IS_WIREFRAME(mi);
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;
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;
1296 st->radius = s_radius;
1297 if (st->radius < 5) st->radius = 5;
1298 if (st->radius > 200) st->radius = 200;
1300 divisions = s_quality;
1301 if (divisions < 0) divisions = 0;
1302 if (divisions > 5) divisions = 5;
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;
1308 st->minfood = s_minfood;
1309 if (st->minfood < 0) st->minfood = 0;
1310 if (st->minfood > 1000) st->minfood = 1000;
1312 st->maxfood = s_maxfood;
1313 if (st->maxfood < 0) st->maxfood = 0;
1314 if (st->maxfood > 1000) st->maxfood = 1000;
1316 if (st->maxfood < st->minfood) st->maxfood = st->minfood+1;
1318 st->keep_old_cells = s_keepold;
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;
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;
1329 for (i=0; i<NUM_CELL_SHAPES; ++i) st->cell_list[i] = -1;
1330 st->nucleus_list = -1;
1333 st->sphere = create_sphere( st, divisions );
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);
1341 create_nucleus_texture( st );
1343 reshape_glcells (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1347 draw_glcells( ModeInfo *mi )
1349 State *st = &sstate[MI_SCREEN(mi)];
1350 Display *dpy = MI_DISPLAY(mi);
1351 Window window = MI_WINDOW(mi);
1353 if (!st->glx_context) return;
1355 glXMakeCurrent( MI_DISPLAY(mi), MI_WINDOW(mi),
1356 *(st->glx_context) );
1358 mi->polygon_count = render( st );
1360 if (mi->fps_p) do_fps (mi);
1363 glXSwapBuffers( dpy, window );
1367 free_glcells( ModeInfo *mi )
1370 State *st = &sstate[MI_SCREEN(mi)];
1372 if (st->glx_context) {
1373 glXMakeCurrent( MI_DISPLAY(mi), MI_WINDOW(mi),
1374 *(st->glx_context) );
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 );
1384 if (st->cell) free( st->cell );
1385 free( st->disturbance );
1386 glDeleteTextures( 1, &st->texture_name );
1387 free( st->texture );
1391 XSCREENSAVER_MODULE( "GLCells", glcells )