From d3a98cf6cbc3bd0b9efc570f58e8812c03931c18 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 16 Oct 2018 10:08:48 +0200 Subject: Original 5.40 --- hacks/glx/glcells.c | 1388 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1388 insertions(+) create mode 100644 hacks/glx/glcells.c (limited to 'hacks/glx/glcells.c') diff --git a/hacks/glx/glcells.c b/hacks/glx/glcells.c new file mode 100644 index 0000000..adf9866 --- /dev/null +++ b/hacks/glx/glcells.c @@ -0,0 +1,1388 @@ +/* -*- Mode: C; tab-width: 2 -*- */ +/* glcells --- Cells growing on your screen */ + +/*- + * Cells growing on your screen + * + * Copyright (c) 2007 by Matthias Toussaint + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + * + * 2007: Written by Matthias Toussaint + * 0.1 Initial version + * 0.2 Bugfixes (threading) and code cleanup by Jamie Zawinski + * Window scaling bug + performance bug in tick() + */ + +#include /* gettimeofday */ + +#include "xlockmore.h" +#include + +/********************************** + DEFINES + **********************************/ + +#define INDEX_OFFSET 100000 +#define NUM_CELL_SHAPES 10 + +#define release_glcells 0 +#define glcells_handle_event xlockmore_no_events + +#define DEF_DELAY "20000" +#define DEF_MAXCELLS "800" +#define DEF_RADIUS "40" +#define DEF_SEEDS "1" +#define DEF_QUALITY "3" +#define DEF_KEEPOLD "False" +#define DEF_MINFOOD "5" +#define DEF_MAXFOOD "20" +#define DEF_DIVIDEAGE "20" +#define DEF_MINDIST "1.4" +#define DEF_PAUSE "50" + +#define DEFAULTS "*delay: 30000 \n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ + "*suppressRotationAnimation: True\n" \ + +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#ifndef HAVE_JWZGLES /* glDrawElements unimplemented... */ +# define USE_VERTEX_ARRAY +#endif + +#define TEX_SIZE 64 + +/********************************** + TYPEDEFS + **********************************/ + +typedef struct /* a 3-D vector */ +{ + double x, y, z; /* 3-D coordinates (we don't need w here) */ +} Vector; + +typedef struct /* a triangle (indexes of vertexes in some list) */ +{ + int i[3]; /* the three indexes for the triangle corners */ +} Triangle; + +typedef struct +{ + float *vertex; + float *normal; + unsigned *index; + int num_index; +} VertexArray; + +typedef struct /* an 3-D object without normal vectors */ +{ + Vector *vertex; /* the vertexes */ + Triangle *triangle; /* triangle list */ + int num_vertex; /* number of vertexes */ + int num_triangle; /* number of triangles */ +} Object; + +typedef struct /* an 3-D object with smooth normal vectors */ +{ + Vector *vertex; /* the vertexes */ + Vector *normal; /* the vertex normal vectors */ + Triangle *triangle; /* triangle list */ + int num_vertex; /* number of vertexes */ + int num_triangle; /* number of triangles */ +} ObjectSmooth; + +typedef struct /* Cell */ +{ + double x, y; /* position */ + double vx, vy; /* movement vector */ + int age; /* cells age */ + double min_dist; /* minimum distance to other cells */ + int energy; /* health */ + double rotation; /* random rot, so they don't look all the same */ + double radius; /* current size of cell */ + double growth; /* current growth rate. might be <1.0 while dividing, + >1.0 when finished dividing and food is available + and 1.0 when grown up */ +} Cell; + +typedef struct /* hacks state */ +{ + GLXContext *glx_context; + int width, height; /* current size of viewport */ + double screen_scale; /* we scale content with window size */ + int num_cells; /* current number of cell in list */ + Cell *cell; /* array of cells */ + int cell_polys; + GLfloat color[4]; /* current cell color */ + double radius; /* cell radius */ + int move_dist; /* min distance from neighbours for forking */ + int max_cells; /* maximum number of cells */ + int num_seeds; /* number of initial seeds */ + int keep_old_cells; /* draw dead cells? */ + int divide_age; /* min age for division */ + /* display lists for the cell stages */ + int cell_list[NUM_CELL_SHAPES]; + int nucleus_list; + int minfood; /* minimum amount of food per area unit */ + int maxfood; /* maximum amount of food per area unit */ + int pause; /* pause at end (all cells dead) */ + int pause_counter; + int wire; /* draw wireframe? */ + Object *sphere; /* the raw undisturbed sphere */ + double *disturbance; /* disturbance values for the vertexes */ + int *food; /* our petri dish (e.g. screen) */ + GLubyte *texture; /* texture data for nucleus */ + GLuint texture_name; /* texture name for binding */ +} State; + +/********************************** + STATIC STUFF + **********************************/ + +static State *sstate = NULL; + +static XrmOptionDescRec opts[] = { + { "-maxcells", ".maxcells", XrmoptionSepArg, 0 }, + { "-radius", ".radius", XrmoptionSepArg, 0 }, + { "-seeds", ".seeds", XrmoptionSepArg, 0 }, + { "-quality", ".quality", XrmoptionSepArg, 0 }, + { "-minfood", ".minfood", XrmoptionSepArg, 0 }, + { "-maxfood", ".maxfood", XrmoptionSepArg, 0 }, + { "-divideage", ".divideage", XrmoptionSepArg, 0 }, + { "-mindist", ".mindist", XrmoptionSepArg, 0 }, + { "-pause", ".pause", XrmoptionSepArg, 0 }, + { "-keepold", ".keepold", XrmoptionNoArg, "True" } +}; + +static int s_maxcells; +static int s_radius; +static int s_seeds; +static int s_quality; +static int s_minfood; +static int s_maxfood; +static int s_divideage; +static int s_pause; +static float s_min_dist; +static Bool s_keepold; + +static argtype vars[] = { + {&s_maxcells, "maxcells", "Max Cells", DEF_MAXCELLS, t_Int}, + {&s_radius, "radius", "Radius", DEF_RADIUS, t_Int}, + {&s_seeds, "seeds", "Seeds", DEF_SEEDS, t_Int}, + {&s_quality, "quality", "Quality", DEF_QUALITY, t_Int}, + {&s_minfood, "minfood", "Min Food", DEF_MINFOOD, t_Int}, + {&s_maxfood, "maxfood", "Max Food", DEF_MAXFOOD, t_Int}, + {&s_pause, "pause", "Pause at end", DEF_PAUSE, t_Int}, + {&s_divideage, "divideage", "Age for duplication (Ticks)", DEF_DIVIDEAGE, t_Int}, + {&s_min_dist, "mindist", "Minimum preferred distance to other cells", DEF_MINDIST, t_Float}, + {&s_keepold, "keepold", "Keep old cells", DEF_KEEPOLD, t_Bool} +}; + +/********************************** + PROTOTYPES + **********************************/ + +/* render scene */ +static int render( State *st ); +/* create initial cells and fill petri dish with food */ +static void create_cells( State * ); +/* do one animation step */ +static void tick( State *st ); +/* draw a single cell */ +static void draw_cell( State *st, int shape ); +/* draw cells nucleus */ +static void draw_nucleus( State *st ); +/* return randum number in the interval min-max */ +static int random_interval( int min, int max ); +/* retunr random number in the interval 0-max */ +static int random_max( int max ); +/* create display list for given disturbance weighting factor */ +static int create_list( State *st, double fac ); +/* return length of vector */ +static double vector_length( Vector * ); +/* normalize vector */ +static void vector_normalize( Vector * ); +/* a += b */ +static void vector_add( Vector *a, Vector *b ); +/* a -= b */ +static void vector_sub( Vector *a, Vector *b ); +/* a *= fac */ +static void vector_mul( Vector *a, double fac ); +/* a.x = a.y = a.z = 0 */ +static void vector_clear( Vector *a ); +/* return crossproduct a*b in out */ +static void vector_crossprod( Vector *a, Vector *b, Vector *out ); +/* return 1 if vectors are equal (epsilon compare) otherwise 0 */ +static int vector_compare( Vector *a, Vector *b ); +/* compute normal vector of given triangle and return in out */ +static void triangle_normal( Vector *a, Vector *b, Vector *c, Vector *out ); +/* take an Object and create an ObjectSmooth out of it */ +static ObjectSmooth *create_ObjectSmooth( Object * ); +/* Subdivide the Object once (assuming it's supposed to be a shpere */ +static Object *subdivide( Object *obj ); +/* free an Object */ +static void free_Object( Object * ); +/* free an ObjectSmooth */ +static void free_ObjectSmooth( ObjectSmooth * ); +/* scale an Object. return pointer to the object */ +/*static Object *scale_Object( Object *obj, double scale );*/ +/* create a perfect sphere refining with divisions */ +static Object *create_sphere( State *st, int divisions ); +/* make a copy of the given Object */ +static Object *clone_Object( Object * ); +/* return 1 if cell is capable to divide */ +static int can_divide( State *st, Cell *cell ); +#ifdef USE_VERTEX_ARRAY +static VertexArray *array_from_ObjectSmooth( ObjectSmooth * ); +#endif +static void create_nucleus_texture( State *st ); + +ENTRYPOINT ModeSpecOpt glcells_opts = { countof(opts), opts, countof(vars), vars, + NULL }; + + +/********************************** + INLINE FUNCTIONS + **********************************/ +/* create random numbers +*/ +static inline int random_interval( int min, int max ) +{ + int n = max - min; + if (n == 0) n = 1; + return min+(random()%n); +} + +static inline int random_max( int max ) +{ + return random()%max; +} + +/* Vector stuff +*/ + +/* a += b */ +static inline void vector_add( Vector *a, Vector *b ) +{ + a->x += b->x; + a->y += b->y; + a->z += b->z; +} + +/* a -= b */ +static inline void vector_sub( Vector *a, Vector *b ) +{ + a->x -= b->x; + a->y -= b->y; + a->z -= b->z; +} + +/* a *= v */ +static inline void vector_mul( Vector *a, double v ) +{ + a->x *= v; + a->y *= v; + a->z *= v; +} + +/* set to 0 */ +static inline void vector_clear( Vector *vec ) +{ + vec->x = vec->y = vec->z = 0; +} + +/* return vector length */ +static inline double vector_length( Vector *vec ) +{ + return sqrt( vec->x*vec->x + vec->y*vec->y + vec->z*vec->z ); +} + +/* normalize vector */ +static inline void vector_normalize( Vector *vec ) +{ + double len = vector_length( vec ); + + if (len != 0.0) { + vector_mul( vec, 1.0 / len ); + } +} + +/* crossproduct */ +static inline void vector_crossprod( Vector *a, Vector *b, Vector *out ) +{ + out->x = a->y*b->z - a->z*b->y; + out->y = a->z*b->x - a->x*b->z; + out->z = a->x*b->y - a->y*b->x; +} + +/* epsilon compare of two vectors */ +static inline int vector_compare( Vector *a, Vector *b ) +{ + const double epsilon = 0.0000001; + Vector delta = *a; + + vector_sub( &delta, b ); + if (fabs(delta.x) < epsilon && + fabs(delta.y) < epsilon && + fabs(delta.z) < epsilon) { + return 1; + } + + return 0; +} + +/* check if given cell is capable of dividing + needs space, must be old enough, grown up and healthy +*/ +static inline int can_divide( State *st, Cell *cell ) +{ + if (cell->min_dist > st->move_dist && + cell->age >= st->divide_age && + cell->radius > 0.99 * st->radius && + cell->energy > 0) { + return 1; + } + + return 0; +} + +/********************************** + FUNCTIONS + **********************************/ + +/* compute normal vector of given + triangle spanned by the points a, b, c +*/ +static void triangle_normal( Vector *a, Vector *b, Vector *c, Vector *out ) +{ + Vector v1 = *a; + Vector v2 = *a; + + vector_sub( &v1, b ); + vector_sub( &v2, c ); + vector_crossprod( &v1, &v2, out ); +} + +/* free */ +static void free_Object( Object *obj ) +{ + free( obj->vertex ); + free( obj->triangle ); + free( obj ); +} + +static void free_ObjectSmooth( ObjectSmooth *obj ) +{ + free( obj->vertex ); + free( obj->triangle ); + free( obj->normal ); + free( obj ); +} + +/* scale the given Object */ +#if 0 +static Object *scale_Object( Object *obj, double scale ) +{ + int v; + + for (v=0; vnum_vertex; ++v) { + vector_mul( &obj->vertex[v], scale ); + } + + return obj; +} +#endif + +/* create a copy of the given Object */ +static Object *clone_Object( Object *obj ) +{ + /* alloc */ + Object *ret = (Object *) malloc( sizeof( Object ) ); + + ret->vertex = + (Vector *) malloc( obj->num_vertex*sizeof(Vector) ); + ret->triangle = + (Triangle *) malloc( obj->num_triangle*sizeof(Triangle) ); + ret->num_vertex = obj->num_vertex; + ret->num_triangle = obj->num_triangle; + /* copy */ + memcpy( ret->vertex, obj->vertex, + obj->num_vertex*sizeof(Vector) ); + memcpy( ret->triangle, obj->triangle, + obj->num_triangle*sizeof(Triangle) ); + + return ret; +} + +#ifdef USE_VERTEX_ARRAY +static VertexArray *array_from_ObjectSmooth( ObjectSmooth *obj ) +{ + int i, j; + VertexArray *array = (VertexArray *) malloc( sizeof( VertexArray ) ); + + array->vertex = (float *) malloc( 3*sizeof(float)*obj->num_vertex ); + array->normal = (float *) malloc( 3*sizeof(float)*obj->num_vertex ); + array->index = (unsigned *) malloc( 3*sizeof(unsigned)*obj->num_triangle ); + array->num_index = obj->num_triangle*3; + + for (i=0, j=0; inum_vertex; ++i) { + array->vertex[j] = obj->vertex[i].x; + array->normal[j++] = obj->normal[i].x; + array->vertex[j] = obj->vertex[i].y; + array->normal[j++] = obj->normal[i].y; + array->vertex[j] = obj->vertex[i].z; + array->normal[j++] = obj->normal[i].z; + } + + for (i=0, j=0; inum_triangle; ++i) { + array->index[j++] = obj->triangle[i].i[0]; + array->index[j++] = obj->triangle[i].i[1]; + array->index[j++] = obj->triangle[i].i[2]; + } + + return array; +} +#endif /* USE_VERTEX_ARRAY */ + + +/* create a smoothed version of the given Object + by computing average normal vectors for the vertexes +*/ +static ObjectSmooth *create_ObjectSmooth( Object *obj ) +{ + int t, v, i; + Vector *t_normal = + (Vector *) malloc( obj->num_triangle*sizeof(Vector) ); + ObjectSmooth *ret = + (ObjectSmooth *) malloc( sizeof( ObjectSmooth ) ); + + /* fill in vertexes and triangles */ + ret->num_vertex = obj->num_vertex; + ret->num_triangle = obj->num_triangle; + ret->vertex = + (Vector *) malloc( obj->num_vertex * sizeof( Vector ) ); + ret->normal = + (Vector *) malloc( obj->num_vertex * sizeof( Vector ) ); + ret->triangle = + (Triangle *) malloc( obj->num_triangle * sizeof( Triangle ) ); + + for (v=0; vnum_vertex; ++v) { + ret->vertex[v] = obj->vertex[v]; + } + + for (t=0; tnum_triangle; ++t) { + ret->triangle[t] = obj->triangle[t]; + } + + /* create normals (triangles) */ + for (t=0; tnum_triangle; ++t) { + triangle_normal( &ret->vertex[ret->triangle[t].i[0]], + &ret->vertex[ret->triangle[t].i[1]], + &ret->vertex[ret->triangle[t].i[2]], + &t_normal[t] ); + } + + /* create normals (vertex) by averaging triangle + normals at vertex + */ + for (v=0; vnum_vertex; ++v) { + vector_clear( &ret->normal[v] ); + for (t=0; tnum_triangle; ++t) { + for (i=0; i<3; ++i) { + if (ret->triangle[t].i[i] == v) { + vector_add( &ret->normal[v], &t_normal[t] ); + } + } + } + /* as we have only a half sphere we force the + normals at the bortder to be perpendicular to z. + the simple algorithm above makes an error here. + */ + if (fabs(ret->vertex[v].z) < 0.0001) { + ret->normal[v].z = 0.0; + } + + vector_normalize( &ret->normal[v] ); + } + + free( t_normal ); + + return ret; +} + +/* subdivide the triangles of the object once + The order of this algorithm is probably something like O(n^42) :) + but I can't think of something smarter at the moment +*/ +static Object *subdivide( Object *obj ) +{ + /* create for worst case (which I dont't know) */ + int start, t, i, v; + int index_list[1000]; + int index_cnt, index_found; + Object *tmp = (Object *)malloc( sizeof(Object) ); + Object *ret = (Object *)malloc( sizeof(Object) ); + Object *c_ret; + + tmp->vertex = + (Vector *)malloc( 100*obj->num_vertex*sizeof( Vector ) ); + tmp->triangle = + (Triangle *)malloc( 4*obj->num_triangle*sizeof( Triangle ) ); + tmp->num_vertex = 0; + tmp->num_triangle = 0; + ret->vertex = + (Vector *)malloc( 100*obj->num_vertex*sizeof( Vector ) ); + ret->triangle = + (Triangle *)malloc( 4*obj->num_triangle*sizeof( Triangle ) ); + ret->num_vertex = 0; + ret->num_triangle = 0; +#ifdef PRINT_STAT + fprintf( stderr, "in v=%d t=%d\n", + obj->num_vertex, obj->num_triangle ); +#endif + /* for each triangle create 3 new vertexes and the 4 + corresponding triangles + */ + for (t=0; tnum_triangle; ++t) { + /* copy the three original vertexes */ + for (i=0; i<3; ++i) { + tmp->vertex[tmp->num_vertex++] = + obj->vertex[obj->triangle[t].i[i]]; + } + + /* create 3 new */ + tmp->vertex[tmp->num_vertex] = + obj->vertex[obj->triangle[t].i[0]]; + vector_add( &tmp->vertex[tmp->num_vertex], + &obj->vertex[obj->triangle[t].i[1]] ); + vector_mul( &tmp->vertex[tmp->num_vertex++], 0.5 ); + + tmp->vertex[tmp->num_vertex] = + obj->vertex[obj->triangle[t].i[1]]; + vector_add( &tmp->vertex[tmp->num_vertex], + &obj->vertex[obj->triangle[t].i[2]] ); + vector_mul( &tmp->vertex[tmp->num_vertex++], 0.5 ); + + tmp->vertex[tmp->num_vertex] = + obj->vertex[obj->triangle[t].i[2]]; + vector_add( &tmp->vertex[tmp->num_vertex], + &obj->vertex[obj->triangle[t].i[0]] ); + vector_mul( &tmp->vertex[tmp->num_vertex++], 0.5 ); + + /* create triangles */ + start = tmp->num_vertex-6; + + tmp->triangle[tmp->num_triangle].i[0] = start; + tmp->triangle[tmp->num_triangle].i[1] = start+3; + tmp->triangle[tmp->num_triangle++].i[2] = start+5; + + tmp->triangle[tmp->num_triangle].i[0] = start+3; + tmp->triangle[tmp->num_triangle].i[1] = start+1; + tmp->triangle[tmp->num_triangle++].i[2] = start+4; + + tmp->triangle[tmp->num_triangle].i[0] = start+5; + tmp->triangle[tmp->num_triangle].i[1] = start+4; + tmp->triangle[tmp->num_triangle++].i[2] = start+2; + + tmp->triangle[tmp->num_triangle].i[0] = start+3; + tmp->triangle[tmp->num_triangle].i[1] = start+4; + tmp->triangle[tmp->num_triangle++].i[2] = start+5; + } + + /* compress object eliminating double vertexes + (welcome to the not so smart section) + */ + /* copy original triangle list */ + for (t=0; tnum_triangle; ++t) { + ret->triangle[t] = tmp->triangle[t]; + } + ret->num_triangle = tmp->num_triangle; + + /* copy unique vertexes and correct triangle list */ + for (v=0; vnum_vertex; ++v) { + /* create list of vertexes that are the same */ + index_cnt = 0; + for (i=0; inum_vertex; ++i) { + /* check if i and v are the same + first in the list is the smallest index + */ + if (vector_compare( &tmp->vertex[v], &tmp->vertex[i] )) { + index_list[index_cnt++] = i; + } + } + + /* check if vertex unknown so far */ + index_found = 0; + for (i=0; inum_vertex; ++i) { + if (vector_compare( &ret->vertex[i], + &tmp->vertex[index_list[0]] )) { + index_found = 1; + break; + } + } + + if (!index_found) { + ret->vertex[ret->num_vertex] = tmp->vertex[index_list[0]]; + + /* correct triangles + (we add an offset to the index, so we can tell them apart) + */ + for (t=0; tnum_triangle; ++t) { + for (i=0; itriangle[t].i[0] == index_list[i]) { + ret->triangle[t].i[0] = ret->num_vertex+INDEX_OFFSET; + } + if (ret->triangle[t].i[1] == index_list[i]) { + ret->triangle[t].i[1] = ret->num_vertex+INDEX_OFFSET; + } + if (ret->triangle[t].i[2] == index_list[i]) { + ret->triangle[t].i[2] = ret->num_vertex+INDEX_OFFSET; + } + } + } + ret->num_vertex++; + } + } + + free_Object( tmp ); + + /* correct index offset */ + for (t=0; tnum_triangle; ++t) { + ret->triangle[t].i[0] -= INDEX_OFFSET; + ret->triangle[t].i[1] -= INDEX_OFFSET; + ret->triangle[t].i[2] -= INDEX_OFFSET; + } + + /* normalize vertexes */ + for (v=0; vnum_vertex; ++v) { + vector_normalize( &ret->vertex[v] ); + } +#ifdef PRINT_STAT + fprintf( stderr, "out v=%d t=%d\n", + ret->num_vertex, ret->num_triangle ); +#endif + /* shrink the arrays by cloning */ + c_ret = clone_Object( ret ); + free_Object( ret ); + + return c_ret; +} + +static int render( State *st ) +{ +#ifdef PRINT_STAT + struct timeval tv1, tv2; + int usec; +#endif + GLfloat LightAmbient[]= { 0.1f, 0.1f, 0.1f, 1.0f }; + GLfloat LightPosition[]= { -20.0f, -10.0f, -100.0f, 0.0f }; + int b; + int num_paint = 0; + + if (0 == st->food) return 0; +#ifdef PRINT_STAT + gettimeofday( &tv1, NULL ); +#endif + /* life goes on... */ + tick( st ); +#ifdef PRINT_STAT + gettimeofday( &tv2, NULL ); + usec = (tv2.tv_sec-tv1.tv_sec)*1000000+(tv2.tv_usec-tv1.tv_usec); + fprintf( stderr, "tick %d\n", usec ); + gettimeofday( &tv1, NULL ); +#endif + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + glDepthFunc(GL_LESS); + glEnable(GL_DEPTH_TEST); + glLightfv( GL_LIGHT0, GL_AMBIENT, LightAmbient ); + glLightfv( GL_LIGHT0, GL_DIFFUSE, st->color ); + glLightfv( GL_LIGHT0, GL_POSITION, LightPosition ); + + /* prepare lighting vs. wireframe */ + if (!st->wire) { + glEnable( GL_LIGHT0 ); + glEnable( GL_LIGHTING ); + glEnable( GL_NORMALIZE ); + glPolygonMode( GL_FRONT, GL_FILL ); + } else { +# ifndef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */ + glPolygonMode( GL_FRONT, GL_LINE ); +# endif + } + +# if 0 + if (st->wire) { + glDisable(GL_DEPTH_TEST); + glColor3f (1, 1, 1); + glBegin(GL_LINE_LOOP); + glVertex3f(0, 0, 0); glVertex3f(st->width, 0, 0); + glVertex3f(st->width, st->height, 0); glVertex3f(0, st->height, 0); + glVertex3f(0, 0, 0); glVertex3f(st->width/4, 0, 0); + glVertex3f(st->width/4, st->height/4, 0); glVertex3f(0, st->height/4, 0); + glEnd(); + } +# endif + + /* draw the dead cells if choosen */ + if (st->keep_old_cells) { + for (b=0; bnum_cells; ++b) { + if (st->cell[b].energy <= 0) { + num_paint++; + glPushMatrix(); + glTranslatef( st->cell[b].x, st->cell[b].y, 0.0 ); + glRotatef( st->cell[b].rotation, 0.0, 0.0, 1.0 ); + glScalef( st->cell[b].radius, st->cell[b].radius, st->cell[b].radius ); + draw_cell( st, 9 ); + glPopMatrix(); + } + } + } + + /* draw the living cells */ + for (b=0; bnum_cells; ++b) { + if (st->cell[b].energy >0) { + double fac = (double)st->cell[b].energy / 50.0; + int shape; + if (fac < 0.0) fac = 0.0; + if (fac > 1.0) fac = 1.0; + + shape = (int)(9.0*fac); + num_paint++; + /*glColor3f( fac, fac, fac );*/ + +# if 0 + if (st->wire) { + glBegin(GL_LINES); + glVertex3f(0, 0, 0); + glVertex3f(st->cell[b].x, st->cell[b].y, 0); + glEnd(); + } +# endif + + glPushMatrix(); + glTranslatef( st->cell[b].x, st->cell[b].y, 0.0 ); + glRotatef( st->cell[b].rotation, 0.0, 0.0, 1.0 ); + glScalef( st->cell[b].radius, st->cell[b].radius, st->cell[b].radius ); + draw_cell( st, 9-shape ); + glPopMatrix(); + } + } + + /* draw cell nuclei */ + if (!st->wire) + { + glDisable( GL_LIGHT0 ); + glDisable( GL_LIGHTING ); + + glEnable( GL_BLEND ); + glDisable( GL_DEPTH_TEST ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_TEXTURE_2D ); + glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + glBindTexture( GL_TEXTURE_2D, st->texture_name ); + + for (b=0; bnum_cells; ++b) { + if (st->cell[b].energy>0 || st->keep_old_cells) { + glPushMatrix(); + glTranslatef( st->cell[b].x, st->cell[b].y, 0.0 ); + glScalef( st->cell[b].radius, st->cell[b].radius, st->cell[b].radius ); + draw_nucleus( st ); + glPopMatrix(); + } + } + + glDisable( GL_TEXTURE_2D ); + glDisable( GL_BLEND ); + } + +#ifdef PRINT_STAT + gettimeofday( &tv2, NULL ); + usec = (tv2.tv_sec-tv1.tv_sec)*1000000+(tv2.tv_usec-tv1.tv_usec); + fprintf( stderr, "OpenGL %d\n", usec ); +#endif + return num_paint * st->cell_polys; +} + +/* this creates the initial subdivided half-dodecaedron */ +static Object *create_sphere( State *st, int divisions ) +{ + int num_vertex = 9; + int num_triangle = 10; + int i, v, t; + double a, aStep = (double)M_PI / 3.0; + double e; + int vi[30] = { 0, 7, 1, 1, 7, 2, 2, 8, 3, 3, 8, 4, 4, 6, 5, + 5, 6, 0, 0, 6, 7, 2, 7, 8, 4, 8, 6, 6, 8, 7 }; + Object *obj = (Object *)malloc( sizeof( Object ) ); + + obj->vertex = (Vector *)malloc( num_vertex*sizeof( Vector ) ); + obj->triangle = + (Triangle *)malloc( num_triangle*sizeof( Triangle ) ); + obj->num_vertex = num_vertex; + obj->num_triangle = num_triangle; + + /* create vertexes for dodecaedron */ + a = 0.0; + for (v=0; v<6; ++v) { + obj->vertex[v].x = sin( a ); + obj->vertex[v].y = -cos( a ); + obj->vertex[v].z = 0.0; + + a += aStep; + } + + a = -60.0/180.0*(double)M_PI; + e = 58.2825/180.0 * (double)M_PI; + for (;v<9; ++v) { + obj->vertex[v].x = sin( a )*cos( e ); + obj->vertex[v].y = -cos( a )*cos( e ); + obj->vertex[v].z = -sin( e ); + + a += 2.0*aStep; + } + + /* create triangles */ + for (t=0; tnum_triangle; ++t) { + obj->triangle[t].i[0] = vi[3*t]; + obj->triangle[t].i[1] = vi[3*t+1]; + obj->triangle[t].i[2] = vi[3*t+2]; + } + + /* subdivide as specified */ + for (i=0; icell_polys = obj->num_triangle; + + return obj; +} + +static int create_list( State *st, double fac ) +{ + int v; + Object *obj = clone_Object( st->sphere ); + ObjectSmooth *smooth; +#ifdef USE_VERTEX_ARRAY + VertexArray *vertex_array; +#else + int t, i; +#endif + int list = glGenLists(1); + + /* apply wrinckle factor */ + for (v=0; vnum_vertex; ++v) { + vector_mul( &obj->vertex[v], 1.0+fac*st->disturbance[v] ); + } + + /* compute normals */ + smooth = create_ObjectSmooth( obj ); + free_Object( obj ); + + /* Create display list */ + glNewList( list, GL_COMPILE ); +#ifdef USE_VERTEX_ARRAY + vertex_array = array_from_ObjectSmooth( smooth ); + glEnableClientState( GL_VERTEX_ARRAY ); + glEnableClientState( GL_NORMAL_ARRAY ); + glVertexPointer( 3, GL_FLOAT, 0, vertex_array->vertex ); + glNormalPointer( GL_FLOAT, 0, vertex_array->normal ); + glDrawElements( GL_TRIANGLES, vertex_array->num_index, + GL_UNSIGNED_INT, vertex_array->index ); + free( vertex_array ); +#else + glBegin( GL_TRIANGLES ); + + for (t=0; tnum_triangle; ++t) { + for (i=0; i<3; ++i) { + glNormal3f( smooth->normal[smooth->triangle[t].i[i]].x, + smooth->normal[smooth->triangle[t].i[i]].y, + smooth->normal[smooth->triangle[t].i[i]].z ); + glVertex3f( smooth->vertex[smooth->triangle[t].i[i]].x, + smooth->vertex[smooth->triangle[t].i[i]].y, + smooth->vertex[smooth->triangle[t].i[i]].z ); + } + } + + glEnd(); +#endif + glEndList(); + + free_ObjectSmooth( smooth ); + + return list; +} + +static void draw_cell( State *st, int shape ) +{ +# ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */ + if (st->wire) { + glDisable(GL_DEPTH_TEST); + glColor3f (1, 1, 1); + glPushMatrix(); + glScalef (0.33, 0.33, 1); + glBegin (GL_LINE_LOOP); + glVertex3f (-1, -1, 0); glVertex3f (-1, 1, 0); + glVertex3f ( 1, 1, 0); glVertex3f ( 1, -1, 0); + glEnd(); + if (shape == 9) { + glBegin (GL_LINES); + glVertex3f (-1, -1, 0); glVertex3f (1, 1, 0); + glVertex3f (-1, 1, 0); glVertex3f (1, -1, 0); + glEnd(); + } + glPopMatrix(); + return; + } +# endif + + if (-1 == st->cell_list[shape]) { + st->cell_list[shape] = create_list( st, (double)shape/10.0 ); + } + + glCallList( st->cell_list[shape] ); +} + +static void create_nucleus_texture( State *st ) +{ + int x, y; + int w2 = TEX_SIZE/2; + float s = w2*w2/4.0; + + st->texture = (GLubyte *) malloc( 4*TEX_SIZE*TEX_SIZE ); + + for (y=0; ytexture[4*(x+y*TEX_SIZE)] = (GLubyte)0; + st->texture[4*(x+y*TEX_SIZE)+1] = (GLubyte)0; + st->texture[4*(x+y*TEX_SIZE)+2] = (GLubyte)0; + st->texture[4*(x+y*TEX_SIZE)+3] = (GLubyte)v; + } + } + + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glGenTextures( 1, &st->texture_name ); + glBindTexture( GL_TEXTURE_2D, st->texture_name ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0, + GL_RGBA, GL_UNSIGNED_BYTE, st->texture ); +} + +static void draw_nucleus( State *st ) +{ + if (-1 == st->nucleus_list) { + float z = -1.2f; + float r=1.0/2.0f; + st->nucleus_list = glGenLists( 1 ); + glNewList( st->nucleus_list, GL_COMPILE ); + glBegin( GL_QUADS ); + glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -r, -r, z ); + glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -r, r, z ); + glTexCoord2f( 1.0f, 1.0f ); glVertex3f( r, r, z ); + glTexCoord2f( 1.0f, 0.0f ); glVertex3f( r, -r, z ); + glEnd(); + glEndList(); + } + + glCallList( st->nucleus_list ); +} + +static void create_cells( State *st ) +{ + int border = (int)(200.0 * st->screen_scale); + int i, foodcnt; + int w = st->width-2*border; + int h = st->height-2*border; + + st->color[0] = 0.5 + random_max( 1000 ) * 0.0005; + st->color[1] = 0.5 + random_max( 1000 ) * 0.0005; + st->color[2] = 0.5 + random_max( 1000 ) * 0.0005; + st->color[3] = 1.0f; + + /* allocate if startup */ + if (!st->cell) { + st->cell = (Cell *) malloc( st->max_cells * sizeof(Cell)); + } + + /* fill the screen with random food for our little critters */ + foodcnt = (st->width*st->height)/16; + for (i=0; ifood[i] = random_interval( st->minfood, st->maxfood ); + } + + /* create the requested seed-cells */ + st->num_cells = st->num_seeds; + + for (i=0; inum_cells; ++i) { + st->cell[i].x = border + random_max( w ); + st->cell[i].y = border + random_max( h ); + st->cell[i].vx = 0.0; + st->cell[i].vy = 0.0; + st->cell[i].age = random_max( 0x0f ); + st->cell[i].min_dist = 500.0; + st->cell[i].energy = random_interval( 5, 5+0x3f ); + st->cell[i].rotation = ((double)random()/(double)RAND_MAX)*360.0; + st->cell[i].radius = st->radius; + st->cell[i].growth = 1.0; + } +} + +/* all this is rather expensive :( */ +static void tick( State *st ) +{ + int new_num_cells, num_cells=0; + int b, j; + int x, y, w4=st->width/4, h4=st->height/4, offset; + double min_dist; + int min_index; + int num_living = 0; + const double check_dist = 0.75*st->move_dist; + const double grow_dist = 0.75*st->radius; + const double adult_radius = st->radius; + + /* find number of cells capable of division + and count living cells + */ + for (b=0; bnum_cells; ++b) { + if (st->cell[b].energy > 0) num_living++; + if (can_divide( st, &st->cell[b] )) num_cells++; + } + new_num_cells = st->num_cells + num_cells; + + /* end of simulation ? */ + if (0 == num_living || new_num_cells >= st->max_cells) { + if (st->pause_counter > 0) st->pause_counter--; + if (st->pause_counter > 0) return; + create_cells( st ); + st->pause_counter = st->pause; + } else if (num_cells) { /* any fertile candidates ? */ + for (b=0, j=st->num_cells; bnum_cells; ++b) { + if (can_divide( st, &st->cell[b] )) { + st->cell[b].vx = random_interval( -50, 50 ) * 0.01; + st->cell[b].vy = random_interval( -50, 50 ) * 0.01; + st->cell[b].age = random_max( 0x0f ); + /* half energy for both plus some bonus for forking */ + st->cell[b].energy = + st->cell[b].energy/2 + random_max( 0x0f ); + /* forking makes me shrink */ + st->cell[b].growth = 0.995; + + /* this one initially goes into the oposite direction */ + st->cell[j].vx = -st->cell[b].vx; + st->cell[j].vy = -st->cell[b].vy; + /* same center */ + st->cell[j].x = st->cell[b].x; + st->cell[j].y = st->cell[b].y; + st->cell[j].age = random_max( 0x0f ); + st->cell[j].energy = (st->cell[b].energy); + st->cell[j].rotation = + ((double)random()/(double)RAND_MAX)*360.0; + st->cell[j].growth = st->cell[b].growth; + st->cell[j].radius = st->cell[b].radius; + ++j; + } else { + st->cell[b].vx = 0.0; + st->cell[b].vy = 0.0; + } + } + + st->num_cells = new_num_cells; + } + + /* for each find a direction to escape */ + if (st->num_cells > 1) { + for (b=0; bnum_cells; ++b) { + if (st->cell[b].energy > 0) { + double vx; + double vy; + double len; + + /* grow or shrink */ + st->cell[b].radius *= st->cell[b].growth; + /* find closest neighbour */ + min_dist = 100000.0; + min_index = 0; + for (j=0; jnum_cells; ++j) { + if (j!=b) { + const double dx = st->cell[b].x - st->cell[j].x; + const double dy = st->cell[b].y - st->cell[j].y; + + if (fabs(dx) < check_dist || fabs(dy) < check_dist) { + const double dist = dx*dx+dy*dy; + /*const double dist = sqrt( dx*dx+dy*dy );*/ + if (distcell[b].x - st->cell[min_index].x; + vy = st->cell[b].y - st->cell[min_index].y; + len = sqrt( vx*vx + vy*vy ); + if (len > 0.0001) { + st->cell[b].vx = vx/len; + st->cell[b].vy = vy/len; + } + st->cell[b].min_dist = len; + /* if not adult (radius too small) */ + if (st->cell[b].radius < adult_radius) { + /* if too small 60% stop shrinking */ + if (st->cell[b].radius < adult_radius * 0.6) { + st->cell[b].growth = 1.0; + } + /* at safe distance we start growing again */ + if (len > grow_dist) { + if (st->cell[b].energy > 30) { + st->cell[b].growth = 1.005; + } + } + } else { /* else keep size */ + st->cell[b].growth = 1.0; + } + } + } + } else { + st->cell[0].min_dist = 2*st->move_dist; + } + + /* now move em, snack and burn energy */ + for (b=0; bnum_cells; ++b) { + /* if still alive */ + if (st->cell[b].energy > 0) { + /* agility depends on amount of energy */ + double fac = (double)st->cell[b].energy / 50.0; + if (fac < 0.0) fac = 0.0; + if (fac > 1.0) fac = 1.0; + + st->cell[b].x += fac*(2.0 - + (4.0*(double)random() / (double)RAND_MAX) + + st->cell[b].vx); + st->cell[b].y += fac*(2.0 - + (4.0*(double)random() / (double)RAND_MAX) + + st->cell[b].vy); + + /* get older and burn energy */ + if (st->cell[b].energy > 0) { + st->cell[b].age++; + st->cell[b].energy--; + } + + /* have a snack */ + x = ((int)st->cell[b].x)/4; + if (x<0) x=0; + if (x>=w4) x = w4-1; + y = ((int)st->cell[b].y)/4; + if (y<0) y=0; + if (y>=h4) y = h4-1; + + offset = x+y*w4; + + /* don't eat if already satisfied */ + if (st->cell[b].energy < 100 && + st->food[offset] > 0) { + st->food[offset]--; + st->cell[b].energy++; + /* if you are hungry, eat more */ + if (st->cell[b].energy < 50 && + st->food[offset] > 0) { + st->food[offset]--; + st->cell[b].energy++; + } + } + } + } +} + +ENTRYPOINT void +reshape_glcells( ModeInfo *mi, int width, int height ) +{ + State *st = &sstate[MI_SCREEN(mi)]; +# ifdef HAVE_MOBILE + int rot = current_device_rotation(); +# endif + st->height = height; + st->width = width; +# ifdef HAVE_MOBILE + st->screen_scale = (double)(width < height ? width : height) / 1600.0; +# else + st->screen_scale = (double)width / 1600.0; +# endif + + st->radius = s_radius; + if (st->radius < 5) st->radius = 5; + if (st->radius > 200) st->radius = 200; + st->radius *= st->screen_scale; + + st->move_dist = s_min_dist; + if (st->move_dist < 1.0) st->move_dist = 1.0; + if (st->move_dist > 3.0) st->move_dist = 3.0; + st->move_dist *= st->radius; + + glViewport (0, 0, (GLint) width, (GLint) height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho( 0, width, height, 0, 200, 0 ); +# ifdef HAVE_MOBILE + glRotatef (rot, 0, 0, 1); +# endif + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + if (st->food) free( st->food ); + st->food = (int *)malloc( ((width*height)/16)*sizeof(int) ); + /* create_cells( st );*/ + +# ifdef HAVE_MOBILE + glTranslatef (st->width/2, st->height/2, 0); + if (rot == 90 || rot == -90 || rot == 270 || rot == -270) + st->width = height, st->height = width; + glRotatef (rot, 0, 0, 1); + if (st->wire) glScalef(0.8, 0.8, 1); + glTranslatef (-st->width/2, -st->height/2, 0); +# endif +} + +ENTRYPOINT void +init_glcells( ModeInfo *mi ) +{ + int i, divisions; + State *st=0; + + MI_INIT(mi, sstate); + st = &sstate[MI_SCREEN(mi)]; + + st->glx_context = init_GL(mi); + st->cell = 0; + st->num_cells = 0; + st->wire = MI_IS_WIREFRAME(mi); + + /* get settings */ + st->max_cells = s_maxcells;; + if (st->max_cells < 50) st->max_cells = 50; + if (st->max_cells > 10000) st->max_cells = 10000; + + st->pause = s_pause; + if (st->pause < 0) st->pause = 0; + if (st->pause > 400) st->pause = 400; + st->pause_counter = st->pause; + + st->radius = s_radius; + if (st->radius < 5) st->radius = 5; + if (st->radius > 200) st->radius = 200; + + divisions = s_quality; + if (divisions < 0) divisions = 0; + if (divisions > 5) divisions = 5; + + st->num_seeds = s_seeds; + if (st->num_seeds < 1) st->num_seeds = 1; + if (st->num_seeds > 16) st->num_seeds = 16; + + st->minfood = s_minfood; + if (st->minfood < 0) st->minfood = 0; + if (st->minfood > 1000) st->minfood = 1000; + + st->maxfood = s_maxfood; + if (st->maxfood < 0) st->maxfood = 0; + if (st->maxfood > 1000) st->maxfood = 1000; + + if (st->maxfood < st->minfood) st->maxfood = st->minfood+1; + + st->keep_old_cells = s_keepold; + + st->divide_age = s_divideage; + if (st->divide_age < 1) st->divide_age = 1; + if (st->divide_age > 1000) st->divide_age = 1000; + + st->move_dist = s_min_dist; + if (st->move_dist < 1.0) st->move_dist = 1.0; + if (st->move_dist > 3.0) st->move_dist = 3.0; + st->move_dist *= st->radius; + + for (i=0; icell_list[i] = -1; + st->nucleus_list = -1; + st->food = 0; + + st->sphere = create_sphere( st, divisions ); + st->disturbance = + (double *) malloc( st->sphere->num_vertex*sizeof(double) ); + for (i=0; isphere->num_vertex; ++i) { + st->disturbance[i] = + 0.05-((double)random()/(double)RAND_MAX*0.1); + } + + create_nucleus_texture( st ); + + reshape_glcells (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); +} + +ENTRYPOINT void +draw_glcells( ModeInfo *mi ) +{ + State *st = &sstate[MI_SCREEN(mi)]; + Display *dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + + if (!st->glx_context) return; + + glXMakeCurrent( MI_DISPLAY(mi), MI_WINDOW(mi), + *(st->glx_context) ); + + mi->polygon_count = render( st ); + + if (mi->fps_p) do_fps (mi); + + glFinish(); + glXSwapBuffers( dpy, window ); +} + +ENTRYPOINT void +free_glcells( ModeInfo *mi ) +{ + int i; + State *st = &sstate[MI_SCREEN(mi)]; + + if (st->glx_context) { + glXMakeCurrent( MI_DISPLAY(mi), MI_WINDOW(mi), + *(st->glx_context) ); + + /* nuke everything before exit */ + if (st->sphere) free_Object( st->sphere ); + if (st->food) free( st->food ); + for (i=0; icell_list[i] != -1) { + glDeleteLists( st->cell_list[i], 1 ); + } + } + if (st->cell) free( st->cell ); + free( st->disturbance ); + glDeleteTextures( 1, &st->texture_name ); + free( st->texture ); + } +} + +XSCREENSAVER_MODULE( "GLCells", glcells ) -- cgit v1.2.3-55-g7522