From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / glcells.c
index 86355f10dee35b7e2f13875163f6df2997a50c8d..c0cbbbf077e983da3a2aa2baf17bb68914dab880 100644 (file)
@@ -4,7 +4,7 @@
 /*-
  * Cells growing on your screen
  *
- * Copyright (c) 2006 by Matthias Toussaint
+ * 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,
  * event will the author be liable for any lost revenue or profits or
  * other special, indirect and consequential damages.
  *
- * 2006: Written by Matthias Toussaint
+ * 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 <sys/time.h> /* gettimeofday */
@@ -34,6 +37,7 @@
 #define NUM_CELL_SHAPES 10
 
 #define refresh_glcells 0
+#define release_glcells 0
 #define glcells_handle_event 0
 
 #define DEF_DELAY     "20000"
 #define DEF_MINFOOD   "5"
 #define DEF_MAXFOOD   "20"
 #define DEF_DIVIDEAGE "20"
-#define DEF_MINDIST   "1.40"
+#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)))
 
-#define USE_VERTEX_ARRAY
+#ifndef HAVE_JWZGLES /* glDrawElements unimplemented... */
+# define USE_VERTEX_ARRAY
+#endif
 
 #define TEX_SIZE 64
 
@@ -116,11 +123,12 @@ 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 */
-  int radius;           /* cell radius */
+  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 */
@@ -180,7 +188,7 @@ static argtype vars[] = {
   {&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 prefered distance to other cells",       DEF_MINDIST,  t_Float},
+  {&s_min_dist,  "mindist",   "Minimum preferred distance to other cells",       DEF_MINDIST,  t_Float},
   {&s_keepold,   "keepold",   "Keep old cells", DEF_KEEPOLD,  t_Bool}
 };
 
@@ -238,7 +246,9 @@ static Object *create_sphere( State *st, int divisions );
 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, 
@@ -252,7 +262,9 @@ ENTRYPOINT ModeSpecOpt glcells_opts = { countof(opts), opts,
 */
 static inline int random_interval( int min, int max )
 {
-  return min+(random()%(max-min));
+  int n = max - min;
+  if (n == 0) n = 1;
+  return min+(random()%n);
 }
 
 static inline int random_max( int max )
@@ -416,6 +428,7 @@ static Object *clone_Object( Object *obj )
   return ret;
 }
 
+#ifdef USE_VERTEX_ARRAY
 static VertexArray *array_from_ObjectSmooth( ObjectSmooth *obj )
 {
   int i, j;
@@ -443,6 +456,8 @@ static VertexArray *array_from_ObjectSmooth( ObjectSmooth *obj )
   
   return array;
 }
+#endif /* USE_VERTEX_ARRAY */
+
 
 /* create a smoothed version of the given Object
    by computing average normal vectors for the vertexes 
@@ -690,8 +705,7 @@ static int render( State *st )
   fprintf( stderr, "tick %d\n", usec );
   gettimeofday( &tv1, NULL );
 #endif
-  glClearColor( 0, 0, 0, 0 );
-  
+
   glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
   glDepthFunc(GL_LESS);
   glEnable(GL_DEPTH_TEST);
@@ -706,9 +720,24 @@ static int render( State *st )
     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; b<st->num_cells; ++b) {
@@ -736,6 +765,15 @@ static int render( State *st )
       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 );
@@ -844,6 +882,8 @@ static int create_list( State *st, double fac )
   ObjectSmooth *smooth;
 #ifdef USE_VERTEX_ARRAY
   VertexArray *vertex_array;
+#else
+  int t, i;
 #endif
   int list = glGenLists(1);  
   
@@ -892,6 +932,27 @@ static int create_list( State *st, double fac )
 
 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 );
   }
@@ -951,9 +1012,10 @@ static void draw_nucleus( State *st )
 
 static void create_cells( State *st )
 {
+  int border = (int)(200.0 * st->screen_scale);
   int i, foodcnt;
-  int w = st->width-400;
-  int h = st->height-400;
+  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;
@@ -975,8 +1037,8 @@ static void create_cells( State *st )
   st->num_cells = st->num_seeds;
 
   for (i=0; i<st->num_cells; ++i) {
-    st->cell[i].x        = 200 + random_max( w );
-    st->cell[i].y        = 200 + random_max( h );
+    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 );
@@ -999,6 +1061,7 @@ static void tick( State *st )
   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
@@ -1068,7 +1131,8 @@ static void tick( State *st )
             const double dy = st->cell[b].y - st->cell[j].y;
             
             if (fabs(dx) < check_dist || fabs(dy) < check_dist) {
-              const double dist = sqrt( dx*dx+dy*dy );
+              const double dist = dx*dx+dy*dy;
+              /*const double dist = sqrt( dx*dx+dy*dy );*/
               if (dist<min_dist) {
                 min_dist = dist;
                 min_index = j;
@@ -1086,9 +1150,9 @@ static void tick( State *st )
         }
         st->cell[b].min_dist = len;
         /* if not adult (radius too small) */
-        if (st->cell[b].radius < st->radius) {
+        if (st->cell[b].radius < adult_radius) {
           /* if too small 60% stop shrinking */
-          if (st->cell[b].radius < st->radius * 0.6) {
+          if (st->cell[b].radius < adult_radius * 0.6) {
             st->cell[b].growth = 1.0;
           }
           /* at safe distance we start growing again */
@@ -1130,9 +1194,11 @@ static void tick( State *st )
       
       /* have a snack */
       x = ((int)st->cell[b].x)/4;
-      if (x<0) x=0; if (x>=w4) x = w4-1;
+      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;
+      if (y<0) y=0;
+      if (y>=h4) y = h4-1;
     
       offset = x+y*w4;
     
@@ -1156,37 +1222,60 @@ 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 );
+  /* 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
 }
 
+static void free_glcells( ModeInfo *mi );
+
 ENTRYPOINT void 
 init_glcells( ModeInfo *mi )
 {
   int i, divisions;
   State *st=0;
   
-  if (!sstate) {
-    sstate = (State *)
-        calloc( MI_NUM_SCREENS(mi), sizeof(State) );
-    if (!sstate) {
-      fprintf( stderr, "%s: out of memory\n", progname );
-      exit(1);
-    }
-  }
+  MI_INIT(mi, sstate, free_glcells);
   st = &sstate[MI_SCREEN(mi)];
   
   st->glx_context = init_GL(mi);
@@ -1267,31 +1356,36 @@ draw_glcells( ModeInfo *mi )
                   *(st->glx_context) );
   
   mi->polygon_count = render( st );
-  
+
   if (mi->fps_p) do_fps (mi);
   
   glFinish();
   glXSwapBuffers( dpy, window );
 }
 
-ENTRYPOINT void 
-release_glcells( ModeInfo *mi )
+static 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; i<NUM_CELL_SHAPES; ++i) {
-    if (st->cell_list[i] != -1) {
-      glDeleteLists( st->cell_list[i], 1 );
+    /* nuke everything before exit */
+    if (st->sphere) free_Object( st->sphere );
+    if (st->food)   free( st->food );
+    for (i=0; i<NUM_CELL_SHAPES; ++i) {
+      if (st->cell_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 );
   }
-  if (st->cell) free( st->cell );
-  free( st->disturbance );
-  glDeleteTextures( 1, &st->texture_name );
-  free( st->texture );
 }
 
 XSCREENSAVER_MODULE( "GLCells", glcells )